业务场景是这样:想要做一个自定义注解@MethodCache,注解方法,功能是通过Aspect切片来自动缓存方法结果
@Target({ METHOD })
@Retention(RUNTIME)
public @interface MethodCache {
/**
* 过期时间 单位秒 默认60秒
*/
int expire() default 60;
}
Aspect类
@Aspect
@Configuration
public class MethodCacheAnnotationAspect {
// 日志
private Logger logger = LoggerFactory.getLogger(MethodCacheAnnotationAspect.class);
//redis
@Autowired
private RedisTemplate bbscache;
// around 建言 切入点为匹配注解了@MethodCache
@Around("@annotation(com.ewt360.bbsapi.service.component.MethodCache)")
public Object cacheProcess(ProceedingJoinPoint jp) throws Throwable {
Class<?> targetClz = jp.getTarget().getClass();
String methodName = jp.getSignature().getName();
if(!(jp.getSignature() instanceof MethodSignature)){
logger.warn("该方法接口无法启用缓存功能: {}", jp.getSignature().toLongString());
return jp.proceed();
}
MethodSignature methodSign = (MethodSignature)jp.getSignature();
MethodCache sc = methodSign.getMethod().getAnnotation(MethodCache.class);
if (sc == null)
return jp.proceed();
int expire = sc.expire() > 0 ? sc.expire() : 200;
// 组装缓存key
String cacheKey = buildCacheKey(targetClz, methodName, jp.getArgs());
logger.info("cacheInvoke =>{}",cacheKey);
Object rval = cacheInvoke(sc, methodSign, jp, cacheKey, expire);
return rval;
}
private String buildCacheKey(Class targetClz, String methodName, Object[] args){
return targetClz.getPackage()+methodName+ StringUtils.arrayToDelimitedString(args, ".");
}
private Object cacheInvoke(MethodCache sc, MethodSignature methodSign, ProceedingJoinPoint jp, String cacheKey, int expire) throws Throwable {
//得到方法的结果类型
Class returnClazz = methodSign.getReturnType();
Object result;
Object rval = bbscache.opsForValue().get(cacheKey);
if (rval == null) {
logger.info("miss from cache, load backend for key : {}", cacheKey);
result = jp.proceed();
if(result != null){
logger.info("cache to redis {},{}", cacheKey, JSON.toJSONString(result));
bbscache.opsForValue().set(cacheKey, result, expire, TimeUnit.SECONDS);
}
}
else{
if(ObjectUtils.isBaseType(returnClazz)){
result = rval;
}
else {
JSON.parseObject((String) rval, new TypeReference<List<Integer>>() {});
result = JSONObject.toJavaObject((JSON) rval, returnClazz);
}
logger.info("cache get object: {}", JSON.toJSONString(rval));
}
return result;
}
}
使用的时候只要在方法上加上注解就ok
@MethodCache()
public List<Integer> testInt() {
List<Integer> list = new ArrayList<>();
list.add(0);
list.add(1);
return list;
}
利用redis缓存方法执行结果,如果已缓存则将缓存转换为方法出参类型,正常的参数类型都OK的,可以得到方法的结果类型,但是如果是泛型类型的话,比如List和map, 却只能得到List,Map类型,并不能得到确切的泛型类型,比如
List<Map<String,String>>
List<User>
Map<String, User>
只能解出
List<JSONObject>
Map<Object, JSONObject>
因为泛型的类型擦除,所以很难做到,也想过在注解里面传入具体类型,但是也没办法把泛型类型传进来,这个要怎么做,希望大家提供思路!!