SpringBoot自定义spring-boot-redis-starter

目录

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


1. springboot 自定义的 starter 的用途

        Spring Boot Starter 是一种用于简化 Spring Boot 应用程序依赖项管理的方式。它们是一组预定义的依赖项,可以将它们添加到您的应用程序中,以便您可以更轻松地配置和使用 Spring Boot 应用程序中的各种功能。

        自定义 Spring Boot Starter 可以帮助您将自己的库或框架集成到 Spring Boot 应用程序中。通过创建自己的 Starter,您可以将所有必需的依赖项打包到一个单独的模块中,并提供一个简单的配置方式,以便其他开发人员可以轻松地使用您的库或框架。

自定义 Spring Boot Starter 的主要用途包括:

        1. 简化依赖项管理:通过将所有必需的依赖项打包到一个单独的模块中,您可以简化其他开发人员使用您的库或框架的过程。

        2. 提供简单的配置方式:通过提供一个简单的配置方式,您可以使其他开发人员更轻松地配置和使用您的库或框架。

        3. 提供自定义的自动配置:通过提供自定义的自动配置,您可以使您的库或框架更容易地集成到 Spring Boot 应用程序中。

2. 自定义 Spring Boot Starter 可以用于各种场景

        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 等日志框

3. 以自定义 redis-starter 为例

  3.1 自定义读取配置文件类

        自定义 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=

3.2 自定义 RedisTemplateAutoConfiguration

        这里需要用到一个关键的主键, @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 jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        // ObjectMapper om = new ObjectMapper();
        ObjectMapper om = mapperBuilder.build();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        // 如果enableDefaultTyping过期(SpringBoot后续版本过期了)
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // String的序列化配置
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        RedisTemplate template = new RedisTemplate<>();
        template.setConnectionFactory(lettuceConnectionFactory);
        // key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // hash的key也采用String的序列化方式

        template.setHashKeySerializer(stringRedisSerializer);
        // value序列化方式采用jackson
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        // hash的value序列化方式采用jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        // 刷新属性设置
        template.afterPropertiesSet();
        return template;
    }

    @Bean
    public StringRedisTemplate stringRedisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(lettuceConnectionFactory);
        // String 的序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        template.setKeySerializer(stringRedisSerializer);
        template.setValueSerializer(stringRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);
        return template;
    }

} 
  

3.3 自定义 redis 缓存配置

        同样的,  @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);
        }
    }

}

3.4 自定义reids Lettuce 连接池

        如果没有连接池的话, 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;
    }

}

3.6 自定义 spring.factories 文件

        模仿框架的定义方式, 在 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

3.7 在其他模块测试自定义的starter

        对我们的自定义 redis-starter 使用 install 命令, 之后在 maven 仓库里可以看到

SpringBoot自定义spring-boot-redis-starter_第1张图片

在 pom 文件中引入, 并在其他模块的 yml 文件中配置 RedisTemplateConfigProperties 需要的配置参数, 这里要注意前缀和属性名称要和配置类对应上

SpringBoot自定义spring-boot-redis-starter_第2张图片

        之后从配置文件中可以看到, 我们在其他模块编写的 yml 配置, 已经在创建连接工厂的时候被读取到 

SpringBoot自定义spring-boot-redis-starter_第3张图片

SpringBoot自定义spring-boot-redis-starter_第4张图片

你可能感兴趣的:(redis,spring,boot,java)