缓存与 Redis 自动配置
概述
Spring Boot 通过 CacheAutoConfiguration 实现缓存抽象层的自动配置,支持多种缓存提供者(Redis、Caffeine、Simple 等)的自动探测与切换。同时自动配置任务执行器 ThreadPoolTaskExecutor 和任务调度器 ThreadPoolTaskScheduler。
本文将拆解缓存与任务自动配置的 10 个关键细节,涵盖 CacheConfigurationImportSelector 探测机制、RedisCacheManager 构建流程、CaffeineCacheManager 创建、任务执行器配置等核心内容。
本文基于 Spring Boot 3.2.5 + Spring Data Redis 3.x 源码分析。
1. CacheAutoConfiguration 的 CacheConfigurationImportSelector
CacheAutoConfiguration 是缓存自动配置的入口,核心机制是通过 CacheConfigurationImportSelector 按顺序探测可用的缓存提供者:
@AutoConfiguration(after = {HibernateJpaAutoConfiguration.class, TransactionAutoConfiguration.class})
@ConditionalOnClass(CacheManager.class)
@ConditionalOnBean(CacheAspectSupport.class)
@EnableConfigurationProperties(CacheProperties.class)
public class CacheAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@Import(CacheConfigurationImportSelector.class) // 核心:导入选择器
static class CacheConfigurationInternal {}
}CacheConfigurationImportSelector 的实现:
class CacheConfigurationImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 返回一个 String[],每个元素是一个配置类的全限定名
// Spring Boot 会依次尝试创建这些配置类
return new String[] {
"org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration",
"org.springframework.boot.autoconfigure.cache.CaffeineCacheConfiguration",
"org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration"
// ... 更多配置类
};
}
}工作机制:
CacheAutoConfiguration 触发
↓
@Import(CacheConfigurationImportSelector.class)
↓
selectImports() 返回 String[] 配置类列表
↓
Spring 按数组顺序尝试匹配每个配置类的 @Conditional 条件
↓
第一个匹配成功的配置类生效(注册 CacheManager Bean)CacheConfigurations 工具类:
public final class CacheConfigurations {
// 维护所有缓存提供者与配置类的映射
private static final Map<CacheType, Class<?>> MAPPINGS;
static {
MAPPINGS = Collections.unmodifiableMap(
new LinkedHashMap<CacheType, Class<?>>() {{
put(CacheType.REDIS, RedisCacheConfiguration.class);
put(CacheType.CAFFEINE, CaffeineCacheConfiguration.class);
put(CacheType.SIMPLE, SimpleCacheConfiguration.class);
// ...
}}
);
}
public static String[] getConfigurationClassNames() {
List<String> names = new ArrayList<>();
for (Class<?> configurationClass : MAPPINGS.values()) {
names.add(configurationClass.getName());
}
return StringUtils.toStringArray(names);
}
}2. 缓存提供者的探测顺序
CacheConfigurationImportSelector 返回的配置类数组顺序决定了缓存提供者的优先级。Spring Boot 3.x 中默认的探测顺序为:
| 优先级 | 缓存提供者 | 配置类 | 条件 |
|---|---|---|---|
| 1(最高) | Redis | RedisCacheConfiguration | classpath 中存在 RedisOperations 且已配置 RedisConnectionFactory |
| 2 | Caffeine | CaffeineCacheConfiguration | classpath 中存在 Caffeine 库 |
| 3 | Simple | SimpleCacheConfiguration | 无条件(兜底实现) |
完整探测链(按 CacheConfigurations 定义顺序):
RedisCacheConfiguration
↓ 是否满足 @ConditionalClass(RedisOperations.class)
↓ && @ConditionalOnBean(RedisConnectionFactory.class)
├── 是 → 注册 RedisCacheManager
└── 否 → 继续探测
CaffeineCacheConfiguration
↓ 是否满足 @ConditionalOnClass(Caffeine.class)
├── 是 → 注册 CaffeineCacheManager
└── 否 → 继续探测
SimpleCacheConfiguration(无条件,始终可用)
↓
注册 ConcurrentMapCacheManager(兜底)CacheType 枚举:
public enum CacheType {
GENERIC, // 通过 ApplicationContext 获取
JCACHE, // JSR-107 JCache
HAZELCAST, // Hazelcast
INFINISPAN, // Infinispan
COUCHBASE, // Couchbase
REDIS, // Redis
CAFFEINE, // Caffeine
SIMPLE, // ConcurrentHashMap(兜底)
NONE // 不启用缓存
}3. RedisCacheManagerBuilder 的 4 个构建步骤
RedisCacheConfiguration 配置类通过 RedisCacheManagerBuilder 构建 RedisCacheManager:
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(CacheProperties.class)
public class RedisCacheConfiguration {
@Bean
RedisCacheManager cacheManager(CacheProperties cacheProperties,
CacheManagerCustomizers cacheManagerCustomizers,
ObjectProvider<RedisCacheManagerBuilderCustomizer> redisCacheManagerBuilderCustomizers,
RedisConnectionFactory redisConnectionFactory,
ResourceLoader resourceLoader) {
RedisCacheManagerBuilder builder = RedisCacheManagerBuilder
// 步骤 1:创建 RedisCacheWriter
.fromConnectionFactory(redisConnectionFactory)
// 步骤 2:设置默认缓存配置
.cacheDefaults(createConfiguration(cacheProperties, resourceLoader.getClassLoader()));
// 步骤 3:设置初始缓存名称及自定义配置
List<String> cacheNames = cacheProperties.getCacheNames();
if (!cacheNames.isEmpty()) {
builder.initialCacheNames(new LinkedHashSet<>(cacheNames));
}
// 步骤 4:启用事务感知
if (cacheProperties.getRedis().isEnableStatistics()) {
builder.enableStatistics();
}
// 应用自定义器
redisCacheManagerBuilderCustomizers.orderedStream()
.forEach(customizer -> customizer.customize(builder));
RedisCacheManager cacheManager = builder.build();
cacheManager.setTransactionAware(cacheProperties.getRedis().isCacheNullValues());
return cacheManager;
}
}4 个构建步骤总结:
| 步骤 | 方法 | 说明 |
|---|---|---|
| ① | fromConnectionFactory(redisConnectionFactory) | 创建 RedisCacheWriter,封装 Redis 连接 |
| ② | cacheDefaults(RedisCacheConfiguration) | 设置默认的缓存配置(TTL、序列化器等) |
| ③ | initialCacheNames(Set<String>) | 预创建指定名称的缓存,可覆盖默认配置 |
| ④ | enableStatistics() / transactionAware(true) | 启用统计和事务感知支持 |
4. RedisCacheWriter.nonLockingRedisCacheWriter() 无锁写入
RedisCacheWriter 是 Redis 缓存写入的核心抽象,RedisCacheManagerBuilder.fromConnectionFactory() 默认创建无锁写入器:
public class RedisCacheWriter {
public static RedisCacheWriter nonLockingRedisCacheWriter(
RedisConnectionFactory connectionFactory) {
return new RedisCacheWriter() {
@Override
public void put(String name, byte[] key, byte[] value, Duration ttl) {
// 直接调用 RedisConnection.set()
execute(connection -> {
if (ttl == null || ttl.isZero()) {
connection.stringCommands().set(key, value);
} else if (ttl.isNegative()) {
connection.keyCommands().del(key);
} else {
connection.stringCommands().setEx(key, ttl.getSeconds(), value);
}
return OK;
});
}
@Override
public byte[] get(String name, byte[] key) {
// 直接调用 RedisConnection.get()
return execute(connection ->
connection.stringCommands().get(key));
}
@Override
public void remove(String name, byte[] key) {
// 直接调用 RedisConnection.del()
execute(connection ->
connection.keyCommands().del(key));
}
// ... clean(), clearStatistics()
};
}
}无锁 vs 加锁写入:
| 特性 | nonLockingRedisCacheWriter | lockingRedisCacheWriter |
|---|---|---|
| 原子性 | 依赖 Redis 单线程模型保证 | 使用 Redis 分布式锁 |
| 锁 | 无锁 | SETNX 或 Redisson 锁 |
| 适用场景 | 单实例缓存、缓存穿透压力不大 | 缓存雪崩防御、热点 key 重建 |
| 性能 | 高 | 稍低(锁开销) |
execute() 方法:
private <T> T execute(RedisCallback<T> callback) {
// 获取 RedisConnection(从连接池)
RedisConnection connection = connectionFactory.getConnection();
try {
return callback.doInRedis(connection);
} finally {
connection.close(); // 归还到连接池
}
}5. RedisCacheConfiguration.defaultCacheConfig() 的 8 个默认配置
RedisCacheConfiguration.defaultCacheConfig() 提供了开箱即用的默认配置:
public class RedisCacheConfiguration {
public static RedisCacheConfiguration defaultCacheConfig() {
return defaultCacheConfig(null);
}
public static RedisCacheConfiguration defaultCacheConfig(ClassLoader classLoader) {
return new RedisCacheConfiguration()
// 1. Key 序列化:StringRedisSerializer
.serializeKeysWith(
RedisSerializationContext.SerializationPair.fromSerializer(
new StringRedisSerializer()))
// 2. Value 序列化:JdkSerializationRedisSerializer
.serializeValuesWith(
RedisSerializationContext.SerializationPair.fromSerializer(
new JdkSerializationRedisSerializer(classLoader)))
// 3. Null 值的 TTL:0(不过期)
.nullValueTTL(Duration.ZERO)
// 4. 缓存 null 值(防止缓存穿透)
.cacheNullValues(true)
// 5. Key 前缀(用于区分不同的 cache name)
.prefixCacheNameWith("")
// 6. 不启用统计
.disableStatistics()
// 7. 默认 TTL(不过期)
.entryTtl(Duration.ZERO)
// 8. 不启用事务感知
.disableCachingNullValues(); // 实际对应 cacheNullValues = false
}
}8 个默认配置汇总:
| # | 配置项 | 默认值 | 说明 |
|---|---|---|---|
| ① | keySerializationPair | StringRedisSerializer | Key 使用字符串序列化 |
| ② | valueSerializationPair | JdkSerializationRedisSerializer | Value 使用 JDK 序列化 |
| ③ | nullValueTTL | Duration.ZERO | null 值缓存不过期 |
| ④ | cacheNullValues | true | 允许缓存 null 值 |
| ⑤ | prefixCacheNameWith | "" | Key 前缀为空 |
| ⑥ | enableStatistics | false | 不启用统计 |
| ⑦ | entryTtl | Duration.ZERO | 默认条目不过期 |
| ⑧ | transactionAware | false | 不启用事务感知 |
自定义配置示例:
@Bean
public RedisCacheManagerBuilderCustomizer cacheManagerBuilderCustomizer() {
return builder -> builder
.cacheDefaults(RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(30))
.prefixCacheNameWith("myapp:")
.serializeValuesWith(
RedisSerializationContext.SerializationPair.fromSerializer(
new Jackson2JsonRedisSerializer<>(Object.class))));
}6. @Cacheable(cacheNames = "users") 的 RedisCache.get() 流程
当使用 @Cacheable(cacheNames = "users") 注解时,方法执行前会调用 RedisCache.get() 查找缓存:
// CacheInterceptor(Spring Cache 的核心拦截器)
public class CacheInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// 解析 @Cacheable 注解,获取 cacheNames = "users"
CacheOperationContext context = getCacheOperationContext(invocation);
Set<String> cacheNames = context.getCacheNames();
// 通过 CacheManager.getCache("users") 获取 Cache 实例
Cache cache = cacheManager.getCache("users");
// 调用 Cache.get(key)
Cache.ValueWrapper result = cache.get(key);
if (result != null) {
return result.get(); // 缓存命中
}
// 缓存未命中,执行目标方法
Object value = invocation.proceed();
// 将结果存入缓存
cache.put(key, value);
return value;
}
}RedisCache.get() 实现:
public class RedisCache implements Cache {
private final String name; // 缓存名称(如 "users")
private final RedisCacheWriter cacheWriter;
private final RedisCacheConfiguration cacheConfig;
@Override
public ValueWrapper get(Object key) {
// 1. 将 key 转换为缓存键
byte[] cacheKey = serializeCacheKey(key); // 使用 StringRedisSerializer
// 2. 调用 RedisCacheWriter.get() → RedisConnection.get()
byte[] result = cacheWriter.get(name, cacheKey);
if (result == null) {
return null; // 缓存未命中
}
// 3. 反序列化缓存值
// 使用 valueSerializationPair(JdkSerializationRedisSerializer)
// 返回 Cache.ValueWrapper
return toValueWrapper(cacheConfig.getValueSerializationPair()
.deserialize(result));
}
}完整查找流程:
@Cacheable("users") 方法调用
↓
CacheInterceptor.invoke()
↓
CacheManager.getCache("users") → 获取 RedisCache 实例
↓
RedisCache.get(key)
↓
RedisCacheWriter.get() → RedisConnection.get(key_bytes)
↓
Redis 服务端 GET 命令
├── 命中 → 反序列化 → 返回 ValueWrapper
└── 未命中 → 返回 null → 执行目标方法 → put() 写入缓存7. CaffeineCacheConfiguration 的 @ConditionalOnClass
CaffeineCacheConfiguration 是 Caffeine 缓存提供者的配置类,在 classpath 中存在 Caffeine 库时注册:
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({Caffeine.class}) // classpath 中有 Caffeine 库
public class CaffeineCacheConfiguration {
@Bean
@ConditionalOnMissingBean
CaffeineCacheManager cacheManager(CacheProperties cacheProperties,
CacheManagerCustomizers cacheManagerCustomizers) {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
// 设置 Caffeine 规格(从 spring.cache.caffeine.spec 读取)
String spec = cacheProperties.getCaffeine().getSpec();
if (StringUtils.hasText(spec)) {
cacheManager.setCacheSpecification(spec);
}
// 设置预定义的缓存名称
List<String> cacheNames = cacheProperties.getCacheNames();
if (!cacheNames.isEmpty()) {
cacheManager.setCacheNames(cacheNames);
}
return cacheManagerCustomizers.customize(cacheManager);
}
}application.yml 配置 Caffeine:
spring:
cache:
type: caffeine # 显式指定使用 Caffeine
cache-names: # 预创建缓存名称
- users
- products
caffeine:
spec: maximumSize=500,expireAfterWrite=10m # Caffeine 规格8. CaffeineCacheManager 的 getCache() 创建
CaffeineCacheManager 管理 Caffeine 缓存实例,getCache() 方法负责按需创建:
public class CaffeineCacheManager implements CacheManager {
private final ConcurrentHashMap<String, Cache> cacheMap = new ConcurrentHashMap<>();
private Caffeine<Object, Object> caffeineBuilder;
private CacheLoader<Object, Object> cacheLoader;
@Override
public Cache getCache(String name) {
// 1. 从缓存 Map 中获取已创建的 Cache
Cache cache = this.cacheMap.get(name);
if (cache != null) {
return cache;
}
// 2. 未创建则加锁创建
synchronized (this.cacheMap) {
cache = this.cacheMap.get(name);
if (cache == null) {
// 3. 创建新的 Caffeine Cache
cache = createNativeCaffeineCache(name);
// 4. 注册到缓存 Map
this.cacheMap.put(name, cache);
}
return cache;
}
}
protected Cache createNativeCaffeineCache(String name) {
// 使用 Caffeine.newBuilder() 构建
Caffeine<Object, Object> builder = (this.caffeineBuilder != null)
? this.caffeineBuilder
: Caffeine.newBuilder();
// 构建 Caffeine 原始缓存实例
com.github.benmanes.caffeine.cache.Cache<Object, Object> nativeCache;
if (this.cacheLoader != null) {
// 带 CacheLoader 的 LoadingCache
nativeCache = builder.build(this.cacheLoader);
} else {
// 普通 Cache
nativeCache = builder.build();
}
// 包装为 Spring Cache 适配器
return new com.github.benmanes.caffeine.cache.spring.CaffeineCache(name, nativeCache);
}
}缓存创建流程:
CaffeineCacheManager.getCache("users")
↓
cacheMap 中查找 → 未找到
↓
createNativeCaffeineCache("users")
↓
Caffeine.newBuilder().build() → 原生 Caffeine Cache
↓
包装为 CaffeineCache(Spring Cache 适配器)
↓
注册到 cacheMap
↓
返回 Cache 实例9. TaskExecutionAutoConfiguration 创建 ThreadPoolTaskExecutor
TaskExecutionAutoConfiguration 自动配置异步任务执行器,供 @Async 注解使用:
@AutoConfiguration(after = TaskSchedulingAutoConfiguration.class)
@ConditionalOnClass(ThreadPoolTaskExecutor.class)
@EnableConfigurationProperties(TaskExecutionProperties.class)
public class TaskExecutionAutoConfiguration {
@Bean
@ConditionalOnMissingBean(Executor.class)
public ThreadPoolTaskExecutor applicationTaskExecutor(
TaskExecutorBuilder taskExecutorBuilder) {
// 使用 TaskExecutorBuilder 构建 ThreadPoolTaskExecutor
ThreadPoolTaskExecutor executor = taskExecutorBuilder.build();
executor.setTaskDecorator(new TaskDecorator() {
@Override
public Runnable decorate(Runnable task) {
// 包装任务,传递上下文(如安全上下文、MDC)
return task;
}
});
return executor;
}
@Bean
@ConditionalOnMissingBean
public TaskExecutorBuilder taskExecutorBuilder(
TaskExecutionProperties properties) {
// 将 spring.task.execution.pool.* 配置绑定到 TaskExecutorBuilder
TaskExecutionProperties.Pool pool = properties.getPool();
TaskExecutorBuilder builder = new TaskExecutorBuilder();
builder = builder
.queueCapacity(pool.getQueueCapacity()) // 队列容量
.corePoolSize(pool.getCoreSize()) // 核心线程数
.maxPoolSize(pool.getMaxSize()) // 最大线程数
.allowCoreThreadTimeOut(pool.isAllowCoreThreadTimeout())
.keepAlive(pool.getKeepAlive()); // 线程存活时间
return builder;
}
}spring.task.execution.pool 配置映射:
| 配置项 | 默认值 | 说明 |
|---|---|---|
spring.task.execution.pool.core-size | 8 | 核心线程数 |
spring.task.execution.pool.max-size | Integer.MAX_VALUE | 最大线程数 |
spring.task.execution.pool.queue-capacity | Integer.MAX_VALUE | 工作队列容量 |
spring.task.execution.pool.keep-alive | 60s | 线程存活时间 |
spring.task.execution.pool.allow-core-thread-timeout | true | 允许核心线程超时 |
TaskExecutorBuilder.build() 创建 ThreadPoolTaskExecutor:
public class TaskExecutorBuilder {
public ThreadPoolTaskExecutor build() {
// 创建 Spring 的 ThreadPoolTaskExecutor
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(this.corePoolSize);
executor.setMaxPoolSize(this.maxPoolSize);
executor.setQueueCapacity(this.queueCapacity);
executor.setKeepAliveSeconds((int) this.keepAlive.getSeconds());
executor.setAllowCoreThreadTimeOut(this.allowCoreThreadTimeOut);
executor.setThreadNamePrefix(this.threadNamePrefix);
executor.setRejectedExecutionHandler(this.rejectedExecutionHandler);
executor.initialize(); // 初始化线程池
return executor;
}
}10. TaskSchedulingAutoConfiguration 创建 ThreadPoolTaskScheduler
TaskSchedulingAutoConfiguration 自动配置任务调度器,供 @Scheduled 注解使用:
@AutoConfiguration(after = TaskExecutionAutoConfiguration.class)
@ConditionalOnClass(ThreadPoolTaskScheduler.class)
@EnableConfigurationProperties(TaskSchedulingProperties.class)
public class TaskSchedulingAutoConfiguration {
@Bean
@ConditionalOnMissingBean({SchedulingConfigurer.class, TaskScheduler.class,
ScheduledExecutorService.class})
public ThreadPoolTaskScheduler taskScheduler(
TaskSchedulerBuilder taskSchedulerBuilder) {
return taskSchedulerBuilder.build();
}
@Bean
@ConditionalOnMissingBean
public TaskSchedulerBuilder taskSchedulerBuilder(
TaskSchedulingProperties properties) {
// 从 spring.task.scheduling.pool.size 读取配置
TaskSchedulerBuilder builder = new TaskSchedulerBuilder();
builder = builder
.poolSize(properties.getPool().getSize()) // 调度线程池大小
.threadNamePrefix(properties.getThreadNamePrefix()); // 线程名前缀
return builder;
}
}spring.task.scheduling 配置映射:
| 配置项 | 默认值 | 说明 |
|---|---|---|
spring.task.scheduling.pool.size | 1 | 调度线程池大小 |
spring.task.scheduling.thread-name-prefix | scheduling- | 线程名前缀 |
TaskSchedulerBuilder.build() 创建 ThreadPoolTaskScheduler:
public class TaskSchedulerBuilder {
public ThreadPoolTaskScheduler build() {
// 创建 Spring 的 ThreadPoolTaskScheduler
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(this.poolSize);
scheduler.setThreadNamePrefix(this.threadNamePrefix);
scheduler.setRejectedExecutionHandler(this.rejectedExecutionHandler);
scheduler.setWaitForTasksToCompleteOnShutdown(this.waitForTasksToCompleteOnShutdown);
scheduler.setAwaitTerminationSeconds(this.awaitTerminationSeconds);
scheduler.initialize(); // 初始化调度器
return scheduler;
}
}底层使用 ScheduledExecutorFactoryBean:
// ThreadPoolTaskScheduler 内部使用 ScheduledExecutorFactoryBean
// 实际创建 java.util.concurrent.ScheduledThreadPoolExecutor
ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean();
factory.setPoolSize(this.poolSize);
factory.afterPropertiesSet();
ScheduledExecutorService executor = factory.getObject();总结
缓存与 Redis 自动配置的 10 个细节点总结如下:
| # | 细节点 | 核心类/机制 |
|---|---|---|
| ① | CacheAutoConfiguration 的 CacheConfigurationImportSelector | 按 String[] 顺序探测 CacheManager 实现 |
| ② | 缓存提供者的探测顺序 | RedisCacheConfiguration → CaffeineCacheConfiguration → SimpleCacheConfiguration |
| ③ | RedisCacheManagerBuilder 的 4 个构建步骤 | RedisCacheWriter → RedisCacheConfiguration.defaultCacheConfig() → initialCacheConfiguration → transactionAware |
| ④ | RedisCacheWriter.nonLockingRedisCacheWriter() 无锁写入 | RedisConnection.set() / get() / del() 直接操作 |
| ⑤ | RedisCacheConfiguration.defaultCacheConfig() 的 8 个默认配置 | keySerializationPair = StringRedisSerializer、valueSerializationPair = JdkSerializationRedisSerializer、nullValueTTL |
| ⑥ | @Cacheable(cacheNames = "users") 的 RedisCache.get() 流程 | RedisCache.lookup(key) → RedisConnection.get(key) → Cache.ValueWrapper |
| ⑦ | CaffeineCacheConfiguration 的 @ConditionalOnClass | com.github.benmanes.caffeine.cache.Caffeine 存在 |
| ⑧ | CaffeineCacheManager 的 getCache() 创建 | createNativeCaffeineCache(name) → Caffeine.newBuilder().build() |
| ⑨ | TaskExecutionAutoConfiguration 创建 ThreadPoolTaskExecutor | TaskExecutorBuilder 从 spring.task.execution.pool 绑定 |
| ⑩ | TaskSchedulingAutoConfiguration 创建 ThreadPoolTaskScheduler | spring.task.scheduling.pool.size → ScheduledExecutorFactoryBean |