目录
1. springboot 自定义的 starter 的用途
2. 自定义 Spring Boot Starter 可以用于各种场景
3. 以自定义 redis-starter 为例
3.1 自定义读取配置文件类
3.2 自定义 RedisTemplateAutoConfiguration
3.3 自定义 redis 缓存配置
3.4 自定义reids Lettuce 连接池
3.6 自定义 spring.factories 文件
3.7 在其他模块测试自定义的starter
Spring Boot Starter 是一种用于简化 Spring Boot 应用程序依赖项管理的方式。它们是一组预定义的依赖项,可以将它们添加到您的应用程序中,以便您可以更轻松地配置和使用 Spring Boot 应用程序中的各种功能。
自定义 Spring Boot Starter 可以帮助您将自己的库或框架集成到 Spring Boot 应用程序中。通过创建自己的 Starter,您可以将所有必需的依赖项打包到一个单独的模块中,并提供一个简单的配置方式,以便其他开发人员可以轻松地使用您的库或框架。
自定义 Spring Boot Starter 的主要用途包括:
1. 简化依赖项管理:通过将所有必需的依赖项打包到一个单独的模块中,您可以简化其他开发人员使用您的库或框架的过程。
2. 提供简单的配置方式:通过提供一个简单的配置方式,您可以使其他开发人员更轻松地配置和使用您的库或框架。
3. 提供自定义的自动配置:通过提供自定义的自动配置,您可以使您的库或框架更容易地集成到 Spring Boot 应用程序中。
1. 数据库访问:您可以创建一个自定义 Starter,用于简化数据库访问的配置和使用。例如,您可以创建一个 Starter,用于集成 MyBatis 或 Hibernate 等 ORM 框架。
2. 缓存:您可以创建一个自定义 Starter,用于简化缓存的配置和使用。例如,您可以创建一个 Starter,用于集成 Redis 或 Ehcache 等缓存框架。
3. 消息队列:您可以创建一个自定义 Starter,用于简化消息队列的配置和使用。例如,您可以创建一个 Starter,用于集成 RabbitMQ 或 Kafka 等消息队列框架。
4. 安全性:您可以创建一个自定义 Starter,用于简化安全性的配置和使用。例如,您可以创建一个 Starter,用于集成 Spring Security 或 OAuth2 等安全框架。
5. 日志:您可以创建一个自定义 Starter,用于简化日志的配置和使用。例如,您可以创建一个 Starter,用于集成 Log4j2 或 Logback 等日志框
自定义 starter 必要的 配置类, 用于读取yml或properties中的配置, 目的是为了在不同的模块都可以引入我们的自定义配置
/**
* @author yukun.yan
* @description RedisTemplateConfigProperties
* @date 2023/5/4 17:18
*/
@Data
@PropertySource(value = "classpath:sp-redis.properties")
@ConfigurationProperties(prefix = "sp.redis")
public class RedisTemplateConfigProperties {
private String host;
private String password;
private int port;
private int database;
private int minIdle;
private int maxIdle;
private int maxTotal;
private int timeout;
private int expire;
private int maxWait;
}
以及配置文件前缀, 这个不配置具体的参数, 目的是为了在其他模块配置
sp.redis.database=
sp.redis.password=
sp.redis.port=
sp.redis.host=
sp.redis.timeout=
sp.redis.min-idle=
sp.redis.max-idle=
sp.redis.max-total=
sp.redis.expire=
sp.redis.max-wait=
sp.redis.enable=
这里需要用到一个关键的主键, @AutoConfigureBefore(RedisAutoConfiguration.class)
顾名思义, 这个注解的意思是在 RedisAutoConfiguration.class 注入之前去注入我们自定义的 RedisTemplateAutoConfiguration 中配置的 bean, 不然会出现这样的错误 :
错误的原因是, springboot 在启动时会优先加载 springboot 框架自带的 spring.factories 文件中的定义的自动配置类, 把框架原本的 redisTemplate 作为 bean 注入到 容器中, 之后才会加载我们自定义的 RedisTemplateAutoConfiguration, 并且再次把我们魔改过 redisTemplate 注入到 bean 中, 这样容器中出现了2个名字一样的 redisTemplate, 所以会出现上述的错误
我们自定义的 redis-starter 的目的是想让框架加载我们魔改后的 redisTemplate, 结合源码发现, 框架只会在缺失 redisTemplate 这个 bean 的时候才会加载框架自带的 redisTemplate, 源码位置 : org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration
所以我们可以使用 @AutoConfigureBefore(RedisAutoConfiguration.class) 这个注解, 让框架优先加载我们自定义的 redisTemplate, 并注入到容器中, 之后框架在加载原本的 RedisAutoConfiguration 时候, 通过注解 @ConditionalOnMissingBean(name = "redisTemplate") 来控制不再做加载
/**
* @author yukun.yan
* @description RedisTemplateAutoConfiguration redis操作类自动装配
* @date 2023/5/4 16:43
*/
@AutoConfigureBefore(RedisAutoConfiguration.class)
@PropertySource(value = "classpath:sp-redis.properties")
@EnableConfigurationProperties({RedisTemplateConfigProperties.class}) // 依赖配置文件
public class RedisTemplateAutoConfiguration {
/**
* com.fasterxml.jackson.databind.deser.BeanDeserializer
* ObjectMapper om = new ObjectMapper(); 调用 vanillaDeserialize方法
* ObjectMapper om = mapperBuilder.build(); 调用 deserializeFromObject方法
*/
@Bean
public RedisTemplate redisTemplate(LettuceConnectionFactory lettuceConnectionFactory, Jackson2ObjectMapperBuilder mapperBuilder) {
// Json序列化配置
Jackson2JsonRedisSerializer
同样的, @AutoConfigureAfter({RedisTemplateAutoConfiguration.class}) 这个注解是规定在我们自定义的 RedisTemplateAutoConfiguration 执行完毕之后, 在加载缓存配置, 否则在没有 redis 的情况下, 加载缓存没有意义
/**
* @author yukun.yan
* @description RedisCacheAutoConfiguration redis缓存自动配置
* @date 2023/5/5 10:15
*/
@AutoConfigureAfter({RedisAutoConfiguration.class})
@ConditionalOnMissingBean({CacheManager.class})
@EnableConfigurationProperties(CacheProperties.class)
public class RedisCacheAutoConfiguration {
/**
* 实例化自定义的缓存管理器
*/
@Bean
@SuppressWarnings(value = {"rawtypes"})
public RedisCacheManager redisCacheManager(RedisTemplate redisTemplate) {
RedisConnectionFactory redisConnectionFactory = Objects.requireNonNull(redisTemplate.getConnectionFactory());
RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
return new CustomRedisCacheManager(redisCacheWriter, redisCacheConfiguration);
}
/**
* 自定义缓存规则
*/
private static class CustomRedisCacheManager extends RedisCacheManager {
public CustomRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
super(cacheWriter, defaultCacheConfiguration);
}
@Override
protected RedisCache createRedisCache(String name, RedisCacheConfiguration cacheConfig) {
String[] cells = StringUtils.delimitedListToStringArray(name, "#");
name = cells[0];
if (cells.length > 1) {
long ttl = Long.parseLong(cells[1]);
// 根据传参设置缓存失效时间,默认单位是秒
cacheConfig = cacheConfig.entryTtl(Duration.ofSeconds(ttl));
}
return super.createRedisCache(name, cacheConfig);
}
}
}
如果没有连接池的话, SpringBoot2.0 版本之后 spring-boot-starter-data-redis 的底层默认使用了 Lettuce 来操作 redis ,早期的版本使用的是Jedis
使用 @Primary 注解来标注我们自定义的 Lettuce 连接池为主连接池
/**
* @author yukun.yan
* @description LettuceConnectionAutoConfiguration redis连接池自动配置
* @date 2023/5/5 10:24
*/
@AutoConfigureAfter({RedisAutoConfiguration.class}) // 之后配置链接工厂
@EnableConfigurationProperties({RedisTemplateConfigProperties.class}) // 依赖配置文件
@ConditionalOnBean({RedisTemplate.class, RedisConnectionFactory.class, RedisTemplateConfigProperties.class})
public class LettuceConnectionAutoConfiguration {
/**
* 配置连接池信息
* 容器在缺失 RedisConnectionFactory 配置的时候, 会兜底注入 JedisConnectionFactory
* @see JedisConnectionConfiguration#redisConnectionFactory(org.springframework.beans.factory.ObjectProvider)
*
* @param properties
* @return
*/
@Bean
@Primary
public LettuceConnectionFactory lettuceConnectionFactory(final RedisTemplateConfigProperties properties) {
// 基础配置
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setDatabase(properties.getDatabase());
redisStandaloneConfiguration.setHostName(properties.getHost());
redisStandaloneConfiguration.setPort(properties.getPort());
redisStandaloneConfiguration.setPassword(RedisPassword.of(properties.getPassword()));
// 连接池配置
LettucePoolingClientConfiguration lettuceClientConfiguration = LettucePoolingClientConfiguration
.builder()
.commandTimeout(Duration.ofMillis(properties.getTimeout()))
.poolConfig(getPoolConfig(properties)).build();
// 根据配置和客户端配置创建连接
LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(redisStandaloneConfiguration, lettuceClientConfiguration);
lettuceConnectionFactory.afterPropertiesSet();
return lettuceConnectionFactory;
}
/**
* 获取连接池配置
*
* @param properties
* @return
*/
private GenericObjectPoolConfig getPoolConfig(final RedisTemplateConfigProperties properties) {
GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
genericObjectPoolConfig.setMaxIdle(properties.getMaxIdle());
genericObjectPoolConfig.setMinIdle(properties.getMinIdle());
genericObjectPoolConfig.setMaxTotal(properties.getMaxTotal());
genericObjectPoolConfig.setMaxWaitMillis(properties.getMaxWait());
return genericObjectPoolConfig;
}
}
模仿框架的定义方式, 在 resource 下创建一个 META-INF 包, 并创建 spring.factories 文件
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.kone.sp.common.redis2.config.RedissonAutoConfiguration,\
com.kone.sp.common.redis2.config.RedisCacheAutoConfiguration,\
com.kone.sp.common.redis2.config.RedisTemplateAutoConfiguration,\
com.kone.sp.common.redis2.config.LettuceConnectionAutoConfiguration
对我们的自定义 redis-starter 使用 install 命令, 之后在 maven 仓库里可以看到
在 pom 文件中引入, 并在其他模块的 yml 文件中配置 RedisTemplateConfigProperties 需要的配置参数, 这里要注意前缀和属性名称要和配置类对应上
之后从配置文件中可以看到, 我们在其他模块编写的 yml 配置, 已经在创建连接工厂的时候被读取到