正在显示
7 个修改的文件
包含
494 行增加
和
0 行删除
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 RedisCacheEvict { | ||
15 | + | ||
16 | + //缓存名称 | ||
17 | + String[] cacheNames() default ""; | ||
18 | + | ||
19 | + //缓存key | ||
20 | + String cacheKey(); | ||
21 | + | ||
22 | + //是否清空cacheName的全部数据 | ||
23 | + boolean allEntries() default false; | ||
24 | +} |
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 RedisCachePut { | ||
15 | + | ||
16 | + //缓存名称,可以多个 | ||
17 | + String[] cacheNames() default ""; | ||
18 | + | ||
19 | + //缓存key | ||
20 | + String cacheKey(); | ||
21 | + | ||
22 | + //有效期时间(单位:秒),默认8个小时 | ||
23 | + int expire() default 28800; | ||
24 | +} |
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 RedisCacheable { | ||
15 | + | ||
16 | + //缓存名称,可以多个 | ||
17 | + String[] cacheNames() default ""; | ||
18 | + | ||
19 | + //缓存key | ||
20 | + String cacheKey(); | ||
21 | + | ||
22 | + //有效期时间(单位:秒),默认8个小时 | ||
23 | + int expire() default 28800; | ||
24 | + | ||
25 | + //缓存主动刷新时间(单位:秒),默认不主动刷新 | ||
26 | + int reflash() default -1; | ||
27 | + | ||
28 | +} |
1 | +package com.tianbo.warehouse.annotation.cache.imp; | ||
2 | + | ||
3 | +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.RedisCacheable; | ||
6 | +import com.tianbo.warehouse.util.IO.JDKSerializeUtil; | ||
7 | +import com.tianbo.warehouse.util.redis.DefaultKeyGenerator; | ||
8 | +import lombok.extern.slf4j.Slf4j; | ||
9 | +import org.aspectj.lang.ProceedingJoinPoint; | ||
10 | +import org.aspectj.lang.Signature; | ||
11 | +import org.aspectj.lang.annotation.Around; | ||
12 | +import org.aspectj.lang.annotation.Aspect; | ||
13 | +import org.springframework.beans.factory.annotation.Autowired; | ||
14 | +import org.springframework.data.redis.core.RedisTemplate; | ||
15 | +import org.springframework.data.redis.core.ValueOperations; | ||
16 | +import org.springframework.stereotype.Component; | ||
17 | + | ||
18 | +import javax.annotation.Resource; | ||
19 | +import java.util.concurrent.TimeUnit; | ||
20 | + | ||
21 | +@Aspect | ||
22 | +@Component | ||
23 | +@Slf4j | ||
24 | +public class RedisCacheableAspect { | ||
25 | + | ||
26 | + @Resource | ||
27 | + private RedisTemplate<String , Object> redisTemplate; | ||
28 | + | ||
29 | + @Autowired | ||
30 | + private DefaultKeyGenerator defaultKeyGenerator; | ||
31 | + | ||
32 | + /** | ||
33 | + * @Description: 读取缓存数据 | ||
34 | + * @param: | ||
35 | + * @return: | ||
36 | + * @throws: | ||
37 | + * @author: pengl | ||
38 | + * @Date: 2017/11/13 16:46 | ||
39 | + */ | ||
40 | + @Around(value = "@annotation(cache)") | ||
41 | + public Object cached(final ProceedingJoinPoint pjp , RedisCacheable cache) throws Throwable { | ||
42 | + try{ | ||
43 | + //生成缓存KEY | ||
44 | + String[] keys = defaultKeyGenerator.generateKey(pjp, cache.cacheNames(), cache.cacheKey()); | ||
45 | + Object valueData = null; | ||
46 | + for(String key : keys){ | ||
47 | + //获取缓存中的值 | ||
48 | + ValueOperations<String, Object> valueOper = redisTemplate.opsForValue(); | ||
49 | + byte[] value = (byte[]) valueOper.get(key); | ||
50 | + if(value != null){ | ||
51 | + //如果缓存有值,需要判断刷新缓存设置和当前缓存的失效时间 | ||
52 | + int reflash = cache.reflash(); | ||
53 | + if(reflash > 0){ | ||
54 | + //查询当前缓存失效时间是否在主动刷新规则范围内 | ||
55 | + long exp = redisTemplate.getExpire(key, TimeUnit.SECONDS); | ||
56 | + if(exp <= reflash){ | ||
57 | + //主动刷新缓存,为不影响本次获取效率,采用异步线程刷新缓存 | ||
58 | + } | ||
59 | + } | ||
60 | + return JDKSerializeUtil.unserialize(value); | ||
61 | + } | ||
62 | + //缓存中没有值,执行实际数据查询方法 | ||
63 | + if(valueData == null) { | ||
64 | + valueData = pjp.proceed(); | ||
65 | + } | ||
66 | + //写入缓存 | ||
67 | + if(cache.expire() > 0) { | ||
68 | + valueOper.set(key, JDKSerializeUtil.serialize(valueData),cache.expire(),TimeUnit.SECONDS); //否则设置缓存时间 ,序列化存储 | ||
69 | + } else { | ||
70 | + valueOper.set(key, JDKSerializeUtil.serialize(valueData)); | ||
71 | + } | ||
72 | + } | ||
73 | + return valueData; | ||
74 | + }catch(Exception e){ | ||
75 | + log.error("读取Redis缓存失败,异常信息:" + e.getMessage()); | ||
76 | + return pjp.proceed(); | ||
77 | + } | ||
78 | + } | ||
79 | + /** | ||
80 | + * @Description: 新增缓存 | ||
81 | + * @param: | ||
82 | + * @return: | ||
83 | + * @throws: | ||
84 | + * @author:pengl | ||
85 | + * @Date:2017/11/13 17:09 | ||
86 | + */ | ||
87 | + @Around(value = "@annotation(cacheput)") | ||
88 | + public Object cachePut (final ProceedingJoinPoint pjp , RedisCachePut cacheput) throws Throwable{ | ||
89 | + try{ | ||
90 | + //生成缓存KEY | ||
91 | + String[] keys = defaultKeyGenerator.generateKey(pjp, cacheput.cacheNames(), cacheput.cacheKey()); | ||
92 | + Object valueData = pjp.proceed(); | ||
93 | + //写入缓存 | ||
94 | + for(String key : keys){ | ||
95 | + ValueOperations<String, Object> valueOper = redisTemplate.opsForValue(); | ||
96 | + if(cacheput.expire() > 0) { | ||
97 | + valueOper.set(key, JDKSerializeUtil.serialize(pjp.getArgs()[0]),cacheput.expire(),TimeUnit.SECONDS); | ||
98 | + } else { | ||
99 | + valueOper.set(key, JDKSerializeUtil.serialize(pjp.getArgs()[0])); | ||
100 | + } | ||
101 | + } | ||
102 | + return valueData; | ||
103 | + }catch (Exception e){ | ||
104 | + log.error("写入Redis缓存失败,异常信息:" + e.getMessage()); | ||
105 | + return pjp.proceed(); | ||
106 | + } | ||
107 | + } | ||
108 | + /** | ||
109 | + * @Description: 删除缓存 | ||
110 | + * @param: | ||
111 | + * @return: | ||
112 | + * @throws: | ||
113 | + * @author: pengl | ||
114 | + * @Date:2017/11/13 17:09 | ||
115 | + */ | ||
116 | + @Around(value = "@annotation(cachevict)") | ||
117 | + public Object cacheEvict (final ProceedingJoinPoint pjp , RedisCacheEvict cachevict) throws Throwable{ | ||
118 | + try{ | ||
119 | + String[] cacheNames = cachevict.cacheNames(); | ||
120 | + boolean allEntries = cachevict.allEntries(); | ||
121 | + if(allEntries){ | ||
122 | + if(cacheNames == null || cacheNames.length == 0){ | ||
123 | + Signature signature = pjp.getSignature(); | ||
124 | + cacheNames = new String[]{signature.getDeclaringTypeName() + "." + signature.getName()}; | ||
125 | + } | ||
126 | + for(String cacheName : cacheNames){ | ||
127 | + redisTemplate.delete(redisTemplate.keys("*" + "RedisKey_CacheName_" + cacheName + "*")); | ||
128 | + } | ||
129 | + }else{ | ||
130 | + String[] keys = defaultKeyGenerator.generateKey(pjp, cachevict.cacheNames(), cachevict.cacheKey()); | ||
131 | + for(String key : keys) { | ||
132 | + redisTemplate.delete(key); | ||
133 | + } | ||
134 | + } | ||
135 | + }catch (Exception e){ | ||
136 | + log.error("删除Redis缓存失败,异常信息:" + e.getMessage()); | ||
137 | + } | ||
138 | + return pjp.proceed(); | ||
139 | + } | ||
140 | +} |
1 | +package com.tianbo.warehouse.bean; | ||
2 | + | ||
3 | +import org.springframework.cache.CacheManager; | ||
4 | +import org.springframework.cache.annotation.CachingConfigurerSupport; | ||
5 | +import org.springframework.cache.annotation.EnableCaching; | ||
6 | +import org.springframework.context.annotation.Bean; | ||
7 | +import org.springframework.context.annotation.Configuration; | ||
8 | +import org.springframework.data.redis.cache.RedisCacheConfiguration; | ||
9 | +import org.springframework.data.redis.cache.RedisCacheManager; | ||
10 | +import org.springframework.data.redis.cache.RedisCacheWriter; | ||
11 | +import org.springframework.data.redis.connection.RedisConnectionFactory; | ||
12 | +import org.springframework.data.redis.core.RedisTemplate; | ||
13 | +import org.springframework.data.redis.core.StringRedisTemplate; | ||
14 | +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; | ||
15 | +import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; | ||
16 | +import org.springframework.data.redis.serializer.RedisSerializationContext; | ||
17 | +import org.springframework.data.redis.serializer.StringRedisSerializer; | ||
18 | + | ||
19 | +import java.time.Duration; | ||
20 | +import java.util.HashMap; | ||
21 | +import java.util.Map; | ||
22 | + | ||
23 | +@Configuration | ||
24 | +@EnableCaching | ||
25 | +public class RedisConfig extends CachingConfigurerSupport { | ||
26 | + | ||
27 | + /** | ||
28 | + * 缓存管理器 | ||
29 | + * @param redisConnectionFactory | ||
30 | + * @return | ||
31 | + */ | ||
32 | + @Bean | ||
33 | + public CacheManager cacheManager( | ||
34 | + @SuppressWarnings("rawtypes") RedisConnectionFactory redisConnectionFactory) { | ||
35 | + return new RedisCacheManager( | ||
36 | + | ||
37 | + RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory), | ||
38 | + | ||
39 | + this.getRedisCacheConfigurationWithTtl(246060), // 默认缓存时间策略 | ||
40 | + | ||
41 | + this.getRedisCacheConfigurationMap()); // 指定缓存时间策略 | ||
42 | + | ||
43 | + } | ||
44 | + /** | ||
45 | + * redis模板操作类 | ||
46 | + * @param factory | ||
47 | + * @return | ||
48 | + */ | ||
49 | + @Bean | ||
50 | + public RedisTemplate redisTemplate(RedisConnectionFactory factory) { | ||
51 | + final StringRedisTemplate template = new StringRedisTemplate(); | ||
52 | + template.setConnectionFactory(factory); | ||
53 | + template.setKeySerializer(new StringRedisSerializer()); | ||
54 | + template.setValueSerializer(new JdkSerializationRedisSerializer()); | ||
55 | + return template; | ||
56 | + } | ||
57 | + | ||
58 | + /** | ||
59 | + * @Description: 设置缓存时间, 设置key与value的序列化. | ||
60 | + * @param seconds 缓存时间, 单位: 秒. | ||
61 | + * @return | ||
62 | + */ | ||
63 | + private RedisCacheConfiguration getRedisCacheConfigurationWithTtl(Integer seconds) { | ||
64 | + | ||
65 | + return RedisCacheConfiguration.defaultCacheConfig() | ||
66 | + | ||
67 | + .serializeKeysWith(RedisSerializationContext.SerializationPair | ||
68 | + | ||
69 | + .fromSerializer(new Jackson2JsonRedisSerializer<>(String.class))) | ||
70 | + | ||
71 | + .serializeValuesWith(RedisSerializationContext.SerializationPair | ||
72 | + | ||
73 | + .fromSerializer(new Jackson2JsonRedisSerializer<>(Object.class))) | ||
74 | + | ||
75 | + .entryTtl(Duration.ofSeconds(seconds)); | ||
76 | + | ||
77 | + } | ||
78 | + | ||
79 | + | ||
80 | + private Map<String, RedisCacheConfiguration> getRedisCacheConfigurationMap() { | ||
81 | + | ||
82 | + Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>(); | ||
83 | + | ||
84 | + redisCacheConfigurationMap.put("short_cache", this.getRedisCacheConfigurationWithTtl(30)); | ||
85 | + | ||
86 | + redisCacheConfigurationMap.put("long_cache", this.getRedisCacheConfigurationWithTtl(60)); | ||
87 | + | ||
88 | + return redisCacheConfigurationMap; | ||
89 | + | ||
90 | + } | ||
91 | +} |
1 | +package com.tianbo.warehouse.util.IO; | ||
2 | + | ||
3 | +import java.io.ByteArrayInputStream; | ||
4 | +import java.io.ByteArrayOutputStream; | ||
5 | +import java.io.IOException; | ||
6 | +import java.io.ObjectInputStream; | ||
7 | +import java.io.ObjectOutputStream; | ||
8 | +import java.util.ArrayList; | ||
9 | +import java.util.List; | ||
10 | +import lombok.extern.slf4j.Slf4j; | ||
11 | + | ||
12 | +/** | ||
13 | + * | ||
14 | + * @author baoy | ||
15 | + * | ||
16 | + */ | ||
17 | +@Slf4j | ||
18 | +public class JDKSerializeUtil { | ||
19 | + | ||
20 | + /** | ||
21 | + * 序列化 | ||
22 | + * @param object | ||
23 | + * @return | ||
24 | + */ | ||
25 | + public static byte[] serialize(Object object) { | ||
26 | + ObjectOutputStream oos = null; | ||
27 | + ByteArrayOutputStream baos = null; | ||
28 | + try { | ||
29 | + baos = new ByteArrayOutputStream(); | ||
30 | + oos = new ObjectOutputStream(baos); | ||
31 | + oos.writeObject(object); | ||
32 | + byte[] bytes = baos.toByteArray(); | ||
33 | + return bytes; | ||
34 | + } catch (Exception e) { | ||
35 | + | ||
36 | + } | ||
37 | + return null; | ||
38 | + } | ||
39 | + | ||
40 | + /** | ||
41 | + * 反序列化 | ||
42 | + * @param bytes | ||
43 | + * @return | ||
44 | + */ | ||
45 | + public static Object unserialize(byte[] bytes) { | ||
46 | + ByteArrayInputStream bais = null; | ||
47 | + try { | ||
48 | + bais = new ByteArrayInputStream(bytes); | ||
49 | + ObjectInputStream ois = new ObjectInputStream(bais); | ||
50 | + return ois.readObject(); | ||
51 | + } catch (Exception e) { | ||
52 | + | ||
53 | + } | ||
54 | + return null; | ||
55 | + } | ||
56 | + | ||
57 | + /** | ||
58 | + * 序列化存储list | ||
59 | + * @param list | ||
60 | + * @return | ||
61 | + * @throws IOException | ||
62 | + */ | ||
63 | + public static byte[] serializeList(List<Object> list) throws IOException { | ||
64 | + if (list == null) { | ||
65 | + throw new NullPointerException("Can't serialize null"); | ||
66 | + } | ||
67 | + byte[] rv = null; | ||
68 | + ByteArrayOutputStream bos = null; | ||
69 | + ObjectOutputStream os = null; | ||
70 | + try { | ||
71 | + bos = new ByteArrayOutputStream(); | ||
72 | + os = new ObjectOutputStream(bos); | ||
73 | + for (Object o : list) { | ||
74 | + os.writeObject(o); | ||
75 | + } | ||
76 | + os.writeObject(null); | ||
77 | + rv = bos.toByteArray(); | ||
78 | + } catch (IOException e) { | ||
79 | + throw new IllegalArgumentException("Non-serializable object", e); | ||
80 | + } finally { | ||
81 | + if (os != null) { | ||
82 | + os.close(); | ||
83 | + } | ||
84 | + if (bos != null) { | ||
85 | + bos.close(); | ||
86 | + } | ||
87 | + } | ||
88 | + return rv; | ||
89 | + } | ||
90 | + | ||
91 | + /** | ||
92 | + * 反序列化 | ||
93 | + * @param in | ||
94 | + * @return | ||
95 | + * @throws IOException | ||
96 | + */ | ||
97 | + public static List<Object> deserialize(byte[] in) throws IOException { | ||
98 | + List<Object> list = new ArrayList<Object>(); | ||
99 | + ByteArrayInputStream bis = null; | ||
100 | + ObjectInputStream is = null; | ||
101 | + try { | ||
102 | + if (in != null) { | ||
103 | + bis = new ByteArrayInputStream(in); | ||
104 | + is = new ObjectInputStream(bis); | ||
105 | + while (true) { | ||
106 | + Object object = is.readObject(); | ||
107 | + if (object == null) { | ||
108 | + break; | ||
109 | + } else { | ||
110 | + list.add(object); | ||
111 | + } | ||
112 | + } | ||
113 | + } | ||
114 | + } catch (IOException e) { | ||
115 | + log.error("Caught IOException decoding %d bytes of data", in.length, e); | ||
116 | + } catch (ClassNotFoundException e) { | ||
117 | + log.error("Caught CNFE decoding %d bytes of data", in.length, e); | ||
118 | + } finally { | ||
119 | + if (is != null) { | ||
120 | + is.close(); | ||
121 | + } | ||
122 | + if (bis != null) { | ||
123 | + bis.close(); | ||
124 | + } | ||
125 | + } | ||
126 | + return list; | ||
127 | + } | ||
128 | +} | ||
129 | + |
1 | +package com.tianbo.warehouse.util.redis; | ||
2 | + | ||
3 | +import com.alibaba.druid.util.StringUtils; | ||
4 | +import lombok.extern.slf4j.Slf4j; | ||
5 | + | ||
6 | +import org.aspectj.lang.ProceedingJoinPoint; | ||
7 | +import org.aspectj.lang.Signature; | ||
8 | +import org.aspectj.lang.reflect.MethodSignature; | ||
9 | +import org.springframework.expression.EvaluationContext; | ||
10 | +import org.springframework.expression.spel.standard.SpelExpressionParser; | ||
11 | +import org.springframework.expression.spel.support.StandardEvaluationContext; | ||
12 | +import org.springframework.stereotype.Component; | ||
13 | + | ||
14 | +import java.util.Arrays; | ||
15 | + | ||
16 | +@Component | ||
17 | +@Slf4j | ||
18 | +public class DefaultKeyGenerator { | ||
19 | + /** | ||
20 | + * @Description: redis key生成 | ||
21 | + * @param: cacheKey:key值必传 ,cacheNames:缓存名称,不传取方法路径 | ||
22 | + * @return: | ||
23 | + * @throws: | ||
24 | + * @author:pengl | ||
25 | + * @Date:2017/11/13 10:58 | ||
26 | + */ | ||
27 | + public String[] generateKey(ProceedingJoinPoint pjp, String[] cacheNames, String cacheKey) throws NoSuchMethodException { | ||
28 | + | ||
29 | + if (StringUtils.isEmpty(cacheKey)) { | ||
30 | + throw new NullPointerException("CacheKey can not be null..."); | ||
31 | + } | ||
32 | + | ||
33 | + Signature signature = pjp.getSignature(); | ||
34 | + if(cacheNames == null || cacheNames.length == 0){ | ||
35 | + cacheNames = new String[]{signature.getDeclaringTypeName() + "." + signature.getName()}; | ||
36 | + } | ||
37 | + String[] results = new String[cacheNames.length]; | ||
38 | + //解析cacheKey | ||
39 | + EvaluationContext evaluationContext = new StandardEvaluationContext(); | ||
40 | + if (!(signature instanceof MethodSignature)) { | ||
41 | + throw new IllegalArgumentException("This annotation can only be used for methods..."); | ||
42 | + } | ||
43 | + MethodSignature methodSignature = (MethodSignature) signature; | ||
44 | + //method参数列表 | ||
45 | + String[] parameterNames = methodSignature.getParameterNames(); | ||
46 | + Object[] args = pjp.getArgs(); | ||
47 | + for(int i = 0; i < parameterNames.length; i++){ | ||
48 | + String parameterName = parameterNames[i]; | ||
49 | + evaluationContext.setVariable(parameterName, args[i]); | ||
50 | + } | ||
51 | + 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类型 | ||
54 | + } | ||
55 | + log.info("=============>>>generateKeys : " + Arrays.toString(results)); | ||
56 | + return results; | ||
57 | + } | ||
58 | +} |
-
请 注册 或 登录 后发表评论