Spring Boot提供了非常方便完善的缓存抽象框架,使其可以很方便的集成多种缓存组件,如EhCache、Redis、Guava等。关于Spring Boot与这些组件的集成以及其申明式使用,网上也有很多很好的指导教程,这里不再复述,因为本人曾经开发的产品中,经常有需要为不同缓存设置不同缓存过期时间的使用场景,比如需要频繁更新的业务数据,我们可以设置较短的缓存过期时间,类似配置类的数据,不会经常改动,我们就设置一个比较长的缓存过期时间,这样既不影响业务,又不用实现复杂的缓存刷新逻辑,本文主要讲解下为不同的缓存设置不同的到期时间,本文中涉及的Spring Boot版本为3.2.3。

自定义TtlRedisCacheManager

使用TtlRedisCacheManager覆盖系统默认的RedisCacheManager,TtlRedisCacheManager继承于RedisCacheManager,这里主要覆盖父类中的createRedisCache方法,具体请参考代码注释:

import java.time.Duration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCache;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.util.StringUtils;

import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Configuration
public class TTLRedisCacheConfiguration {

    @Primary
    @Bean
    public RedisCacheManager ttlCacheManager(RedisConnectionFactory redisConnectionFactory) {
        return new TtlRedisCacheManager(RedisCacheWriter.lockingRedisCacheWriter(redisConnectionFactory),
            RedisCacheConfiguration.defaultCacheConfig());
    }

    static class TtlRedisCacheManager extends RedisCacheManager {

        // 用于获取yml配置文件中缓存自定义时间的配置
        @Resource
        private CacheTTLConfig cacheTTLConfig;

        public TtlRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
            super(cacheWriter, defaultCacheConfiguration);
        }

        @Override
        protected RedisCache createRedisCache(String name, RedisCacheConfiguration cacheConfig) {

            // 根据不同的缓存名称获取不同的缓存过期时间配置
            long ttl = getCacheTtl(name);

            // 如果存在合法的配置,则使用配置设置缓存的过期时间
            if (ttl > 0) {
                // 根据传参设置缓存过期时间
                cacheConfig = cacheConfig.entryTtl(Duration.ofSeconds(ttl));
            }

            return super.createRedisCache(name, cacheConfig);
        }

        private long getCacheTtl(String cacheName) {
            String ttl = cacheTTLConfig.getCacheTTL(cacheName);
            if (StringUtils.hasLength(ttl)) {
                try {
                    return Long.parseLong(ttl);
                } catch (NumberFormatException e) {
                    log.warn(cacheName + " cache ttl config is invalid, will use system default value.", e);;
                }
            }
            return -1L;
        }
    }

}
配置缓存过期时间

我们使用键值对的方式配置不同的缓存过期时间,缓存自定义配置样例及配置类CacheTTLConfig的代码实现

application.yml配置:

spring: # 配置Spring属性
  data: # 配置Spring Redis属性 
    cachettls: # 配置不同缓存的过期时间,注意:如何管理缓存数据需要系统的设计方案,否则会导致缓存泛滥,反而影响业务。
      role1: 10
      role2: 60

CacheTTLConfig配置类:

import java.util.Map;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import lombok.Data;

@Data
@Component
@ConfigurationProperties(prefix = "spring.data") /** 获取yml前缀为spring.data的配置 */
public class CacheTTLConfig {

    /*
     * 名称和配置的名称保持一直
     */
    private Map<String, String> cachettls;

    public String getCacheTTL(String cacheName) {
        return cachettls.get(cacheName);
    }

}
缓存注解使用

在使用缓存的地方添加@Cacheable注解,和之前没有变化:

import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.feifanmaster.xtool.mapper.RoleMapper;
import com.feifanmaster.xtool.security.domain.model.Role;

import lombok.extern.slf4j.Slf4j;

/**
 * @author: Jackie
 * @date: 2024-02-11
 */
@Slf4j
@Service
public class RoleService extends ServiceImpl<RoleMapper, Role> {

    @Cacheable(value = "role2", key = "#name")
    public Page<Role> page(Long currentPage, Long pageSize, String name) {
        Page<Role> page = new Page<>(currentPage, pageSize);
        LambdaQueryWrapper<Role> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(StringUtils.hasText(name), Role::getName, name);

        log.info("Get role from db.");
        return super.page(page, queryWrapper);
    }
}

如此我们就实现了缓存过期时间的自定义,非侵入式的修改,对于存量代码我们也能很方便的改造。