java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > spring自定义缓存注解

关于spring的自定义缓存注解分析

作者:morris131

这篇文章主要介绍了关于spring的自定义缓存注解分析,因为所有的key的失效时间都一样,要想实现不同的key不同的失效时间,就得需要自定义缓存注解,需要的朋友可以参考下

为什么要自定义缓存注解?

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的失效时间都一样,要想实现不同的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注解的切面实现。

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注解的切面实现类。

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注解的切面实现类。

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)));
	}
}

总结

到此这篇关于关于spring的自定义缓存注解分析的文章就介绍到这了,更多相关spring自定义缓存注解内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文