我的编程空间,编程开发者的网络收藏夹
学习永远不晚

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

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

关于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的失效时间都一样,要想实现不同的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)));
	}
}

总结

  • 自定义缓存注解使用了AOP的通知功能,所以需要开启AOP,需要在配置类上加上@EnableAspectJAutoProxy注解。
  • 改进:可使用类似@EnableCache注解导入一个入口类,不再需要多个切面,多个注解的处理逻辑放在一起,参考Spring Cache。
  • 扩展:解析表达式可使用缓存,加快解析速度;方法上面支持多个@Cached注解。

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

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

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

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

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

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

spring自定义校验注解ConstraintValidator的示例分析

这篇文章主要介绍spring自定义校验注解ConstraintValidator的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!一、前言系统执行业务逻辑之前,会对输入数据进行校验,检测数据是否有效合法的。所以
2023-06-20

SpringCache缓存自定义配置的示例分析

这篇文章将为大家详细讲解有关SpringCache缓存自定义配置的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。Cacheable指定自定义属性详情请参考spring官网添加链接描述1.key的名
2023-06-26

Java自定义注解实现Redis自动缓存的方法

在实际开发中,可能经常会有这样的需要:从MySQL中查询一条数据(比如用户信息),此时需要将用户信息保存至Redis。刚开始我们可能会在查询的业务逻辑之后再写一段Redis相关操作的代码,时间长了后发现这部分代码实际上仅仅做了Redis的写
2023-05-31

java自定义切面增强方式(关于自定义注解aop)

这篇文章主要介绍了java自定义切面增强方式(关于自定义注解aop),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
2023-05-14

spring缓存自定义resolver的方法怎么用

本篇内容介绍了“spring缓存自定义resolver的方法怎么用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、概述cache-asid
2023-06-29

SpringBoot自定义注解之脱敏注解的示例分析

这篇文章将为大家详细讲解有关SpringBoot自定义注解之脱敏注解的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。自定义注解之脱敏注解数据脱敏是指对某些敏感信息通过脱敏规则进行数据的变形,实现敏
2023-06-22

Spring基于注解的缓存声明深入探究

springboot对缓存支持非常灵活,我们可以使用默认的EhCache,也可以整合第三方的框架,只需配置即可,下面这篇文章主要给大家介绍了关于SpringBoot学习之基于注解缓存的相关资料,需要的朋友可以参考下
2022-11-13

浅析Java自定义注解的用法

注解为我们在代码中添加信息提供一种形式化的方法,使我们可以在源码、编译时、运行时非常方便的使用这些数据。本文主要为大家介绍了Java自定义注解的用法,希望对大家有所帮助
2023-03-21

Spring自定义XML schema 扩展的示例分析

小编给大家分享一下Spring自定义XML schema 扩展的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!Spring整合dubbo的事例
2023-06-15

基于spring-security出现401 403错误自定义处理的示例分析

这篇文章将为大家详细讲解有关基于spring-security出现401 403错误自定义处理的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。spring-security 401 403错误自定
2023-06-20

自定义注解和springAOP捕获Service层异常并处理自定义异常的示例分析

这篇文章主要为大家展示了“自定义注解和springAOP捕获Service层异常并处理自定义异常的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“自定义注解和springAOP捕获Serv
2023-06-15

怎么用Spring的spel获取自定义注解参数值

这篇文章主要介绍了怎么用Spring的spel获取自定义注解参数值的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇怎么用Spring的spel获取自定义注解参数值文章都会有所收获,下面我们一起来看看吧。spel获
2023-06-29

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录