之前写了一篇memcached注解缓存,最近公司使用的是redis也用到了缓存,但是redis与mem稍有区别,特意写下来,避免一些坑!
首先改写了@Cached注解:
import com.common.redis.RedisDBEnum;
import org.springframework.core.annotation.AliasFor;
import java.lang.annotation.*;
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Cached {
int expire() default 60;//second
@AliasFor("key")
String value() default "";
@AliasFor("value")
String key() default "";
RedisDBEnum database() default RedisDBEnum.SYS;
}
切面的修改:(坑就在这里,因为我之前Json转换接触的不多,而公司使用的redis存储的主类型为string类型,所以会有Json与Java对象之间的转换问题,这也是与上次memcached缓存的区别之处)
import com.alibaba.fastjson.JSON;
import com.common.redis.RedisDBEnum;
import com.environment.ds.annotation.Cached;
import com.environment.redis.impl.RedisRepositoryImpl;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
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.core.LocalVariableTableParameterNameDiscoverer;
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 java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Aspect
@Component
public class CacheAop {
@Autowired
private RedisRepositoryImpl redis;
private ExpressionParser parser = new SpelExpressionParser();
private LocalVariableTableParameterNameDiscoverer discoverer =
new LocalVariableTableParameterNameDiscoverer();
@Pointcut("@annotation(com.ymt.crmservice.environment.ds.annotation.Cached)")
public void cachedAspect() {
}
@Around(value = "cachedAspect()")
public Object log(ProceedingJoinPoint pjp) throws Throwable {
// 获取方法名
Signature signature = pjp.getSignature();
String methodName = signature.getName();
Object[] args = pjp.getArgs();
//获取方法的注解
Method targetMethod = ((MethodSignature) signature).getMethod();
Method realMethod = pjp.getTarget().getClass().getDeclaredMethod(methodName,
targetMethod.getParameterTypes());
Map<String, Object> params = new HashMap<>();
params.put("methodName", methodName);
params.put("fullName", targetMethod.getDeclaringClass().getName());
params.put("simpleName", targetMethod.getDeclaringClass().getSimpleName());
Cached cached = realMethod.getAnnotation(Cached.class);
String[] paramList = discoverer.getParameterNames(targetMethod);
StandardEvaluationContext context = new StandardEvaluationContext();
context.setVariables(params);
for (int len = 0; len < paramList.length; len++) {
context.setVariable(paramList[len], args[len]);
}
Expression expression = parser.parseExpression(cached.key());
String key = expression.getValue(context, String.class);
int saveTime = cached.expire();
RedisDBEnum database = cached.database();
Type type = realMethod.getGenericReturnType();
Object proceed;
String str = redis.get(RedisDBEnum.SYS, key);
//命中缓存
if (StringUtils.isNotBlank(str)) {
Class returnClass = realMethod.getReturnType();
// 返回值数据类型是否为List
if (realMethod.getReturnType() == List.class) {//List
//为List则获取泛型的实际类型
if (type instanceof ParameterizedType) {
Type[] actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
returnClass = Class.forName(actualTypeArguments[0].getTypeName());
}
proceed = JSON.parseArray(str, returnClass);
} else {//其他数据类型
proceed = JSON.parseObject(str, returnClass);
}
return proceed;
}
//未命中缓存
proceed = pjp.proceed(args);
if (proceed != null) {
redis.set(database, key, JSON.toJSONString(proceed), saveTime);
}
return proceed;
}
}
改写后支持选择redis库、设置存储时间,有一些多余代码是为了支持spel表达式,使用方法如下:
@Cached(database = RedisDBEnum.SYS, expire = 100,
key = "#fullName + ':' + #methodName + ':' + #dictTypeCode + ':' + #parentCode")
@Override
public List<Data> list(String dictTypeCode, String parentCode) {
// TODO 具体实现
return null;
}