简介和依赖
这里使用的是Redis缓存,所以这里假设你已经安装了Redis服务器并且没有修改默认端口,如果修改了请修改测试的代码。
这里利用AOP实现动态缓存其实和第四篇差不多,所以下面主要介绍一下缓存的切面类。
缓存切面
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
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.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSON;
import redis.clients.jedis.Jedis;
@Component
@Aspect
public class RedisCacheAspect {
private Jedis jedis = new Jedis("127.0.0.1");
private static final ExpressionParser parser=new SpelExpressionParser();
private static final Map<String, Expression> expCache=new ConcurrentHashMap<String, Expression>();
@Pointcut("@annotation(cn.freemethod.cache.RedisCache)")
// @Pointcut("this(cn.freemethod.service.UserService)")
public void redisCachePointcut() {
}
@Around("redisCachePointcut() && @annotation(redisCache)")
public Object dealRedisCache(ProceedingJoinPoint pjp,RedisCache redisCache) throws Throwable {
// MethodSignature signature = (MethodSignature) pjp.getSignature();
// Method method = signature.getMethod();
// RedisCache annotation = method.getAnnotation(RedisCache.class);
if(redisCache != null){
String elKey = redisCache.key();
String key = getElValue(elKey,pjp.getArgs());
System.out.println(key);
Class<?> clazz = redisCache.clazz();
String result = jedis.get(key);
if(result != null)
return JSON.parseObject(result, clazz);
else{
Object object = pjp.proceed();
result = JSON.toJSONString(object);
long timeout = redisCache.timeout();
jedis.set(key, result, "NX", "EX", timeout);
return object;
}
}
return pjp.proceed();
}
public static String getElValue(String keySpEL, Object[] arguments) {
StandardEvaluationContext context=new StandardEvaluationContext();
context.setVariable("args", arguments);
Expression expression=expCache.get(keySpEL);
if(null == expression) {
expression=parser.parseExpression(keySpEL);
expCache.put(keySpEL, expression);
}
return (String) expression.getValue(context);
}
}
上面的:
@Pointcut("@annotation(cn.freemethod.cache.RedisCache)")
就是匹配所有有RedisCache注解的方法。下面是RedisCache注解:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RedisCache {
String key();
Class<?> clazz();
long timeout() default 60;
}
其实缓存切面逻辑是非常简单的: 首先代理的是注解上有RedisCache的方法,取到RedisCache的key,key是一个Spring EL表达式,这个表示和方法的参数有关。通过这个EL表达式解析得到一个key,然后去redis数据库中取数据,如果没有获取到就执行方法获取数据,并且把获取到的结果通过fastjson系列化写到redis数据库中,设置超时时间为RedisCache设置的timeout。如果取到了就使用fastjson反系列化为RedisCache配置的clazz类型。
获取EL key的getElValue方法也非常简化只是绑定了方法参数,下面看一下调用的实例:
@Test
public void testGetUser(){
UserBean user = userServiceImpl.getUser(2);
System.out.println(user);
}
完整的代码请下载后面参考中的完整工程代码。