小工具知识网

当前位置: 首页 > 行业知识 > 转载 spring @Cacheable扩展实现缓存自动过期时间以及自动刷新

行业知识

转载 spring @Cacheable扩展实现缓存自动过期时间以及自动刷新

2026-11-11 06:54:52 来源:互联网转载

转自:聊聊如何基于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插件怎么使用?