行业知识
转载 spring @Cacheable扩展实现缓存自动过期时间以及自动刷新
转自:聊聊如何基于spring @Cacheable扩展实现缓存自动过期时间以及自动刷新-腾讯云开发者社区-腾讯云 (tencent.com)
前言
用过spring cache的朋友应该会知道,Spring Cache默认是不支持在@Cacheable上添加过期时间的,虽然可以通过配置缓存容器时统一指定。形如
@Beanpublic CacheManager cacheManager( @SuppressWarnings("rawtypes") RedisTemplate redisTemplate) { RedisCacheManager cacheManager= new RedisCacheManager(redisTemplate); cacheManager.setDefaultExpiration(60); Map expiresMap = new HashMap(); expiresMap.put("customUser",30L); cacheManager.setExpires(expiresMap); return cacheManager;} 复制
但有时候我们会更习惯通过注解指定过期时间。今天我们就来聊一下如何扩展@Cacheable实现缓存自动过期以及缓存即将到期自动刷新
2
实现注解缓存过期前置知识
SpringCache包含两个顶级接口,Cache和CacheManager,通过CacheManager可以去管理一堆Cache。因此我们要扩展@Cacheable,就脱离不了对Cache和CacheManager进行扩展
其次要实现过期时间,首先是引入的缓存产品,他本身就要支持过期时间,比如引入的缓存为ConcurrentHashMap,他原本就是不支持过期时间,如果要扩展,就要非常耗费精力实现
3
实现注解缓存过期
01
方法一:通过自定义cacheNames方式
形如下
@Cacheable(cacheNames = "customUser#30", key = "#id")
复制
通过#分隔,#后面部分代表过期时间(单位为秒)
实现逻辑步骤为:
1、自定义缓存管理器并继承RedisCacheManager,同时重写createRedisCache方法
示例:
public class CustomizedRedisCacheManager extends RedisCacheManager { public CustomizedRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) { super(cacheWriter, defaultCacheConfiguration); } @Override protected RedisCache createRedisCache(String name, RedisCacheConfiguration cacheConfig) { String[] array = StringUtils.delimitedListToStringArray(name, "#"); name = array[0]; if (array.length > 1) { long ttl = Long.parseLong(array[1]); cacheConfig = cacheConfig.entryTtl(Duration.ofSeconds(ttl)); } return super.createRedisCache(name, cacheConfig); }} 复制
2、将默认的缓存管理器改成我们自定义的缓存管理器
示例:
@EnableCaching @Configurationpublic class CacheConfig { @Bean public CacheManager cacheManager() { RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofDays(1)); CustomizedRedisCacheManager redisCacheManager = new CustomizedRedisCacheManager(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory()), defaultCacheConfig); return redisCacheManager; }} 复制
通过如上2个步骤,即可实现缓存过期
02
方法二:通过自定义派生@Cacheable注解
第一种方法的实现是简单,但缺点是语义不直观,因此得做好宣导以及wiki,不然对于新人来说,他可能都不知道cacheName用#分割是代表啥意思
方法二的实现逻辑步骤如下
1、自定义注解LybGeekCacheable
@Target({ElementType.TYPE, ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Inherited@Documented@Cacheable(cacheManager = CacheConstant.CUSTOM_CACHE_MANAGER,keyGenerator = CacheConstant.CUSTOM_CACHE_KEY_GENERATOR)public @interface LybGeekCacheable { @AliasFor(annotation = Cacheable.class,attribute = "value") String[] value() default {}; @AliasFor(annotation = Cacheable.class,attribute = "cacheNames") String[] cacheNames() default {}; @AliasFor(annotation = Cacheable.class,attribute = "key") String key() default ""; @AliasFor(annotation = Cacheable.class,attribute = "keyGenerator") String keyGenerator() default ""; @AliasFor(annotation = Cacheable.class,attribute = "cacheResolver") String cacheResolver() default ""; @AliasFor(annotation = Cacheable.class,attribute = "condition") String condition() default ""; @AliasFor(annotation = Cacheable.class,attribute = "unless") String unless() default ""; @AliasFor(annotation = Cacheable.class,attribute = "sync") boolean sync() default false; long expiredTimeSecond() default 0; long preLoadTimeSecond() default 0;} 复制
大部分注解和@Cacheable保持一致,新增expiredTimeSecond缓存过期时间以及缓存自动刷新时间preLoadTimeSecond
2、自定义缓存管理器并继承RedisCacheManager并重写loadCaches和createRedisCache
public class CustomizedRedisCacheManager extends RedisCacheManager implements BeanFactoryAware { private Map initialCacheConfigurations; private RedisTemplate cacheRedisTemplate; private RedisCacheWriter cacheWriter; private DefaultListableBeanFactory beanFactory; private RedisCacheConfiguration defaultCacheConfiguration; protected CachedInvocation cachedInvocation; public CustomizedRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, Map initialCacheConfigurations,RedisTemplate cacheRedisTemplate) { super(cacheWriter, defaultCacheConfiguration, initialCacheConfigurations); this.initialCacheConfigurations = initialCacheConfigurations; this.cacheRedisTemplate = cacheRedisTemplate; this.cacheWriter = cacheWriter; this.defaultCacheConfiguration = defaultCacheConfiguration; //采用spring事件驱动亦可 //EventBusHelper.register(this); } public Map getInitialCacheConfigurations() { return initialCacheConfigurations; } @Override protected Collection loadCaches() { List caches = new LinkedList(); for (Map.Entry entry : getInitialCacheConfigurations().entrySet()) { caches.add(createRedisCache(entry.getKey(), entry.getValue())); } return caches; } @Override public RedisCache createRedisCache(String name, @Nullable RedisCacheConfiguration cacheConfig) { CustomizedRedisCache customizedRedisCache = new CustomizedRedisCache(name, cacheWriter, cacheConfig != null ? cacheConfig : defaultCacheConfiguration); return customizedRedisCache; }} 复制
3、在spring bean初始化完成后,设置缓存过期时间,并重新初始化缓存。
Component@Slf4jpublic class CacheExpireTimeInit implements SmartInitializingSingleton, BeanFactoryAware { private DefaultListableBeanFactory beanFactory; @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = (DefaultListableBeanFactory)beanFactory; } @Override public void afterSingletonsInstantiated() { Map beansWithAnnotation = beanFactory.getBeansWithAnnotation(Component.class); if(MapUtil.isNotEmpty(beansWithAnnotation)){ for (Object cacheValue : beansWithAnnotation.values()) { ReflectionUtils.doWithMethods(cacheValue.getClass(), method -> { ReflectionUtils.makeAccessible(method); boolean cacheAnnotationPresent = method.isAnnotationPresent(LybGeekCacheable.class); if(cacheAnnotationPresent){ LybGeekCacheable lybGeekCacheable = method.getAnnotation(LybGeekCacheable.class); CacheHelper.initExpireTime(lybGeekCacheable); } }); } CacheHelper.initializeCaches(); } } 复制
注: 为啥要重新初始化缓存,主要是为了一开始默认的是没设置缓存过期,重新初始化是为了设置过期时间。为啥调用initializeCaches()这个方法,看下官方描述就知道了
/** * Initialize the static configuration of caches. *Triggered on startup through {@link #afterPropertiesSet()}; * can also be called to re-initialize at runtime. * @since 4.2.2 * @see #loadCaches() */ public void initializeCaches() { Collection
上一篇:铲屎官服务器怎么进?
下一篇:dedecms插件怎么使用?