注解及AOP实现Redis缓存组件

使用AOP以及注解实现缓存组件

1. 为什么使用AOP和注解实现缓存?

  项目是一个报表系统,使用druid.io查询hue,每次查询巨大,可以达到每次30多M,而且后台也有很多运算,每次查询对服务器对压力很大,经常出现young gc,因此计划加入缓存提高查询效率,同时减少服务端的压力。

2. 为什么这么设计?

  报表系统的一个基本逻辑:根据查询参数查询数据库,由于是离线的库,每天刷一遍数据,所以每次请求之间的区别仅仅是传递的参数,所以可以将缓存的位置直接放在controller层,根据传递的参数来缓存数据,这样使用注解来实现就比较好了。

3. 注解

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface Cache {
    int expire() default 60 * 60 * 12;

    String type() default "Object";

    Class returnClass() default Object.class;

    Class param() default Object.class;
}

 

其中expire是缓存的保存时间,type是返回的类型是Object还是List形式的对象,returnClass是返回的对象类型,param是参数的类型

4. 设计切面类

/**
 * @author 张驰
 * @since 26 九月 2018
 */
@Aspect
@Component
public class CacheInterceptor {
    private static final String OBJECT = "Object";

    private static final String LIST = "List";

    private static final String PRE_KEY = "md_cache";

    @Autowired
    private JedisClient jedisClient;

    @SuppressWarnings("unchecked")
    @Around("@annotation(com.wormpex.data.warehouse.biz.utils.cache.Cache)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        if (joinPoint == null) {
            return null;
        }

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Cache myAnnotation = method.getAnnotation(Cache.class);
        String type = myAnnotation.type();
        Class returnClass = myAnnotation.returnClass();

        String key = "";
        try {
            key = getKey(joinPoint, method);
            //如果没有命中到缓存值。应该调用目标函数,通过目标函数计算实际值。
            String result = jedisClient.get(key);
            if (StringUtils.isNotBlank(result)) {
                switch (type) {
                    case OBJECT:
                        if (returnClass.equals(JsonResult.class)) {
                            JsonResult jsonResult = JsonUtil.of(result, JsonResult.class);
                            if (!jsonResult.isSuccess()) {
                                jedisClient.del(key);
                                return joinPoint.proceed();
                            }
                        }
                        return JsonUtil.of(result, returnClass);
                    case LIST:
                        return JsonUtil.ofList(result, returnClass);
                    default:
                        jedisClient.del(key);
                        return joinPoint.proceed();
                }
            }
        }
        catch (Throwable t) {
            jedisClient.del(key);
            return joinPoint.proceed();
        }

        Object o = joinPoint.proceed();
        jedisClient.set(key, JsonUtil.toJson(o));
        jedisClient.expire(key, myAnnotation.expire());
        return o;
    }

    /**
     * 根据参数生成cachekey
     * @param joinPoint
     * @param method
     * @return
     */
    @SuppressWarnings("unchecked")
    private String getKey(ProceedingJoinPoint joinPoint, Method method) {
        Class paramClass = method.getAnnotation(Cache.class).param();
        String methodName = method.getDeclaringClass().getPackage().toString() + method.getName() + paramClass.getName();
        return PRE_KEY + methodName + MD5Util.encode(String.valueOf(JsonUtil.toJson(get(paramClass, joinPoint.getArgs()[0]))));
    }

    private static <T> T get(Class<T> clz, Object o) {
        if (clz.isInstance(o)) {
            return clz.cast(o);
        }
        throw new IllegalArgumentException();
    }
}

 

原文地址:https://www.cnblogs.com/zhangchiblog/p/9718952.html