關(guān)于spring的自定義緩存注解分析
為什么要自定義緩存注解?
Spring Cache本身提供@Cacheable、@CacheEvict、@CachePut等緩存注解,為什么還要自定義緩存注解呢?
@Cacheabe不能設(shè)置緩存時(shí)間,導(dǎo)致生成的緩存始終在redis中,當(dāng)然這一點(diǎn)可以通過(guò)修改RedisCacheManager的配置來(lái)設(shè)置緩存時(shí)間:
@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)); // 設(shè)置全局key的有效事件為7天
return redisCacheConfiguration;
}不過(guò)這個(gè)設(shè)置時(shí)全局的,所有的key的失效時(shí)間都一樣,要想實(shí)現(xiàn)不同的key不同的失效時(shí)間,還得自定義緩存注解。
自定義緩存注解
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注解的切面實(shí)現(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 // 開(kāi)啟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注解的切面實(shí)現(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注解的切面實(shí)現(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屬性中的表達(dá)式。
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的通知功能,所以需要開(kāi)啟AOP,需要在配置類上加上@EnableAspectJAutoProxy注解。
- 改進(jìn):可使用類似@EnableCache注解導(dǎo)入一個(gè)入口類,不再需要多個(gè)切面,多個(gè)注解的處理邏輯放在一起,參考Spring Cache。
- 擴(kuò)展:解析表達(dá)式可使用緩存,加快解析速度;方法上面支持多個(gè)@Cached注解。
到此這篇關(guān)于關(guān)于spring的自定義緩存注解分析的文章就介紹到這了,更多相關(guān)spring自定義緩存注解內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決idea每次新建項(xiàng)目都需要重新指定maven目錄
這篇文章主要介紹了解決idea每次新建項(xiàng)目都需要配置maven,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
Spring MVC前后端的數(shù)據(jù)傳輸?shù)膶?shí)現(xiàn)方法
這篇文章主要介紹了Spring MVC前后端的數(shù)據(jù)傳輸?shù)膶?shí)現(xiàn)方法,需要的朋友可以參考下2017-10-10
java泛型的局限探究及知識(shí)點(diǎn)總結(jié)
在本篇內(nèi)容里小編給大家分享的是一篇關(guān)于java泛型的局限探究及知識(shí)點(diǎn)總結(jié)內(nèi)容,有需要的朋友們可以跟著學(xué)習(xí)參考下。2021-07-07
Spring+Quartz實(shí)現(xiàn)動(dòng)態(tài)任務(wù)調(diào)度詳解
這篇文章主要介紹了Spring+Quartz實(shí)現(xiàn)動(dòng)態(tài)任務(wù)調(diào)度詳解,最近經(jīng)常基于spring?boot寫(xiě)定時(shí)任務(wù),并且是使用注解的方式進(jìn)行實(shí)現(xiàn),分成的方便將自己的類注入spring容器,需要的朋友可以參考下2024-01-01
springboot(thymeleaf)中th:field和th:value的區(qū)別及說(shuō)明
這篇文章主要介紹了springboot(thymeleaf)中th:field和th:value的區(qū)別及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10
Spring Boot 如何自定義返回錯(cuò)誤碼錯(cuò)誤信息
這篇文章主要介紹了Spring Boot 如何自定義返回錯(cuò)誤碼錯(cuò)誤信息的相關(guān)知識(shí),非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-08-08
Java利用Poi讀取excel并對(duì)所有類型進(jìn)行處理
這篇文章主要為大家詳細(xì)介紹了Java利用Poi讀取excel并對(duì)所有類型進(jìn)行處理的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2024-01-01
springboot中請(qǐng)求路徑配置在配置文件中詳解
這篇文章主要介紹了springboot中請(qǐng)求路徑配置在配置文件中,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01
SpringBoot整合Redisson實(shí)現(xiàn)分布式鎖
本文主要介紹了SpringBoot整合Redisson實(shí)現(xiàn)分布式鎖,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11

