作者 朱兆平

redis二级缓存

  1 +package com.tianbo.warehouse.annotation.cache.annotation;
  2 +
  3 +import java.lang.annotation.ElementType;
  4 +import java.lang.annotation.Retention;
  5 +import java.lang.annotation.RetentionPolicy;
  6 +import java.lang.annotation.Target;
  7 +
  8 +/**
  9 + * @author mrz
  10 + * 缓存清除
  11 + */
  12 +@Retention(RetentionPolicy.RUNTIME)
  13 +@Target({ElementType.METHOD})
  14 +public @interface RedisCacheDelTarget {
  15 +
  16 + /**指定命名规范的key前缀或完整名称
  17 + * 类名+方法名+key名称+*
  18 + * com.tianbo.warehouse.service.imp.PermissionServiceImp_findAll_findAllMenus
  19 + */
  20 +
  21 + String cacheKey();
  22 +
  23 + //是否清空cacheName的全部数据
  24 + boolean allEntries() default true;
  25 +}
@@ -13,10 +13,10 @@ import java.lang.annotation.Target; @@ -13,10 +13,10 @@ import java.lang.annotation.Target;
13 @Target({ElementType.METHOD}) 13 @Target({ElementType.METHOD})
14 public @interface RedisCacheable { 14 public @interface RedisCacheable {
15 15
16 - //缓存名称,可以多个 16 + //缓存名称,可以多个。暂时不投入使用
17 String[] cacheNames() default ""; 17 String[] cacheNames() default "";
18 18
19 - //缓存key 19 + //缓存key,KEY的名称必须是【#+参数的名称,或者#+{对象名称}】,参考spel表达式
20 String cacheKey(); 20 String cacheKey();
21 21
22 //有效期时间(单位:秒),默认8个小时 22 //有效期时间(单位:秒),默认8个小时
1 package com.tianbo.warehouse.annotation.cache.imp; 1 package com.tianbo.warehouse.annotation.cache.imp;
2 2
  3 +import com.tianbo.warehouse.annotation.cache.annotation.RedisCacheDelTarget;
3 import com.tianbo.warehouse.annotation.cache.annotation.RedisCacheEvict; 4 import com.tianbo.warehouse.annotation.cache.annotation.RedisCacheEvict;
4 import com.tianbo.warehouse.annotation.cache.annotation.RedisCachePut; 5 import com.tianbo.warehouse.annotation.cache.annotation.RedisCachePut;
5 import com.tianbo.warehouse.annotation.cache.annotation.RedisCacheable; 6 import com.tianbo.warehouse.annotation.cache.annotation.RedisCacheable;
6 -import com.tianbo.warehouse.util.IO.JDKSerializeUtil;  
7 -import com.tianbo.warehouse.util.redis.DefaultKeyGenerator; 7 +import com.tianbo.warehouse.annotation.cache.util.JDKSerializeUtil;
  8 +import com.tianbo.warehouse.annotation.cache.util.redis.DefaultKeyGenerator;
8 import lombok.extern.slf4j.Slf4j; 9 import lombok.extern.slf4j.Slf4j;
9 import org.aspectj.lang.ProceedingJoinPoint; 10 import org.aspectj.lang.ProceedingJoinPoint;
10 import org.aspectj.lang.Signature; 11 import org.aspectj.lang.Signature;
@@ -41,7 +42,7 @@ public class RedisCacheableAspect { @@ -41,7 +42,7 @@ public class RedisCacheableAspect {
41 public Object cached(final ProceedingJoinPoint pjp , RedisCacheable cache) throws Throwable { 42 public Object cached(final ProceedingJoinPoint pjp , RedisCacheable cache) throws Throwable {
42 try{ 43 try{
43 //生成缓存KEY 44 //生成缓存KEY
44 - String[] keys = defaultKeyGenerator.generateKey(pjp, cache.cacheNames(), cache.cacheKey()); 45 + String[] keys = defaultKeyGenerator.generateKey(pjp, cache.cacheKey());
45 Object valueData = null; 46 Object valueData = null;
46 for(String key : keys){ 47 for(String key : keys){
47 //获取缓存中的值 48 //获取缓存中的值
@@ -137,4 +138,30 @@ public class RedisCacheableAspect { @@ -137,4 +138,30 @@ public class RedisCacheableAspect {
137 } 138 }
138 return pjp.proceed(); 139 return pjp.proceed();
139 } 140 }
  141 +
  142 +
  143 + /**
  144 + * @Description: 删除指定KEY命名规范的缓存
  145 + * @param:
  146 + * @return:
  147 + * @throws:
  148 + * @author: pengl
  149 + * @Date:2017/11/13 17:09
  150 + */
  151 + @Around(value = "@annotation(redisCacheDelTarget)")
  152 + public Object cacheEvict (final ProceedingJoinPoint pjp , RedisCacheDelTarget redisCacheDelTarget) throws Throwable{
  153 + try{
  154 + String cacheKey = redisCacheDelTarget.cacheKey();
  155 + boolean allEntries = redisCacheDelTarget.allEntries();
  156 + if(allEntries){
  157 + if(cacheKey!=null){
  158 + Long result = redisTemplate.delete(redisTemplate.keys(cacheKey+"*"));
  159 + log.info("已删除{}个缓存",result);
  160 + }
  161 + }
  162 + }catch (Exception e){
  163 + log.error("批量删除Redis缓存失败,异常信息:" + e.getMessage());
  164 + }
  165 + return pjp.proceed();
  166 + }
140 } 167 }
1 -package com.tianbo.warehouse.util.IO; 1 +package com.tianbo.warehouse.annotation.cache.util;
2 2
3 import java.io.ByteArrayInputStream; 3 import java.io.ByteArrayInputStream;
4 import java.io.ByteArrayOutputStream; 4 import java.io.ByteArrayOutputStream;
1 -package com.tianbo.warehouse.util.redis; 1 +package com.tianbo.warehouse.annotation.cache.util.redis;
2 2
3 import com.alibaba.druid.util.StringUtils; 3 import com.alibaba.druid.util.StringUtils;
4 import lombok.extern.slf4j.Slf4j; 4 import lombok.extern.slf4j.Slf4j;
@@ -31,6 +31,7 @@ public class DefaultKeyGenerator { @@ -31,6 +31,7 @@ public class DefaultKeyGenerator {
31 } 31 }
32 32
33 Signature signature = pjp.getSignature(); 33 Signature signature = pjp.getSignature();
  34 +
34 if(cacheNames == null || cacheNames.length == 0){ 35 if(cacheNames == null || cacheNames.length == 0){
35 cacheNames = new String[]{signature.getDeclaringTypeName() + "." + signature.getName()}; 36 cacheNames = new String[]{signature.getDeclaringTypeName() + "." + signature.getName()};
36 } 37 }
@@ -40,18 +41,91 @@ public class DefaultKeyGenerator { @@ -40,18 +41,91 @@ public class DefaultKeyGenerator {
40 if (!(signature instanceof MethodSignature)) { 41 if (!(signature instanceof MethodSignature)) {
41 throw new IllegalArgumentException("This annotation can only be used for methods..."); 42 throw new IllegalArgumentException("This annotation can only be used for methods...");
42 } 43 }
  44 +
  45 + /**
  46 + * 缓存KEY 的命名规范
  47 + * 类名+方法名+key名称+参数1名+参数1值+。。。
  48 + * (加上key名称是为了防止方法名重复)
  49 + * 开始生成缓存key名称
  50 + */
43 MethodSignature methodSignature = (MethodSignature) signature; 51 MethodSignature methodSignature = (MethodSignature) signature;
  52 + String targetClassName = pjp.getTarget().getClass().getName();
  53 + String targetMethodName = methodSignature.getName();
44 //method参数列表 54 //method参数列表
45 String[] parameterNames = methodSignature.getParameterNames(); 55 String[] parameterNames = methodSignature.getParameterNames();
46 Object[] args = pjp.getArgs(); 56 Object[] args = pjp.getArgs();
  57 +
  58 + StringBuffer sb = new StringBuffer();
  59 + //evaluationContext存储参数的kEY与Value
47 for(int i = 0; i < parameterNames.length; i++){ 60 for(int i = 0; i < parameterNames.length; i++){
48 String parameterName = parameterNames[i]; 61 String parameterName = parameterNames[i];
  62 + sb.append(parameterName).append("_").append("Value").append("_").append(args[i]);
  63 + log.info("=============>>>generateKeys :{}",sb.toString());
49 evaluationContext.setVariable(parameterName, args[i]); 64 evaluationContext.setVariable(parameterName, args[i]);
50 } 65 }
  66 +
  67 + /**
  68 + * 缓存key名称生成结束
  69 + */
  70 +
  71 + //通过cachekey的值,从evaluationContext中根据cachekey的值获取其value
51 for(int j = 0; j < cacheNames.length; j++){ 72 for(int j = 0; j < cacheNames.length; j++){
52 - results[j] = "RedisKey_CacheName_" + cacheNames[j] + "_CacheKey_" +  
53 - new SpelExpressionParser().parseExpression(cacheKey).getValue(evaluationContext, String.class);//暂时只使用String类型 73 + results[j] = sb.insert(0,cacheNames).toString();
  74 +// results[j] = "RedisKey_CacheName_" + cacheNames[j] + "_CacheKey_" +
  75 +// new SpelExpressionParser().parseExpression(cacheKey).getValue(evaluationContext, String.class);//暂时只使用String类型
  76 + }
  77 + log.info("=============>>>generateKeys : " + Arrays.toString(results));
  78 + return results;
  79 + }
  80 +
  81 +
  82 + public String[] generateKey(ProceedingJoinPoint pjp,String cacheKey) throws NoSuchMethodException {
  83 + if (StringUtils.isEmpty(cacheKey)) {
  84 + throw new NullPointerException("CacheKey can not be null...");
54 } 85 }
  86 +
  87 + Signature signature = pjp.getSignature();
  88 +
  89 + EvaluationContext evaluationContext = new StandardEvaluationContext();
  90 + if (!(signature instanceof MethodSignature)) {
  91 + throw new IllegalArgumentException("This annotation can only be used for methods...");
  92 + }
  93 + String[] results = new String[1];
  94 + /**
  95 + * 缓存KEY 的命名规范
  96 + * 类名+方法名+key名称+参数1名+参数1值+。。。
  97 + * (加上key名称是为了防止方法名重复)
  98 + * 开始生成缓存key名称
  99 + */
  100 + MethodSignature methodSignature = (MethodSignature) signature;
  101 + String targetClassName = pjp.getTarget().getClass().getName();
  102 + String targetMethodName = methodSignature.getName();
  103 + //method参数列表
  104 + String[] parameterNames = methodSignature.getParameterNames();
  105 + Object[] args = pjp.getArgs();
  106 +
  107 + StringBuffer sb = new StringBuffer();
  108 + sb.append(targetClassName).append("_")
  109 + .append(targetMethodName).append("_")
  110 + .append(cacheKey).append("_");
  111 + //evaluationContext存储参数的kEY与Value
  112 + for(int i = 0; i < parameterNames.length; i++){
  113 + String parameterName = parameterNames[i];
  114 + sb.append("P").append(parameterName).append("_")
  115 + .append("V").append(args[i]);
  116 + evaluationContext.setVariable(parameterName,args[i]);
  117 + }
  118 +
  119 + results[0] = sb.toString();
  120 + //通过cachekey的值,从evaluationContext中根据cachekey的值获取其value,
  121 + //类名+方法名+key名称+参数1名+参数1值+。。。
  122 +
  123 +// new SpelExpressionParser().parseExpression(cacheKey).getValue(evaluationContext, String.class);//暂时只使用String类型
  124 +
  125 + /**
  126 + * 缓存key名称生成结束
  127 + * com.tianbo.warehouse.service.imp.PermissionServiceImp_findAll_findAllMenus_PpageNum_V1PpageSize_V500Pname_V
  128 + */
55 log.info("=============>>>generateKeys : " + Arrays.toString(results)); 129 log.info("=============>>>generateKeys : " + Arrays.toString(results));
56 return results; 130 return results;
57 } 131 }