如何能更简洁的利用aop实现redis缓存,话不多说,上demo
需求:
数据查询时每次都需要从数据库查询数据,数据库压力很大,查询速度慢,
因此设置缓存层,查询数据时先从redis中查询,如果查询不到,则到数据库中查询
然后将数据库中查询的数据放到redis中一份,下次查询时就能直接从redis中查到,不需要查询数据库了
实现过程:
先搭建ssm的架子,引入redis,编写redis 缓存方法 RedisCache.java以及序列化用到的工具类
自定义注解 getCache 目的:
被这个注解标记的方法实现aop
防止redis key重复
编写切面
@Aspect
@Pointcut("@annotation(com.spring_redis.cache.GetCache)")
切入点为自定义注解 即每个被该注解标记的方法实现通知
@Around("getCache()")
利用环绕通知
过程: 查询时,先查询redis 如果存在key-value,则返回不查询
如果不存在,则查询数据库,之后将查询到的数据存入到redis缓存中
redis key格式:为了防止key冲突,创建的key格式为:
包名.类名.方法名.参数类型.参数值",类似 "your.package.SomeService.getById(integer).123"
目录结构:
maven依赖:
UTF-8
4.0.6.RELEASE
3.2.7
org.springframework
spring-core
${spring.version}
org.springframework
spring-web
${spring.version}
org.springframework
spring-oxm
${spring.version}
org.springframework
spring-tx
${spring.version}
org.springframework
spring-aop
${spring.version}
org.springframework
spring-jdbc
${spring.version}
org.springframework
spring-webmvc
${spring.version}
org.springframework
spring-context-support
4.0.6.RELEASE
org.springframework
spring-context
commons-logging
commons-logging
${spring.version}
org.springframework
spring-test
${spring.version}
org.aspectj
aspectjrt
1.6.12
org.aspectj
aspectjweaver
1.6.12
cglib
cglib
2.2
mysql
mysql-connector-java
5.1.31
org.apache.commons
commons-dbcp2
2.0.1
org.codehaus.jackson
jackson-mapper-asl
1.9.13
org.mybatis
mybatis
${mybatis.version}
org.mybatis
mybatis-spring
1.2.2
org.springframework.data
spring-data-redis
1.6.1.RELEASE
redis.clients
jedis
2.7.3
javax.servlet
javax.servlet-api
3.0.1
provided
javax.servlet.jsp
jsp-api
2.2
provided
log4j
log4j
1.2.17
这里只给出redis 的相关配置
在applicationContext-dao.xml 里添加
那重点开始了
创建自定义注解
/**
* 自定义注解,对于查询使用缓存的方法加入该注解
* @author Chenth
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface GetCache {
String name() default "";
String value() default "";
}
被这个自定义注解所标记的方法将实现下面的切面
配置切面
package com.spring_redis.cache;
import java.io.Serializable;
import java.lang.reflect.Method;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import com.spring_redis.util.RedisCache;
@Component
@Aspect
public class GetCacheAOP {
@Autowired
private RedisTemplate redisTemplate;
private RedisCache redisCache = new RedisCache();
@Pointcut("@annotation(com.spring_redis.cache.GetCache)")
public void getCache(){
System.out.println("我是一个切入点");
}
/**
* 在所有标注@getCache的地方切入
* @param joinPoint
*/
@Around("getCache()")
public Object beforeExec(ProceedingJoinPoint joinPoint){
//前置:到redis中查询缓存
System.out.println("调用从redis中查询的方法...");
//redis中key格式: id
String redisKey = getCacheKey(joinPoint);
//获取从redis中查询到的对象
Object objectFromRedis = redisCache.getDataFromRedis(redisKey);
//如果查询到了
if(null != objectFromRedis){
System.out.println("从redis中查询到了数据...不需要查询数据库");
return objectFromRedis;
}
System.out.println("没有从redis中查到数据...");
//没有查到,那么查询数据库
Object object = null;
try {
object = joinPoint.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("从数据库中查询的数据...");
//后置:将数据库中查询的数据放到redis中
System.out.println("调用把数据库查询的数据存储到redis中的方法...");
redisCache.setDataToRedis(redisKey, object);
System.out.println("redis中的数据..."+object.toString());
//将查询到的数据返回
return object;
}
/**
* 根据类名、方法名和参数值获取唯一的缓存键
* @return 格式为 "包名.类名.方法名.参数类型.参数值",类似 "your.package.SomeService.getById(int).123"
*/
@SuppressWarnings("unused")
private String getCacheKey(ProceedingJoinPoint joinPoint) {
MethodSignature ms=(MethodSignature) joinPoint.getSignature();
Method method=ms.getMethod();
String ActionName = method.getAnnotation(GetCache.class).name();
String fieldList = method.getAnnotation(GetCache.class).value();
//System.out.println("签名是"+ms.toString());
for (String field:fieldList.split(","))
ActionName +="."+field;
//先获取目标方法参数
String id = null;
Object[] args = joinPoint.getArgs();
if (args != null && args.length > 0) {
id = String.valueOf(args[0]);
}
ActionName += "="+id;
String redisKey = ms+"."+ActionName;
return redisKey;
}
public void setRedisTemplate(
RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
}
@Pointcut("@annotation(com.spring_redis.cache.GetCache)") 这个切入点的作用是
在所有标注@getCache的地方切入
@Around("getCache()")这里用的是后置通知,即查询之前先查询redis,如果有数据就返回数据,没有就穿透的数据库查询数据,之后再缓存到redis中
这里并没有太多的讲解配置ssm框架,可能后续会写关于spring+springmvc+mybatis的框架整合
编写mapper层,service层,controller层
mapper
/**
*
* @author cmy
* @date 2016-10-22
* @description 持久化
*/
public interface RoomMapper {
@Insert("insert into room(roomName,address) values(#{roomName},#{addRess})")
int insert(Room room);
@Select("select * from room where id=#{id}")
public Room selectByPrimaryKey(@Param("id")Integer id);
}
service
/**
*
* @author cmy
* @date 2016-10-22
* @description test
*/
public interface RoomService {
int insert(Room room)throws Exception;
Room selectByPrimaryKey(Integer id)throws Exception;
}
// 实现
/**
* @author cmy
* @date 2016-10-22
* @description test 实现
*/
public class RoomServiceImpl implements RoomService{
@Autowired
private RoomMapper mapper;
@Override
public int insert(Room room) throws Exception {
return mapper.insert(room);
}
@Override
public Room selectByPrimaryKey(Integer id) throws Exception {
return mapper.selectByPrimaryKey(id);
}
}
/**
*
* @author cmy
* @date 2016-10-22
* @description test controller
*/
@Controller
@RequestMapping("room")
public class RoomController {
@Autowired
private RoomService roomService;
@GetCache(name="room",value="id")
@RequestMapping("selectByPrimaryKey")
public @ResponseBody Object roomList(Integer id) throws Exception{
System.out.println("已查询到数据,准备缓存到redis... "+roomService.selectByPrimaryKey(id).getRoomName());
return roomService.selectByPrimaryKey(id);
}
}
public class RedisCache {
@Autowired
private JedisPool jedisPool = new JedisPool();
//从redis缓存中查询,反序列化
public Object getDataFromRedis(String redisKey){
//查询
Jedis jedis = jedisPool.getResource();
byte[] result = jedis.get(redisKey.getBytes());
//如果查询没有为空
if(null == result){
return null;
}
//查询到了,反序列化
return SerializeUtil.unSerialize(result);
}
//将数据库中查询到的数据放入redis
public void setDataToRedis(String redisKey, Object obj){
//序列化
byte[] bytes = SerializeUtil.serialize(obj);
//存入redis
Jedis jedis = jedisPool.getResource();
String success = jedis.set(redisKey.getBytes(), bytes);
if("OK".equals(success)){
System.out.println("数据成功保存到redis...");
}
}
}
/**
*
* @Description: 序列化反序列化工具
*/
public class SerializeUtil {
/**
*
* 序列化
*/
public static byte[] serialize(Object obj){
ObjectOutputStream oos = null;
ByteArrayOutputStream baos = null;
try {
//序列化
baos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
byte[] byteArray = baos.toByteArray();
return byteArray;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
*
* 反序列化
* @param bytes
* @return
*/
public static Object unSerialize(byte[] bytes){
ByteArrayInputStream bais = null;
try {
//反序列化为对象
bais = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bais);
return ois.readObject();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
以上就是利用aop+自定义注解实现 redis缓存的过程了
有不对之处,还望指出 欢迎留言
有参考到的文章:http://www.cnblogs.com/mrlinfeng/p/5857775.html
http://blog.csdn.net/chentian610/article/details/51012789