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);
}
}
如此我们就实现了缓存过期时间的自定义,非侵入式的修改,对于存量代码我们也能很方便的改造。