作者 朱兆平

用户目录缓存处理优化-未完全完成

@@ -19,8 +19,8 @@ public @interface RedisCacheable { @@ -19,8 +19,8 @@ public @interface RedisCacheable {
19 //缓存key,KEY的名称必须是【#+参数的名称,或者#+{对象名称}】,参考spel表达式 19 //缓存key,KEY的名称必须是【#+参数的名称,或者#+{对象名称}】,参考spel表达式
20 String cacheKey(); 20 String cacheKey();
21 21
22 - //有效期时间(单位:秒),默认8个小时  
23 - int expire() default 28800; 22 + //有效期时间(单位:秒),默认5分钟
  23 + int expire() default 5*60;
24 24
25 //缓存主动刷新时间(单位:秒),默认不主动刷新 25 //缓存主动刷新时间(单位:秒),默认不主动刷新
26 int reflash() default -1; 26 int reflash() default -1;
@@ -6,9 +6,12 @@ import com.tianbo.warehouse.annotation.cache.annotation.RedisCachePut; @@ -6,9 +6,12 @@ import com.tianbo.warehouse.annotation.cache.annotation.RedisCachePut;
6 import com.tianbo.warehouse.annotation.cache.annotation.RedisCacheable; 6 import com.tianbo.warehouse.annotation.cache.annotation.RedisCacheable;
7 import com.tianbo.warehouse.annotation.cache.util.JDKSerializeUtil; 7 import com.tianbo.warehouse.annotation.cache.util.JDKSerializeUtil;
8 import com.tianbo.warehouse.annotation.cache.util.redis.DefaultKeyGenerator; 8 import com.tianbo.warehouse.annotation.cache.util.redis.DefaultKeyGenerator;
  9 +import com.tianbo.warehouse.util.RedisUtils;
9 import lombok.extern.slf4j.Slf4j; 10 import lombok.extern.slf4j.Slf4j;
  11 +import org.apache.commons.lang.StringUtils;
10 import org.aspectj.lang.ProceedingJoinPoint; 12 import org.aspectj.lang.ProceedingJoinPoint;
11 import org.aspectj.lang.Signature; 13 import org.aspectj.lang.Signature;
  14 +import org.aspectj.lang.annotation.After;
12 import org.aspectj.lang.annotation.Around; 15 import org.aspectj.lang.annotation.Around;
13 import org.aspectj.lang.annotation.Aspect; 16 import org.aspectj.lang.annotation.Aspect;
14 import org.springframework.beans.factory.annotation.Autowired; 17 import org.springframework.beans.factory.annotation.Autowired;
@@ -17,6 +20,8 @@ import org.springframework.data.redis.core.ValueOperations; @@ -17,6 +20,8 @@ import org.springframework.data.redis.core.ValueOperations;
17 import org.springframework.stereotype.Component; 20 import org.springframework.stereotype.Component;
18 21
19 import javax.annotation.Resource; 22 import javax.annotation.Resource;
  23 +import java.util.Objects;
  24 +import java.util.Set;
20 import java.util.concurrent.TimeUnit; 25 import java.util.concurrent.TimeUnit;
21 26
22 @Aspect 27 @Aspect
@@ -49,16 +54,18 @@ public class RedisCacheableAspect { @@ -49,16 +54,18 @@ public class RedisCacheableAspect {
49 ValueOperations<String, Object> valueOper = redisTemplate.opsForValue(); 54 ValueOperations<String, Object> valueOper = redisTemplate.opsForValue();
50 byte[] value = (byte[]) valueOper.get(key); 55 byte[] value = (byte[]) valueOper.get(key);
51 if(value != null){ 56 if(value != null){
  57 + log.info("[CACHE-HIT]-命中:[{}],缓存时效:[{}]分",key,redisTemplate.getExpire(key, TimeUnit.MINUTES));
52 //如果缓存有值,需要判断刷新缓存设置和当前缓存的失效时间 58 //如果缓存有值,需要判断刷新缓存设置和当前缓存的失效时间
53 int reflash = cache.reflash(); 59 int reflash = cache.reflash();
54 - if(reflash > 0){ 60 + if(reflash > 0 && StringUtils.isNotEmpty(key)){
55 //查询当前缓存失效时间是否在主动刷新规则范围内 61 //查询当前缓存失效时间是否在主动刷新规则范围内
56 long exp = redisTemplate.getExpire(key, TimeUnit.SECONDS); 62 long exp = redisTemplate.getExpire(key, TimeUnit.SECONDS);
57 if(exp <= reflash){ 63 if(exp <= reflash){
58 //主动刷新缓存,为不影响本次获取效率,采用异步线程刷新缓存 64 //主动刷新缓存,为不影响本次获取效率,采用异步线程刷新缓存
59 } 65 }
60 } 66 }
61 - return JDKSerializeUtil.unserialize(value); 67 + valueData = JDKSerializeUtil.unserialize(value);
  68 + return valueData;
62 } 69 }
63 //缓存中没有值,执行实际数据查询方法 70 //缓存中没有值,执行实际数据查询方法
64 if(valueData == null) { 71 if(valueData == null) {
@@ -66,9 +73,13 @@ public class RedisCacheableAspect { @@ -66,9 +73,13 @@ public class RedisCacheableAspect {
66 } 73 }
67 //写入缓存 74 //写入缓存
68 if(cache.expire() > 0) { 75 if(cache.expire() > 0) {
69 - valueOper.set(key, JDKSerializeUtil.serialize(valueData),cache.expire(),TimeUnit.SECONDS); //否则设置缓存时间 ,序列化存储 76 + //否则设置缓存时间 ,序列化存储
  77 + valueOper.set(key, Objects.requireNonNull(JDKSerializeUtil.serialize(valueData)),cache.expire(),TimeUnit.SECONDS);
  78 + log.info("[CACHE-SET]-已设置缓存:[{}],缓存时效:[{}秒]",key,cache.expire());
70 } else { 79 } else {
71 - valueOper.set(key, JDKSerializeUtil.serialize(valueData)); 80 + //永不过期
  81 + valueOper.set(key, Objects.requireNonNull(JDKSerializeUtil.serialize(valueData)));
  82 + log.info("[CACHE-SET]-已设置永久缓存:[{}]",key);
72 } 83 }
73 } 84 }
74 return valueData; 85 return valueData;
@@ -89,15 +100,17 @@ public class RedisCacheableAspect { @@ -89,15 +100,17 @@ public class RedisCacheableAspect {
89 public Object cachePut (final ProceedingJoinPoint pjp , RedisCachePut cacheput) throws Throwable{ 100 public Object cachePut (final ProceedingJoinPoint pjp , RedisCachePut cacheput) throws Throwable{
90 try{ 101 try{
91 //生成缓存KEY 102 //生成缓存KEY
92 - String[] keys = defaultKeyGenerator.generateKey(pjp, cacheput.cacheNames(), cacheput.cacheKey()); 103 +// String[] keys = defaultKeyGenerator.generateKey(pjp, cacheput.cacheNames(), cacheput.cacheKey());
  104 + String[] keys = defaultKeyGenerator.generateKey(pjp, cacheput.cacheKey());
93 Object valueData = pjp.proceed(); 105 Object valueData = pjp.proceed();
94 //写入缓存 106 //写入缓存
95 for(String key : keys){ 107 for(String key : keys){
96 ValueOperations<String, Object> valueOper = redisTemplate.opsForValue(); 108 ValueOperations<String, Object> valueOper = redisTemplate.opsForValue();
97 if(cacheput.expire() > 0) { 109 if(cacheput.expire() > 0) {
98 - valueOper.set(key, JDKSerializeUtil.serialize(pjp.getArgs()[0]),cacheput.expire(),TimeUnit.SECONDS); 110 + valueOper.set(key, Objects.requireNonNull(JDKSerializeUtil.serialize(valueData)),cacheput.expire(),TimeUnit.SECONDS);
  111 + log.info("[CACHE-SET]-更新缓存:[{}],缓存时效:[{}秒]",key,cacheput.expire());
99 } else { 112 } else {
100 - valueOper.set(key, JDKSerializeUtil.serialize(pjp.getArgs()[0])); 113 + valueOper.set(key, Objects.requireNonNull(JDKSerializeUtil.serialize(valueData)));
101 } 114 }
102 } 115 }
103 return valueData; 116 return valueData;
@@ -151,6 +164,7 @@ public class RedisCacheableAspect { @@ -151,6 +164,7 @@ public class RedisCacheableAspect {
151 @Around(value = "@annotation(redisCacheDelTarget)") 164 @Around(value = "@annotation(redisCacheDelTarget)")
152 public Object cacheEvict (final ProceedingJoinPoint pjp , RedisCacheDelTarget redisCacheDelTarget) throws Throwable{ 165 public Object cacheEvict (final ProceedingJoinPoint pjp , RedisCacheDelTarget redisCacheDelTarget) throws Throwable{
153 try{ 166 try{
  167 + Set<String> deleteBatchByKeys = RedisUtils.deleteBatchByKeys(redisCacheDelTarget.cacheKey());
154 String cacheKey = redisCacheDelTarget.cacheKey(); 168 String cacheKey = redisCacheDelTarget.cacheKey();
155 boolean allEntries = redisCacheDelTarget.allEntries(); 169 boolean allEntries = redisCacheDelTarget.allEntries();
156 if(allEntries){ 170 if(allEntries){
@@ -51,8 +51,9 @@ public class DefaultKeyGenerator { @@ -51,8 +51,9 @@ public class DefaultKeyGenerator {
51 MethodSignature methodSignature = (MethodSignature) signature; 51 MethodSignature methodSignature = (MethodSignature) signature;
52 String targetClassName = pjp.getTarget().getClass().getName(); 52 String targetClassName = pjp.getTarget().getClass().getName();
53 String targetMethodName = methodSignature.getName(); 53 String targetMethodName = methodSignature.getName();
54 - //method参数列表 54 + //method参数
55 String[] parameterNames = methodSignature.getParameterNames(); 55 String[] parameterNames = methodSignature.getParameterNames();
  56 + //method参数值
56 Object[] args = pjp.getArgs(); 57 Object[] args = pjp.getArgs();
57 58
58 StringBuffer sb = new StringBuffer(); 59 StringBuffer sb = new StringBuffer();
@@ -98,21 +99,20 @@ public class DefaultKeyGenerator { @@ -98,21 +99,20 @@ public class DefaultKeyGenerator {
98 * 开始生成缓存key名称 99 * 开始生成缓存key名称
99 */ 100 */
100 MethodSignature methodSignature = (MethodSignature) signature; 101 MethodSignature methodSignature = (MethodSignature) signature;
101 - String targetClassName = pjp.getTarget().getClass().getName(); 102 + String targetClassName = pjp.getTarget().getClass().getSimpleName();
102 String targetMethodName = methodSignature.getName(); 103 String targetMethodName = methodSignature.getName();
103 //method参数列表 104 //method参数列表
104 String[] parameterNames = methodSignature.getParameterNames(); 105 String[] parameterNames = methodSignature.getParameterNames();
105 Object[] args = pjp.getArgs(); 106 Object[] args = pjp.getArgs();
106 107
107 StringBuffer sb = new StringBuffer(); 108 StringBuffer sb = new StringBuffer();
108 - sb.append(targetClassName).append("_")  
109 - .append(targetMethodName).append("_")  
110 - .append(cacheKey).append("_"); 109 + sb.append(cacheKey).append(":").append(targetClassName).append(":")
  110 + .append(targetMethodName).append(":");
111 //evaluationContext存储参数的kEY与Value 111 //evaluationContext存储参数的kEY与Value
112 for(int i = 0; i < parameterNames.length; i++){ 112 for(int i = 0; i < parameterNames.length; i++){
113 String parameterName = parameterNames[i]; 113 String parameterName = parameterNames[i];
114 - sb.append("P").append(parameterName).append("_")  
115 - .append("V").append(args[i]).append("_"); 114 + sb.append("P").append(parameterName).append(":")
  115 + .append("V").append(args[i]).append(":");
116 evaluationContext.setVariable(parameterName,args[i]); 116 evaluationContext.setVariable(parameterName,args[i]);
117 } 117 }
118 118
@@ -192,7 +192,7 @@ public class PermissionServiceImp implements PermissionService { @@ -192,7 +192,7 @@ public class PermissionServiceImp implements PermissionService {
192 } 192 }
193 193
194 @Override 194 @Override
195 -// @RedisCacheable(cacheKey = "getUserMenuTreeByUserId") 195 + @RedisCacheable(cacheKey = "getUserMenuTreeByUserId")
196 public List<PERMISSION> getUserMenuTreeByUserId(@NotNull Integer userId) { 196 public List<PERMISSION> getUserMenuTreeByUserId(@NotNull Integer userId) {
197 List<PERMISSION> loginedUserMenus = permissionMapper.getUserMenuTreeByUserId(userId); 197 List<PERMISSION> loginedUserMenus = permissionMapper.getUserMenuTreeByUserId(userId);
198 List<PERMISSION> loginedUserMenusTree = loginedUserMenus.stream() 198 List<PERMISSION> loginedUserMenusTree = loginedUserMenus.stream()
@@ -3,6 +3,7 @@ package com.tianbo.warehouse.service.imp; @@ -3,6 +3,7 @@ package com.tianbo.warehouse.service.imp;
3 import com.github.pagehelper.Page; 3 import com.github.pagehelper.Page;
4 import com.github.pagehelper.PageHelper; 4 import com.github.pagehelper.PageHelper;
5 import com.github.pagehelper.PageInfo; 5 import com.github.pagehelper.PageInfo;
  6 +import com.tianbo.warehouse.annotation.cache.annotation.RedisCacheDelTarget;
6 import com.tianbo.warehouse.dao.DepartmentMapper; 7 import com.tianbo.warehouse.dao.DepartmentMapper;
7 import com.tianbo.warehouse.dao.ROLEMapper; 8 import com.tianbo.warehouse.dao.ROLEMapper;
8 import com.tianbo.warehouse.dao.RolePermissionMapper; 9 import com.tianbo.warehouse.dao.RolePermissionMapper;
@@ -90,6 +91,7 @@ public class RoleServiceImp implements RoleService{ @@ -90,6 +91,7 @@ public class RoleServiceImp implements RoleService{
90 return roleMapper.insertSelective(record); 91 return roleMapper.insertSelective(record);
91 } 92 }
92 93
  94 + @RedisCacheDelTarget(cacheKey = "getUserMenuTreeByUserId")
93 @Transactional(rollbackFor = Exception.class) 95 @Transactional(rollbackFor = Exception.class)
94 @Override 96 @Override
95 public int setRolePermissoin(RolePermission record){ 97 public int setRolePermissoin(RolePermission record){
@@ -3,6 +3,7 @@ package com.tianbo.warehouse.service.imp; @@ -3,6 +3,7 @@ package com.tianbo.warehouse.service.imp;
3 import com.github.pagehelper.Page; 3 import com.github.pagehelper.Page;
4 import com.github.pagehelper.PageHelper; 4 import com.github.pagehelper.PageHelper;
5 import com.github.pagehelper.PageInfo; 5 import com.github.pagehelper.PageInfo;
  6 +import com.tianbo.warehouse.annotation.cache.annotation.RedisCacheDelTarget;
6 import com.tianbo.warehouse.dao.PERMISSIONMapper; 7 import com.tianbo.warehouse.dao.PERMISSIONMapper;
7 import com.tianbo.warehouse.dao.ROLEMapper; 8 import com.tianbo.warehouse.dao.ROLEMapper;
8 import com.tianbo.warehouse.dao.USERSMapper; 9 import com.tianbo.warehouse.dao.USERSMapper;
@@ -14,6 +15,7 @@ import com.tianbo.warehouse.model.UserRole; @@ -14,6 +15,7 @@ import com.tianbo.warehouse.model.UserRole;
14 import com.tianbo.warehouse.service.PermissionService; 15 import com.tianbo.warehouse.service.PermissionService;
15 import com.tianbo.warehouse.service.UserService; 16 import com.tianbo.warehouse.service.UserService;
16 import org.springframework.beans.factory.annotation.Autowired; 17 import org.springframework.beans.factory.annotation.Autowired;
  18 +import org.springframework.cache.annotation.Cacheable;
17 import org.springframework.stereotype.Service; 19 import org.springframework.stereotype.Service;
18 import org.springframework.transaction.annotation.Transactional; 20 import org.springframework.transaction.annotation.Transactional;
19 21
@@ -23,6 +25,7 @@ import java.util.Collections; @@ -23,6 +25,7 @@ import java.util.Collections;
23 import java.util.List; 25 import java.util.List;
24 import java.util.Random; 26 import java.util.Random;
25 27
  28 +@Cacheable
26 @Service(value = "userService") 29 @Service(value = "userService")
27 public class UserServiceImpl implements UserService{ 30 public class UserServiceImpl implements UserService{
28 31
@@ -153,6 +156,7 @@ public class UserServiceImpl implements UserService{ @@ -153,6 +156,7 @@ public class UserServiceImpl implements UserService{
153 * @return 156 * @return
154 */ 157 */
155 @Override 158 @Override
  159 + @RedisCacheDelTarget(cacheKey = "getUserMenuTreeByUserId")
156 @Transactional(rollbackFor = Exception.class) 160 @Transactional(rollbackFor = Exception.class)
157 public int setUserRole(UserRole userRole){ 161 public int setUserRole(UserRole userRole){
158 try{ 162 try{
@@ -166,10 +170,6 @@ public class UserServiceImpl implements UserService{ @@ -166,10 +170,6 @@ public class UserServiceImpl implements UserService{
166 userRoleMapper.insertSelective(ur); 170 userRoleMapper.insertSelective(ur);
167 } 171 }
168 } 172 }
169 - /**  
170 - * 重写redis用户权限等相关资料  
171 - */  
172 -  
173 return 1; 173 return 1;
174 }catch (Exception e){ 174 }catch (Exception e){
175 e.printStackTrace(); 175 e.printStackTrace();
1 package com.tianbo.warehouse.util; 1 package com.tianbo.warehouse.util;
2 import org.springframework.beans.factory.annotation.Autowired; 2 import org.springframework.beans.factory.annotation.Autowired;
3 import org.springframework.boot.actuate.health.CompositeHealthIndicator; 3 import org.springframework.boot.actuate.health.CompositeHealthIndicator;
4 -import org.springframework.data.redis.core.BoundListOperations;  
5 -import org.springframework.data.redis.core.StringRedisTemplate; 4 +import org.springframework.data.redis.core.*;
6 import org.springframework.stereotype.Component; 5 import org.springframework.stereotype.Component;
7 import org.springframework.util.CollectionUtils; 6 import org.springframework.util.CollectionUtils;
8 7
9 -import java.util.List;  
10 -import java.util.Map;  
11 -import java.util.Set; 8 +import javax.annotation.PostConstruct;
  9 +import java.nio.charset.StandardCharsets;
  10 +import java.util.*;
12 import java.util.concurrent.TimeUnit; 11 import java.util.concurrent.TimeUnit;
13 12
14 13
@@ -27,6 +26,14 @@ public class RedisUtils { @@ -27,6 +26,14 @@ public class RedisUtils {
27 this.redisTemplate = redisTemplate; 26 this.redisTemplate = redisTemplate;
28 } 27 }
29 28
  29 + public static RedisUtils redisUtils;
  30 +
  31 + @PostConstruct
  32 + public void init(){
  33 + redisUtils = this;
  34 + redisUtils.redisTemplate=this.redisTemplate;
  35 + }
  36 +
30 /** 37 /**
31 * 指定缓存失效时间 38 * 指定缓存失效时间
32 * @param key 键 39 * @param key 键
@@ -83,6 +90,39 @@ public class RedisUtils { @@ -83,6 +90,39 @@ public class RedisUtils {
83 } 90 }
84 } 91 }
85 92
  93 + /**
  94 + * 【 递归删除redis key-value 】
  95 + * 效率会低点 但是删除方法安全
  96 + * @author yangjunxiong
  97 + * @date 2021/6/21 10:05
  98 + **/
  99 + public static Set<String> deleteBatchByKeys(String key) {
  100 + Set<String> keys = new HashSet<>();
  101 + redisUtils.redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
  102 + try (Cursor<byte[]> cursor = connection.scan(new ScanOptions.ScanOptionsBuilder()
  103 + .match(key + "*")
  104 + .count(5000).build())) {
  105 + while (cursor.hasNext()) {
  106 + keys.add(new String(cursor.next(), StandardCharsets.UTF_8));
  107 + }
  108 + } catch (Exception e) {
  109 + throw new RuntimeException(e);
  110 + }
  111 + return keys;
  112 + });
  113 + if (CollectionUtils.isEmpty(keys)) {
  114 + return Collections.emptySet();
  115 + }
  116 + for (String key1 : keys) {
  117 + redisUtils.redisTemplate.delete(key1);
  118 + }
  119 + if (!CollectionUtils.isEmpty(keys)) {
  120 + return RedisUtils.deleteBatchByKeys(key);
  121 + }
  122 + return keys;
  123 + }
  124 +
  125 +
86 //============================String============================= 126 //============================String=============================
87 /** 127 /**
88 * 普通缓存获取 128 * 普通缓存获取
@@ -232,7 +232,7 @@ WHERE @@ -232,7 +232,7 @@ WHERE
232 where role_id = #{roleId,jdbcType=INTEGER} 232 where role_id = #{roleId,jdbcType=INTEGER}
233 </update> 233 </update>
234 234
235 - <select id="findAll" parameterType="java.lang.String" resultMap="TreeWithResultMap"> 235 + <select id="findAll" parameterType="java.lang.String" resultMap="TreeWithPermResultMap">
236 SELECT 236 SELECT
237 <include refid="Base_Column_List" /> 237 <include refid="Base_Column_List" />
238 FROM role 238 FROM role