前言
通过在SpringBoot中集成Redis,详细梳理集成过程。包括SpringBoot启动过程中,容器的刷新、自动配置的流程、各类注解的处理。
类比在纯Spring中集成Redis,体验SpringBoot自动配置给开发带来了哪些便利。
一、测试样例
1.1配置文件
application.yml
spring:redis:host: *.*.*.*port: 6379password: 123456database: 0timeout: 1000lettuce:pool:max-active: 8max-idle: 8min-idle: 0
1.2依赖项
pom.xml
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
SpringBoot中默认自带了Redis客户端lettuce
1.3单元测试类
package com.lazy.snail;import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;@SpringBootTest
class ApplicationTests {@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Testvoid contextLoads() {stringRedisTemplate.opsForValue().set("name", "lazysnail");stringRedisTemplate.opsForValue().set("email", "lazy_snail@aliyun.com");}}
- 测试结果:
二、自动配置机制版本差异
Spring Boot自2.7版本开始逐步弃用spring.factories文件,并在3.0版本中将其彻底移除。
2.1SpringBoot2.7版本前
在/META-INF/目录下,包含了一系列的自动配置类,这些类可以在满足特定条件时被Spring Boot自动加载和使用。
2.2SpringBoot2.7.x版本
/META-INF/spring目录下新增了org.springframework.boot.autoconfigure.AutoConfiguration.imports,将原本spring.factories中的自动配置类移过来了。
2.3SpringBoot2.6.x及2.7.x获取候选自动配置类差异
2.7.x版本新增了@AutoConfiguration标记类的处理
2.4SpringBoot3.x获取候选自动配置类
3.x后移除了spring.factories的处理
2.5redis自动配置差异
2.7.x版本后引入了@AutoConfiguration注解。
@AutoConfiguration注解表明该类提供的配置可以被Spring Boot自动应用。
[!NOTE]
Spring Boot 3.x移除spring.factories中自动配置类的处理机制主要是为了:
增强模块化和清晰性,减少隐式依赖。
适应Spring 6和Java 17的现代化要求。
提供更加显式和可控的配置机制,使得开发者能够更好地理解和调试自动配置的过程。
更好地支持条件化配置和@ConfigurationProperties等现代配置方式。
Spring 6和Java 17还没仔细的研究过,后续再分享这块内容
三、redis集成流程
3.1SpringBoot启动过程刷新容器
3.1.1调用BeanFactoryPostProcessor
- 实例化ConfigurationClassPostProcessor
- 调用BeanDefinitionRegistryPostProcessor(ConfigurationClassPostProcessor实现了BeanDefinitionRegistryPostProcessor)
- 处理配置类bean定义信息
- 找到Application(启动SpringBoot的主类)(@SpringBootApplication注解中有@Configuration注解)
- 解析处理Application类上的注解
- 处理@Import注解(@SpringBootApplication注解中的@EnableAutoConfiguration注解含有@Import(AutoConfigurationImportSelector.class))
- 获取自动配置类入口
- 获取候选自动配置类
- 2.7.x版本从spring.factories和org.springframework.boot.autoconfigure.AutoConfiguration.imports中获取配置类
- redis自动配置类的全限定名被获取到
- 经过去重、排除、过滤器,筛选出可用配置类
- 过滤器主要通过配置类上@ConditionalOnClass等条件进行匹配,不符合条件的配置类将被筛除
- RedisAutoConfiguration作为普通配置类进行解析
- 处理RedisAutoConfiguration的@Import
- 引入LettuceConnectionConfiguration配置
由于没有引入Jedis客户端依赖,JedisConnectionConfiguration配置类会被跳过
所有的配置类都解析完成后 (俄罗斯套娃结束),把配置类上所有的bean配置(@Bean、@Import(xxx.Registrar.class)等)加载为bean定义信息。
3.1.2finishBeanFactoryInitialization实例化所有剩余的单例
实例化后,容器中已经存在stringRedisTemplate和redisTemplate实例,可以直接注入使用
四、Redis自动配置类详解
4.1SpringBoot2.7.x版本RedisAutoConfiguration源码
package org.springframework.boot.autoconfigure.data.redis;import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;@AutoConfiguration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {@Bean@ConditionalOnMissingBean(name = "redisTemplate")@ConditionalOnSingleCandidate(RedisConnectionFactory.class)public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplate<Object, Object> template = new RedisTemplate<>();template.setConnectionFactory(redisConnectionFactory);return template;}@Bean@ConditionalOnMissingBean@ConditionalOnSingleCandidate(RedisConnectionFactory.class)public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {return new StringRedisTemplate(redisConnectionFactory);}}
4.2注解说明
1.@AutoConfiguration
- 作用:@AutoConfiguration是Spring Boot 2.7.0引入的注解,用于标记一个类为自动配置类。自动配置类是Spring Boot启动时根据条件自动注入到 Spring 容器中的配置类。
- 工作原理:这个注解通过条件注解(如@ConditionalOnClass、@ConditionalOnMissingBean)确定是否应该应用某些配置。它的功能类似于@EnableAutoConfiguration和@Configuration的组合。
2. @ConditionalOnClass(RedisOperations.class)
- 作用:这是一个条件注解,当类路径中存在RedisOperations时,才会加载这个配置类。
- 具体含义:@ConditionalOnClass(RedisOperations.class) 表明只有在类路径下找到RedisOperations类时,RedisAutoConfiguration 才会被加载。RedisOperations是Spring Data Redis中的接口,代表Redis操作的基本API,因此,只有在Redis相关依赖存在时Redis自动配置才会生效。
- 本案例在pom中引入了spring-boot-starter-data-redis依赖项,所以类路径下存在RedisOperations。
3. @EnableConfigurationProperties(RedisProperties.class)
- 作用:这个注解可以让RedisProperties类的属性能够被注入到Spring容器中。RedisProperties类用来读取application.properties或 application.yml中关于Redis相关的配置。
- 具体含义:RedisProperties类包含Redis配置的相关属性(如 Redis 服务器地址、端口、密码等),@EnableConfigurationProperties注解确保这些配置能够被自动加载并绑定到相应的属性类中。
4. @Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
- 作用:@Import注解用于引入其他配置类。这里会把LettuceConnectionConfiguration和JedisConnectionConfiguration两个配置类导入进来。
- 具体含义:Spring Data Redis提供了两个主要的客户端(spring-boot-starter-data-redis自带了Lettuce的实现,如果需要使用Jedis还要引入redis.clients):Lettuce和Jedis,这两个类负责Redis连接的配置。通过@Import,Redis自动配置类可以根据需要选择使用其中之一来配置Redis连接。
5. @Bean
- 作用:@Bean注解用于将一个方法标记为一个 Bean,方法返回值将作为Spring容器中的一个Bean。Spring会管理这个Bean的生命周期,并根据需要注入到其他地方。
- 具体含义:@Bean注解的作用是将redisTemplate和stringRedisTemplate方法返回的对象注册为Spring容器中的Bean。这样,Spring 容器就会自动注入这两个模板类的实例到需要使用Redis的地方。
- 本案例直接在单元测试类中注入stringRedisTemplate,既可以对redis进行一些简单的操作。
6. @ConditionalOnMissingBean(name = “redisTemplate”)
- 作用:这个注解表示当Spring容器中没有名为"redisTemplate"的Bean时,才会执行该方法并创建RedisTemplate的Bean。
- 具体含义:如果容器中已经存在一个名为 “redisTemplate” 的 Bean,redisTemplate方法不会被调用,也不会再次创建一个新的 RedisTemplate实例。防止覆盖已经存在的Redis配置。
7. @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
- 作用:这个注解表示只有当Spring容器中存在且仅有一个RedisConnectionFactory类型的Bean时,才会执行该方法。
- 具体含义:RedisConnectionFactory是Redis连接的核心接口,@ConditionalOnSingleCandidate确保在容器中只有一个符合条件的连接工厂时,才会进行Redis模板的创建。避免多个Redis连接工厂的冲突。
8. @ConditionalOnMissingBean
- 作用:这个注解表示当Spring容器中没有指定类型的Bean时,才会执行该方法并创建对应的Bean。
- 具体含义:如果容器中没有定义过StringRedisTemplate类型的Bean,就会调用 stringRedisTemplate方法创建并注册这个Bean。如果容器中已有这个类型的Bean,这个方法不会被执行。
五、集成完善(初级)
5.1封装一个Redis工具类的redis服务
package com.lazy.snail;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;import java.util.List;
import java.util.concurrent.TimeUnit;/*** @ClassName RedisService* @Description TODO* @Author lazysnail* @Date 2024/11/14 18:05* @Version 1.0*/
@Service
public class RedisService {private final RedisTemplate<String, Object> redisTemplate;private final StringRedisTemplate stringRedisTemplate;@Autowiredpublic RedisService(RedisTemplate<String, Object> redisTemplate, StringRedisTemplate stringRedisTemplate) {this.redisTemplate = redisTemplate;this.stringRedisTemplate = stringRedisTemplate;}// 保存对象public void set(String key, Object value) {redisTemplate.opsForValue().set(key, value);}// 保存对象并设置过期时间public void set(String key, Object value, long timeout, TimeUnit timeUnit) {redisTemplate.opsForValue().set(key, value, timeout, timeUnit);}// 获取对象public Object get(String key) {return redisTemplate.opsForValue().get(key);}// 删除对象public void delete(String key) {redisTemplate.delete(key);}// 设置 String 类型的值public void setString(String key, String value) {stringRedisTemplate.opsForValue().set(key, value);}// 获取 String 类型的值public String getString(String key) {return stringRedisTemplate.opsForValue().get(key);}// 批量删除public void deleteBatch(List<String> keys) {if (!CollectionUtils.isEmpty(keys)) {redisTemplate.delete(keys);}}// 设置 key 的过期时间public Boolean expire(String key, long timeout, TimeUnit timeUnit) {return redisTemplate.expire(key, timeout, timeUnit);}// 检查 key 是否存在public Boolean hasKey(String key) {return redisTemplate.hasKey(key);}
}
5.2单元测试类
package com.lazy.snail;import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest
class ApplicationTests {@Autowiredprivate RedisService redisService;@Testvoid contextLoads() {redisService.setString("name", "lazysnail");redisService.setString("email", "lazy_snail@aliyun.com");}
}