自定义注解设置缓存有效期的正确姿势
来源:互联网 发布:趋势科技杀毒软件 知乎 编辑:程序博客网 时间:2024/05/21 04:21
引言
这里介绍RedisCacheManager中一个重要的方法,void setExpires(Map<String, Long> expires),该方法的传入参数是一个Map,Map的key值是@Cacheable(或@CacheEvict或@CachePut)注解的value值,Map的value值是缓存的有效期(单位秒),用于批量设置缓存的有效期。
新写了一个SpringRedisCacheManager,继承自RedisCacheManager,用于对@CacheDuration解析及有效期的设置,代码如下。
结果分析
UserService类上标注的CacheDuration设置有效期是6秒,而方法queryFullNameById上CacheDuration设置的有效期是16秒,最后生效的是16秒。
redis缓存的有效期可以通过xml配置文件设置(默认有效期),也可以通过编码的方式手动去设置,但是这两种方式都存在缺陷。xml方式设置的是全局的默认有效期,虽然灵活,但不能给某个缓存设置单独的有效期;硬编码方式虽然可以给不同的缓存设置单独的有效期,但是管理上不够灵活。Spring提供的Cache相关注解中并没有提供有效期的配置参数,so,自定义注解实现缓存有效期的灵活设置诞生了,具体源码前往github下载。
redis-queue.xml配置文件:
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd "><!-- 配置一个高可用环境的哨兵模式的redis环境 --> <bean id="redisSentinelConfiguration" class="org.springframework.data.redis.connection.RedisSentinelConfiguration"> <property name="master"> <bean class="org.springframework.data.redis.connection.RedisNode"> <property name="name" value="mymaster"/> </bean> </property> <property name="sentinels"> <set> <bean class="org.springframework.data.redis.connection.RedisNode"> <constructor-arg name="host" value="${redis1.ip}"/> <constructor-arg name="port" value="${redis1.port}"/> </bean> <bean class="org.springframework.data.redis.connection.RedisNode"> <constructor-arg name="host" value="${redis2.ip}"/> <constructor-arg name="port" value="${redis2.port}"/> </bean> <bean class="org.springframework.data.redis.connection.RedisNode"> <constructor-arg name="host" value="${redis3.ip}"/> <constructor-arg name="port" value="${redis3.port}"/> </bean> </set> </property> </bean> <!-- redis 连接池相关配置--><bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig" ><property name="maxTotal" value="${redis.maxTotal}"/> <property name="maxIdle" value="${redis.maxIdle}"/> <property name="maxWaitMillis" value="${redis.maxWaitMillis}" /> <property name="minIdle" value="${redis.minIdle}"/><property name="testOnBorrow" value="${redis.testOnBorrow}" /></bean><!-- JedisConnectionFactory 相关配置--><bean id="jedisFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"><property name="poolConfig" ><ref bean="jedisPoolConfig"/></property><constructor-arg name="sentinelConfig" ref="redisSentinelConfiguration"/> <property name="timeout" value="${redis.timeout}"/></bean><bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"><property name="connectionFactory"><ref bean="jedisFactory"/></property></bean><!-- 使用自定义的SpringRedisCacheManager类 --><bean id="redisCacheManager" class="tf56.skynet.annotation.SpringRedisCacheManager"><!--为默认的构造方法设置值--><constructor-arg name="redisTemplate" ref="redisTemplate"/><!--设置redi缓存默认的过期时间--><property name="defaultExpiration" value="${redis.expiration}"/></bean> </beans>
这里介绍RedisCacheManager中一个重要的方法,void setExpires(Map<String, Long> expires),该方法的传入参数是一个Map,Map的key值是@Cacheable(或@CacheEvict或@CachePut)注解的value值,Map的value值是缓存的有效期(单位秒),用于批量设置缓存的有效期。
自定义注解
直接贴代码了,如下。
@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD, ElementType.TYPE})public @interface CacheDuration { //Sets the expire time (in seconds). public long duration() default 60;}使用@CacheDuration
@Service("userService")@CacheDuration(duration = 6)public class UserService { @Cacheable(value = "User", key = "'UserId_' + #id", condition = "#id<=110") @CacheDuration(duration = 16) public String queryFullNameById(long id) { System.out.println("execute queryFullNameById method"); return "ZhangSanFeng"; }}新RedisCacheManager
新写了一个SpringRedisCacheManager,继承自RedisCacheManager,用于对@CacheDuration解析及有效期的设置,代码如下。
package tf56.skynet.annotation;import org.springframework.beans.BeansException;import org.springframework.beans.factory.InitializingBean;import org.springframework.cache.annotation.CacheConfig;import org.springframework.cache.annotation.Cacheable;import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationContextAware;import org.springframework.data.redis.cache.RedisCacheManager;import org.springframework.data.redis.core.RedisOperations;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.stereotype.Service;import org.springframework.util.ReflectionUtils;import java.lang.reflect.Method;import java.util.HashMap;import java.util.Map;import java.util.Set;import static com.google.common.collect.Sets.newHashSet;import static org.springframework.core.annotation.AnnotationUtils.findAnnotation;import static org.springframework.util.StringUtils.isEmpty;/** * Created by Administrator on 2017/10/18. * 新写了一个SpringRedisCacheManager,继承自RedisCacheManager, * 用于对@CacheDuration解析及有效期的设置 */public class SpringRedisCacheManager extends RedisCacheManager implements ApplicationContextAware, InitializingBean { private ApplicationContext applicationContext; public SpringRedisCacheManager(RedisTemplate redisTemplate) { super(redisTemplate); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } @Override public void afterPropertiesSet() { parseCacheDuration(applicationContext); } private void parseCacheDuration(ApplicationContext applicationContext) { final Map<String, Long> cacheExpires = new HashMap<>(); String[] beanNames = applicationContext.getBeanNamesForType(Object.class); for (String beanName : beanNames) { final Class clazz = applicationContext.getType(beanName); Service service = findAnnotation(clazz, Service.class); if (null == service) { continue; } addCacheExpires(clazz, cacheExpires); } //设置有效期 super.setExpires(cacheExpires); } private void addCacheExpires(final Class clazz, final Map<String, Long> cacheExpires) { ReflectionUtils.doWithMethods(clazz, new ReflectionUtils.MethodCallback() { @Override public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException { ReflectionUtils.makeAccessible(method); CacheDuration cacheDuration = findCacheDuration(clazz, method); Cacheable cacheable = findAnnotation(method, Cacheable.class); CacheConfig cacheConfig = findAnnotation(clazz, CacheConfig.class); Set<String> cacheNames = findCacheNames(cacheConfig, cacheable); for (String cacheName : cacheNames) { cacheExpires.put(cacheName, cacheDuration.duration()); } } }, new ReflectionUtils.MethodFilter() { @Override public boolean matches(Method method) { return null != findAnnotation(method, Cacheable.class); } }); } /** * CacheDuration标注的有效期,优先使用方法上标注的有效期 * @param clazz * @param method * @return */ private CacheDuration findCacheDuration(Class clazz, Method method) { CacheDuration methodCacheDuration = findAnnotation(method, CacheDuration.class); if (null != methodCacheDuration) { return methodCacheDuration; } CacheDuration classCacheDuration = findAnnotation(clazz, CacheDuration.class); if (null != classCacheDuration) { return classCacheDuration; } throw new IllegalStateException("No CacheDuration config on Class " + clazz.getName() + " and method " + method.toString()); } private Set<String> findCacheNames(CacheConfig cacheConfig, Cacheable cacheable) { return isEmpty(cacheable.value()) ? newHashSet(cacheConfig.cacheNames()) : newHashSet(cacheable.value()); }}redis.properties配置如下:
redis.maxTotal=20redis.maxIdle=10redis.maxWaitMillis=3000redis.minIdle=1redis.testOnBorrow=trueredis.timeout=10000redis1.port=26379redis1.ip=10.7.13.152redis2.port=26379redis2.ip=10.7.13.160redis3.port=26379redis3.ip=10.7.13.209redis.expiration=3000最后将redis.properties 、redis-queue.xml 配置文件配置加载到项目主配置文件xxx-servlet.xml中 部分配置如下所示,对应着配置即可:
<?xml version="1.0" encoding="UTF-8"?><!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure --><beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:cache="http://www.springframework.org/schema/cache" xmlns:util="http://www.springframework.org/schema/util" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd"> <bean id="handlerMapping" class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"> <property name="order" value="1"/> </bean> <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/> <!-- 解决@responseBody返回乱码问题 --> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"> <property name="messageConverters"> <list> <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <property name="supportedMediaTypes"> <list> <value>text/plain;charset=UTF-8</value> </list> </property> </bean> <!-- <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"> <property name="supportedMediaTypes" value="application/json" /> </bean> --> <bean class="org.springframework.http.converter.ResourceHttpMessageConverter"/> <bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"/> <bean class="org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter"/> <bean class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter"/> </list> </property> <property name="webBindingInitializer"> <bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer"> <property name="validator" ref="validator"/> <!-- <property name="conversionService"> <bean class="org.springframework.format.support.FormattingConversionServiceFactoryBean"></bean> </property> --> </bean> </property> </bean> <mvc:default-servlet-handler/> <!-- 指定redisCacheManager--> <cache:annotation-driven cache-manager="redisCacheManager"/> <bean id="configBean" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>classpath:config/redis.properties</value> </list> </property> </bean> <context:component-scan base-package="tf56.skynet"/> <import resource="classpath:/config/redis-queue.xml"/> <context:annotation-config/> <mvc:annotation-driven/></beans>
编写service实现类:
@Service("userService")@CacheDuration(duration = 6)public class UserService { @Cacheable(value = "User", key = "'UserId_' + #id", condition = "#id<=110") @CacheDuration(duration = 16) public String queryFullNameById(long id) { System.out.println("execute queryFullNameById method"); return "ZhangSanFeng"; }}测试代码:
@Test public void testRedisCacheManager() { ApplicationContext context = new ClassPathXmlApplicationContext("redisCacheContext.xml"); UserService userService = (UserService) context.getBean("userService"); RedisTemplate redisTemplate = (RedisTemplate) context.getBean("redisTemplate"); System.out.println("第一次执行查询:" + userService.queryFullNameById(100L)); System.out.println("----------------------------------"); System.out.println("第二次执行查询:" + userService.queryFullNameById(100L)); System.out.println("----------------------------------"); System.out.println("UserId_100有效期(单位秒):" + redisTemplate.getExpire("UserId_100", TimeUnit.SECONDS)); System.out.println("----------------------------------"); try { Thread.sleep(3000); System.out.println("主线程休眠3秒后"); System.out.println("----------------------------------"); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("UserId_100有效期(单位秒):" + redisTemplate.getExpire("UserId_100", TimeUnit.SECONDS)); System.out.println("----------------------------------"); System.out.println("第三次执行查询:" + userService.queryFullNameById(100l)); }测试结果:
execute queryFullNameById method第一次执行查询:ZhangSanFeng----------------------------------第二次执行查询:ZhangSanFeng----------------------------------UserId_100有效期(单位秒):15----------------------------------主线程休眠3秒后----------------------------------UserId_100有效期(单位秒):12----------------------------------第三次执行查询:ZhangSanFeng
结果分析
UserService类上标注的CacheDuration设置有效期是6秒,而方法queryFullNameById上CacheDuration设置的有效期是16秒,最后生效的是16秒。
参考原文:
http://www.jianshu.com/p/2633fb37862c
阅读全文
0 0
- 自定义注解设置缓存有效期的正确姿势
- 缓存更新的正确姿势
- 使用自定义控件的正确姿势
- CATextLayer设置字体的正确姿势
- iOS10跳转系统设置的正确姿势
- Android 正确的启动页设置姿势
- android设置启动页的正确姿势
- iOS10跳转系统设置的正确姿势
- HttpClient正确设置Host的姿势(不需要自定义DNS解析类)
- Session的有效期设置
- 编写自定义控件构造函数的正确姿势 - defStyleAttr/defStyleRes
- 自定义view的正确姿势-教你一步步画出笑脸
- ReactNative自定义控件状态更新的正确姿势
- 搜索的正确姿势
- 读开源库的正确姿势
- 跑步的正确姿势
- 跑步的正确姿势
- 正确的关机姿势
- Redis官方文档(入门必备)
- Matlab GUI,颜色对话框,设置按钮背景颜色
- Hadoop HDFS本地存储目录结构解析
- C++实现一个String类
- Java基础之IO流
- 自定义注解设置缓存有效期的正确姿势
- jvm参数调优
- 关于使用chrome扩展插件
- IFE-TASK17(任务十七:零基础JavaScript编码(五))
- Matlab GUI,进度条(waitbar)
- 设计模式(12)--外观模式
- connector.ClientAbortException: java.io.IOException: 您的主机中的软件中止了一个已建立的连接
- py 查看一个文件夹下的文件数量
- Java细节与规范:ArrayList为何建议赋予默认值及其扩容机制