springboot提供的所有的自动装配类都在spring-boot-autoconfigure(直接查看源码)
也可以通过官网来进行查看(选取要看的版本):
https://docs.spring.io/spring-boot/docs/2.2.3.BUILD-SNAPSHOT/reference/html/appendix-auto-configuration-classes.html#auto-configuration-classes-from-autoconfigure-module
如图所示是spring-boot-autoconfigure下对于redis提供类:
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
//使用redis database数 默认为0
private int database = 0;
//连接url 可包含用户和密码 例如 redis://user:[email protected]:6379
private String url;
//redis 端口号
private String host = "localhost";
//密码
private String password;
//端口号
private int port = 6379;
//是否开启SSL
private boolean ssl;
//超时时间
private Duration timeout;
//设置客户端名
private String clientName;
//支持哨兵机制
private Sentinel sentinel;
//支持集群配置
private Cluster cluster;
//Jedis客户端
private final Jedis jedis = new Jedis();
//Lettuce客户端
private final Lettuce lettuce = new Lettuce();
/**
* 连接池一些属性
*/
public static class Pool {
//池中维护的最大空闲数 为负值代表
private int maxIdle = 8;
//池中要维护的最小空闲连接数 此值必须为正值
private int minIdle = 0;
//最大连接数 -数代表无限制
private int maxActive = 8;
//最大等待时间
private Duration maxWait = Duration.ofMillis(-1);
//
private Duration timeBetweenEvictionRuns;
}
/**
* 集群配置
*/
public static class Cluster {
//集群各节点地址host:port 多个以,隔开隔开
private List nodes;
//集群中执行命令时重定向node的最大数量
private Integer maxRedirects;
}
/**
*Redis 哨兵参数属性
*/
public static class Sentinel {
//集群中master名
private String master;
//集群各节点地址host:port 多个以,隔开隔开
private List nodes;
}
/**
* jedis客户端属性
*/
public static class Jedis {
//连接池一些配置
private Pool pool;
}
/**
*Lettuce客户端属性
*/
public static class Lettuce {
//停止超时时间
private Duration shutdownTimeout = Duration.ofMillis(100);
//连接池一些配置
private Pool pool;
}
}
RedisAutoConfiguration是springboot结合Redis配置核心类。
//禁止其它配置类使用该配置类的@Bean方法
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
//初始化Redis连接工厂类 Import执行的初始化类是顺序执行,这里面会存在一个使用问题须谨慎(后续描述)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
//若系统中不存在默认的redisTemplateBean 进行初始化 依赖Redis连接工厂类
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate
此类是下述LettuceConnectionConfiguration与JedisConnectionConfiguration的父类,提供对一些基础类的管理。例如RedisProperties,RedisSentinelConfiguration,RedisClusterConfiguration类
这里我将这两个类放在一块来解释,因为两者的功能都是一致为了创建初始化Redis连接工厂类
LettuceConnectionConfiguration:
//禁止其它配置类使用该配置类的@Bean方法
@Configuration(proxyBeanMethods = false)
//应用包含RedisClient 此类在io.lettuce.core包中
@ConditionalOnClass(RedisClient.class)
class LettuceConnectionConfiguration extends RedisConnectionConfiguration {
LettuceConnectionConfiguration(RedisProperties properties,
ObjectProvider sentinelConfigurationProvider,
ObjectProvider clusterConfigurationProvider) {
super(properties, sentinelConfigurationProvider, clusterConfigurationProvider);
}
@Bean(destroyMethod = "shutdown")
@ConditionalOnMissingBean(ClientResources.class)
DefaultClientResources lettuceClientResources() {
return DefaultClientResources.create();
}
@Bean
//如果上下文不包含RedisConnectionFactory的bean初始化
@ConditionalOnMissingBean(RedisConnectionFactory.class)
LettuceConnectionFactory redisConnectionFactory(
//ObjectProvider 作用和@Autowired作用一致和Autowired不同在于对于实例对象为null的处理ObjectProvider只有当实例对象的确存在时才检索bean,而Autowired会报错 ObjectProvider可以支持多实例
ObjectProvider builderCustomizers,
ClientResources clientResources) throws UnknownHostException {
LettuceClientConfiguration clientConfig = getLettuceClientConfiguration(builderCustomizers, clientResources,
getProperties().getLettuce().getPool());
//调用Lettuce API创建LettuceConnectionFactory 有兴趣可以自己看下
return createLettuceConnectionFactory(clientConfig);
}
。。。。。
}
JedisConnectionConfiguration:
//禁止其它配置类使用该配置类的@Bean方法
@Configuration(proxyBeanMethods = false)
//上下文引入Jedis包
@ConditionalOnClass({ GenericObjectPool.class, JedisConnection.class, Jedis.class })
class JedisConnectionConfiguration extends RedisConnectionConfiguration {
JedisConnectionConfiguration(RedisProperties properties,
ObjectProvider sentinelConfiguration,
ObjectProvider clusterConfiguration) {
super(properties, sentinelConfiguration, clusterConfiguration);
}
@Bean
//如果上下文不包含RedisConnectionFactory的bean初始化
@ConditionalOnMissingBean(RedisConnectionFactory.class)
JedisConnectionFactory redisConnectionFactory(
//ObjectProvider 作用和@Autowired作用一致和Autowired不同在于对于实例对象为null的处理ObjectProvider只有当实例对象的确存在时才检索bean,而Autowired会报错 ObjectProvider可以支持多实例
ObjectProvider builderCusto mizers) throws UnknownHostException {
//调用Jedis API创建JedisConnectionFactory 有兴趣可以自己看下
return createJedisConnectionFactory(builderCustomizers);
}
。。。。。
}
从上述代码可以看出在整个应用上下文只会存在一个RedisConnectionFactory的实例,如果LettuceConnectionConfiguration和JedisConnectionConfiguration都满足要求都会执行,那么springboot会按照Import中顺序执行初始化,也就是第一优先执行LettuceConnectionConfiguration来创建RedisConnectionFactory实例。例如如果我们使用spring-boot-starter-parent来作为应用的父工程,又想使用Jedis客户端,那么就会出现上述的问题,原因在于spring-boot-starter-data-redis(默认推荐的是基于Lettcue)中会存在Lettuce包的依赖,如果我们不注意这块就会出现不管怎么设置jedis pool的属性发现都毫无作用,因为创建的是LettuceConnectionFactory实例对象。
解决方案:
org.springframework.boot
spring-boot-starter-data-redis
io.lettuce
lettuce-core
两者的功能作用一致,所以将两者放到一起描述 ,这里以JedisClientConfigurationBuilderCustomizer做为Demo来描述。
我们可以使用JedisClientConfigurationBuilderCustomizer来做一些定制化参数配置,也可以在多Redis下使不同客户端使用不同的定制化配置(后续会详细介绍)。
springboot执行定制操作代码:
class JedisConnectionConfiguration extends RedisConnectionConfiguration {
private JedisClientConfiguration getJedisClientConfiguration(
ObjectProvider builderCustomizers) {
。。。。。
//执行应用中被注入到spring类JedisClientConfigurationBuilderCustomizer的Bean
builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
return builder.build();
}
}
测试Demo:
@Configuration
class RedisCustomizerConfiguration {
@Bean
public MyJedisClientConfigurationBuilderCustomizer builderCustomizers(){
return new MyJedisClientConfigurationBuilderCustomizer();
}
@Bean
public MyJedisClientConfigurationBuilderCustomizerTwo builderCustomizersTow(){
return new MyJedisClientConfigurationBuilderCustomizerTwo();
}
public class MyJedisClientConfigurationBuilderCustomizer implements JedisClientConfigurationBuilderCustomizer {
@Override
public void customize(JedisClientConfiguration.JedisClientConfigurationBuilder clientConfigurationBuilder) {
JedisClientConfiguration jedisClientConfiguration = clientConfigurationBuilder.build();
//获取连接池信息 并做定制化设置
GenericObjectPoolConfig genericObjectPoolConfig = jedisClientConfiguration.getPoolConfig().get();
if("test".equals(jedisClientConfiguration.getClientName().get())){
//此两个参数在RedisProperties中统一为timeout 所以默认connectTimeout与readTimeout相同
//可以通过JedisClientConfigurationBuilderCustomizer进行细致化配置
clientConfigurationBuilder.connectTimeout(Duration.ofSeconds(10));
clientConfigurationBuilder.readTimeout(Duration.ofSeconds(10));
clientConfigurationBuilder.usePooling();
}
}
}
public class MyJedisClientConfigurationBuilderCustomizerTwo implements JedisClientConfigurationBuilderCustomizer {
@Override
public void customize(JedisClientConfiguration.JedisClientConfigurationBuilder clientConfigurationBuilder) {
JedisClientConfiguration jedisClientConfiguration = clientConfigurationBuilder.build();
//获取连接池信息 并做定制化设置
GenericObjectPoolConfig genericObjectPoolConfig = jedisClientConfiguration.getPoolConfig().get();
if("test1".equals(jedisClientConfiguration.getClientName().get())) {
//此两个参数在RedisProperties中统一为timeout 所以默认connectTimeout与readTimeout相同
//可以通过JedisClientConfigurationBuilderCustomizer进行细致化配置
clientConfigurationBuilder.connectTimeout(Duration.ofSeconds(6));
clientConfigurationBuilder.readTimeout(Duration.ofSeconds(6));
clientConfigurationBuilder.usePooling();
}
}
}
}
LettuceClientConfigurationBuilderCustomizer的使用方式和JedisClientConfigurationBuilderCustomizer是一致
两类是为了简化spring Data中Repository对于redis的支持的一些列初始化的操作
//禁止其它配置类使用该配置类的@Bean方法
@Configuration(proxyBeanMethods = false)
//应用上下文必须包含 @EnableRedisRepositories注解类 EnableRedisRepositories中会Import RedisRepositoriesRegistrar类,会根据注解中的各参数进行各种初始化
@ConditionalOnClass(EnableRedisRepositories.class)
@ConditionalOnBean(RedisConnectionFactory.class)
//通过配置文件中spring.data.redis.repositories属性来配置是否执行RedisRepositoriesAutoConfiguration
@ConditionalOnProperty(prefix = "spring.data.redis.repositories", name = "enabled", havingValue = "true",
matchIfMissing = true)
//必须包含RedisRepositoryFactoryBean类实例对象
@ConditionalOnMissingBean(RedisRepositoryFactoryBean.class)
//执行仓库注册类
@Import(RedisRepositoriesRegistrar.class)
//在RedisAutoConfiguration之后执行
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisRepositoriesAutoConfiguration {
}
//整体使用了模版方法设计模式(可以了解下24种设计模式)
//提供有关于Redis对于Repository模块基础参数
class RedisRepositoriesRegistrar extends AbstractRepositoryConfigurationSourceSupport {
@Override
protected Class extends Annotation> getAnnotation() {
return EnableRedisRepositories.class;
}
@Override
protected Class> getConfiguration() {
return EnableRedisRepositoriesConfiguration.class;
}
@Override
protected RepositoryConfigurationExtension getRepositoryConfigurationExtension() {
//初始化Redis Repository配置扩展类 用于初始化注册一些基础Bean
return new RedisRepositoryConfigurationExtension();
}
//启用spring Data中Repository 对Redis的支持
@EnableRedisRepositories
private static class EnableRedisRepositoriesConfiguration {
}
}
//
public abstract class AbstractRepositoryConfigurationSourceSupport
//实现该接口可以做到动态注册Bean 详细可以了解下ImportBeanDefinitionRegistrar接口的作用
implements ImportBeanDefinitionRegistrar
//初始化时可以BeanFactory写入类属性中
,BeanFactoryAware
//初始化时可以ResourceLoader写入类属性中
, ResourceLoaderAware
//初始化时可以Environment写入类属性中
, EnvironmentAware {
private ResourceLoader resourceLoader;
private BeanFactory beanFactory;
private Environment environment;
//结合注入类(RedisRepositoriesAutoConfiguration)的Metadata信息将需要初始化的Bean注入
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
BeanNameGenerator importBeanNameGenerator) {
//RepositoryConfigurationDelegate Repository仓库代理类是spring-data对于Repository模块核心类,用于代理执行注册执行Bean
//有兴趣可以查看下spring-data Repository源码 这里不详细介绍
RepositoryConfigurationDelegate delegate = new RepositoryConfigurationDelegate(
getConfigurationSource(registry, importBeanNameGenerator), this.resourceLoader, this.environment);
//注册和Repository有关bean
delegate.registerRepositoriesIn(registry, getRepositoryConfigurationExtension());
}
。。。。。
}
Spring Data中Repository对于Redis的支持可以查看此篇博客
此类是redis对于响应式编程的支
@Configuration(proxyBeanMethods = false)
//Flux类 所属reactor包中 需要在maven依赖中添加
@ConditionalOnClass({ ReactiveRedisConnectionFactory.class, ReactiveRedisTemplate.class, Flux.class })
//在RedisAutoConfiguration启动之后执行
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisReactiveAutoConfiguration {
//创建响应式编码类
@Bean
@ConditionalOnMissingBean(name = "reactiveRedisTemplate")
//必须要求自定义创建ReactiveRedisConnectionFactory实例bean
@ConditionalOnBean(ReactiveRedisConnectionFactory.class)
public ReactiveRedisTemplate reactiveRedisTemplate(
ReactiveRedisConnectionFactory reactiveRedisConnectionFactory, ResourceLoader resourceLoader) {
JdkSerializationRedisSerializer jdkSerializer = new JdkSerializationRedisSerializer(
resourceLoader.getClassLoader());
RedisSerializationContext serializationContext = RedisSerializationContext
.newSerializationContext().key(jdkSerializer).value(jdkSerializer).hashKey(jdkSerializer)
.hashValue(jdkSerializer).build();
return new ReactiveRedisTemplate<>(reactiveRedisConnectionFactory, serializationContext);
}
@Bean
@ConditionalOnMissingBean(name = "reactiveStringRedisTemplate")
//必须要求自定义创建ReactiveRedisConnectionFactory实例bean
@ConditionalOnBean(ReactiveRedisConnectionFactory.class)
public ReactiveStringRedisTemplate reactiveStringRedisTemplate(
ReactiveRedisConnectionFactory reactiveRedisConnectionFactory) {
return new ReactiveStringRedisTemplate(reactiveRedisConnectionFactory);
}
}