前言
通过在SpringBoot中集成Redis,详细梳理集成过程。包括SpringBoot启动过程中,容器的刷新、自动配置的流程、各类注解的处理。
类比在纯Spring中集成Redis,体验SpringBoot自动配置给开发带来了哪些便利。
一、测试样例
1.1配置文件
application.yml
spring:
redis:
host: *.*.*.*
port: 6379
password: 123456
database: 0
timeout: 1000
lettuce:
pool:
max-active: 8
max-idle: 8
min-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 {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Test
void 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自动应用。
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;
@Autowired
public 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 {
@Autowired
private RedisService redisService;
@Test
void contextLoads() {
redisService.setString("name", "lazysnail");
redisService.setString("email", "lazy_snail@aliyun.com");
}
}