Rael_liu 2018-07-26 09:53 采纳率: 0%
浏览 5143
已结题

spring如何在Aspect中获取切点注解方法的泛型参数类型

业务场景是这样:想要做一个自定义注解@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>

因为泛型的类型擦除,所以很难做到,也想过在注解里面传入具体类型,但是也没办法把泛型类型传进来,这个要怎么做,希望大家提供思路!!

  • 写回答

4条回答 默认 最新

  • alunSemiconductor 2018-07-26 12:43
    关注

    这个是多态问题吧,跟泛型没啥关系哦。(一句闲话)

    你需要加这个注解的方法,肯定已经确定数据类型了吧,你在切面获取到之后,切面里应该要用**instanceof**判断一下吧,得出结论再决定怎么放入缓存

    评论

报告相同问题?

悬赏问题

  • ¥50 有数据,怎么建立模型求影响全要素生产率的因素
  • ¥50 有数据,怎么用matlab求全要素生产率
  • ¥15 TI的insta-spin例程
  • ¥15 完成下列问题完成下列问题
  • ¥15 C#算法问题, 不知道怎么处理这个数据的转换
  • ¥15 YoloV5 第三方库的版本对照问题
  • ¥15 请完成下列相关问题!
  • ¥15 drone 推送镜像时候 purge: true 推送完毕后没有删除对应的镜像,手动拷贝到服务器执行结果正确在样才能让指令自动执行成功删除对应镜像,如何解决?
  • ¥15 求daily translation(DT)偏差订正方法的代码
  • ¥15 js调用html页面需要隐藏某个按钮