關(guān)于spring的自定義緩存注解分析
為什么要自定義緩存注解?
Spring Cache本身提供@Cacheable、@CacheEvict、@CachePut等緩存注解,為什么還要自定義緩存注解呢?
@Cacheabe不能設置緩存時間,導致生成的緩存始終在redis中,當然這一點可以通過修改RedisCacheManager的配置來設置緩存時間:
@Bean public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) { RedisCacheConfiguration redisCacheConfiguration = getRedisCacheConfiguration(); RedisCacheManager cacheManager = RedisCacheManager .builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory)) .cacheDefaults(redisCacheConfiguration) .build(); return cacheManager; } private RedisCacheConfiguration getRedisCacheConfiguration() { Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY); jackson2JsonRedisSerializer.setObjectMapper(om); RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig(); redisCacheConfiguration = redisCacheConfiguration.serializeValuesWith( RedisSerializationContext .SerializationPair .fromSerializer(jackson2JsonRedisSerializer) ).entryTtl(Duration.ofDays(7)); // 設置全局key的有效事件為7天 return redisCacheConfiguration; }
不過這個設置時全局的,所有的key的失效時間都一樣,要想實現(xiàn)不同的key不同的失效時間,還得自定義緩存注解。
自定義緩存注解
Cached
類似Spring Cache的@Cacheable。
package com.morris.spring.custom.cache.annotation; import org.springframework.core.annotation.AliasFor; import java.lang.annotation.*; import java.util.concurrent.TimeUnit; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface Cached { @AliasFor("cacheName") String value() default ""; @AliasFor("value") String cacheName() default ""; int expire() default 0; TimeUnit expireUnit() default TimeUnit.SECONDS; String key(); String condition() default ""; String unless() default ""; boolean sync() default false; }
CacheUpdate
類似于Spring Cache的@CachePut。
package com.morris.spring.custom.cache.annotation; import org.springframework.core.annotation.AliasFor; import java.lang.annotation.*; import java.util.concurrent.TimeUnit; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface CacheUpdate { @AliasFor("cacheName") String value() default ""; @AliasFor("value") String cacheName() default ""; int expire() default 0; TimeUnit expireUnit() default TimeUnit.SECONDS; String key(); String condition() default ""; String unless() default ""; }
CacheInvalidate
類似于Spring Cache的@CacheEvict。
package com.morris.spring.custom.cache.annotation; import org.springframework.core.annotation.AliasFor; import java.lang.annotation.*; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface CacheInvalidate { @AliasFor("cacheName") String value() default ""; @AliasFor("value") String cacheName() default ""; String key(); String condition() default ""; String unless() default ""; }
CachedAspect
@Cached注解的切面實現(xiàn)。
package com.morris.spring.custom.cache.annotation; import com.morris.spring.custom.cache.Level2Cache; import com.morris.spring.custom.cache.Level2CacheManage; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.cache.Cache; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.expression.EvaluationContext; import javax.annotation.Resource; import java.util.Objects; @Configuration @Aspect @EnableAspectJAutoProxy // 開啟AOP public class CachedAspect { @Resource private Level2CacheManage cacheManager; @Around(value = "@annotation(cached)") public Object around(ProceedingJoinPoint joinPoint, Cached cached) throws Throwable { Level2Cache cache = cacheManager.getCache(cached.cacheName()); ExpressionEvaluator evaluator = new ExpressionEvaluator(); EvaluationContext context = evaluator.createEvaluationContext(joinPoint); Object key = evaluator.key(cached.key(), context); if(cached.sync()) { // return cache.get(key, () -> { try { return joinPoint.proceed(); } catch (Throwable e) { e.printStackTrace(); return null; } }); } // 先查緩存 Cache.ValueWrapper valueWrapper = cache.get(key); if (Objects.nonNull(valueWrapper)) { return valueWrapper.get(); } Object result = joinPoint.proceed(); context.setVariable("result", result); Boolean condition = evaluator.condition(cached.condition(), context); Boolean unless = evaluator.unless(cached.unless(), context); if (condition && !unless) { if (cached.expire() > 0) { cache.put(key, result, cached.expire(), cached.expireUnit()); } else { cache.put(key, result); } } return result; } }
CacheUpdateAspect
@CacheUpdate注解的切面實現(xiàn)類。
package com.morris.spring.custom.cache.annotation; import com.morris.spring.custom.cache.Level2Cache; import com.morris.spring.custom.cache.Level2CacheManage; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.context.annotation.Configuration; import org.springframework.expression.EvaluationContext; import javax.annotation.Resource; @Configuration @Aspect public class CacheUpdateAspect { @Resource private Level2CacheManage cacheManager; @Around(value = "@annotation(cacheUpdate)") public Object around(ProceedingJoinPoint joinPoint, CacheUpdate cacheUpdate) throws Throwable { Level2Cache cache = cacheManager.getCache(cacheUpdate.cacheName()); ExpressionEvaluator evaluator = new ExpressionEvaluator(); EvaluationContext context = evaluator.createEvaluationContext(joinPoint); Object key = evaluator.key(cacheUpdate.key(), context); Object result = joinPoint.proceed(); context.setVariable("result", result); Boolean condition = evaluator.condition(cacheUpdate.condition(), context); Boolean unless = evaluator.unless(cacheUpdate.unless(), context); if (condition && !unless) { if (cacheUpdate.expire() > 0) { cache.put(key, result, cacheUpdate.expire(), cacheUpdate.expireUnit()); } else { cache.put(key, result); } } return result; } }
CacheInvalidateAspect
@CacheInvalidate注解的切面實現(xiàn)類。
package com.morris.spring.custom.cache.annotation; import com.morris.spring.custom.cache.Level2Cache; import com.morris.spring.custom.cache.Level2CacheManage; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.context.annotation.Configuration; import org.springframework.expression.EvaluationContext; import javax.annotation.Resource; @Configuration @Aspect public class CacheInvalidateAspect { @Resource private Level2CacheManage cacheManager; @Around(value = "@annotation(cacheInvalidate)") public Object around(ProceedingJoinPoint joinPoint, CacheInvalidate cacheInvalidate) throws Throwable { Level2Cache cache = cacheManager.getCache(cacheInvalidate.cacheName()); ExpressionEvaluator evaluator = new ExpressionEvaluator(); EvaluationContext context = evaluator.createEvaluationContext(joinPoint); Object key = evaluator.key(cacheInvalidate.key(), context); Object result = joinPoint.proceed(); context.setVariable("result", result); Boolean condition = evaluator.condition(cacheInvalidate.condition(), context); Boolean unless = evaluator.unless(cacheInvalidate.unless(), context); if (condition && !unless) { cache.evict(key); } return result; } }
ExpressionEvaluator
ExpressionEvaluator主要用于解析注解中key、condition、unless屬性中的表達式。
package com.morris.spring.custom.cache.annotation; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.aop.support.AopUtils; import org.springframework.context.expression.MethodBasedEvaluationContext; import org.springframework.core.DefaultParameterNameDiscoverer; import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.util.StringUtils; import java.lang.reflect.Method; public class ExpressionEvaluator { private final SpelExpressionParser parser = new SpelExpressionParser(); public EvaluationContext createEvaluationContext(ProceedingJoinPoint joinPoint) { MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); Method specificMethod = AopUtils.getMostSpecificMethod(methodSignature.getMethod(), joinPoint.getTarget().getClass()); MethodBasedEvaluationContext context = new MethodBasedEvaluationContext(joinPoint.getTarget(), specificMethod, joinPoint.getArgs(), new DefaultParameterNameDiscoverer()); return context; } public Object key(String keyExpression, EvaluationContext evalContext) { Expression expression = parser.parseExpression(keyExpression); return expression.getValue(evalContext); } public boolean condition(String conditionExpression, EvaluationContext evalContext) { if(!StringUtils.hasText(conditionExpression)) { return true; } Expression expression = parser.parseExpression(conditionExpression); return (Boolean.TRUE.equals(expression.getValue(evalContext, Boolean.class))); } public boolean unless(String unlessExpression, EvaluationContext evalContext) { if(StringUtils.hasText(unlessExpression)) { return false; } Expression expression = parser.parseExpression(unlessExpression); return (Boolean.TRUE.equals(expression.getValue(evalContext, Boolean.class))); } }
總結(jié)
- 自定義緩存注解使用了AOP的通知功能,所以需要開啟AOP,需要在配置類上加上@EnableAspectJAutoProxy注解。
- 改進:可使用類似@EnableCache注解導入一個入口類,不再需要多個切面,多個注解的處理邏輯放在一起,參考Spring Cache。
- 擴展:解析表達式可使用緩存,加快解析速度;方法上面支持多個@Cached注解。
到此這篇關(guān)于關(guān)于spring的自定義緩存注解分析的文章就介紹到這了,更多相關(guān)spring自定義緩存注解內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring MVC前后端的數(shù)據(jù)傳輸?shù)膶崿F(xiàn)方法
這篇文章主要介紹了Spring MVC前后端的數(shù)據(jù)傳輸?shù)膶崿F(xiàn)方法,需要的朋友可以參考下2017-10-10Spring+Quartz實現(xiàn)動態(tài)任務調(diào)度詳解
這篇文章主要介紹了Spring+Quartz實現(xiàn)動態(tài)任務調(diào)度詳解,最近經(jīng)?;趕pring?boot寫定時任務,并且是使用注解的方式進行實現(xiàn),分成的方便將自己的類注入spring容器,需要的朋友可以參考下2024-01-01springboot(thymeleaf)中th:field和th:value的區(qū)別及說明
這篇文章主要介紹了springboot(thymeleaf)中th:field和th:value的區(qū)別及說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-10-10SpringBoot整合Redisson實現(xiàn)分布式鎖
本文主要介紹了SpringBoot整合Redisson實現(xiàn)分布式鎖,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-11-11