注解及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(); } }