参考网页

SpringBoot集成Redis的原理





application.yml配置文件中的属性是如何读入到程序中的?



RedisProperties类(org.springframework.boot.autoconfigure.data.redis.RedisProperties



application.properties配置参数示例

spring.redis.database=0

spring.redis.host=192.168.188.7

spring.redis.password=123

spring.redis.port=6379

spring.redis.pool.max-idle=8

spring.redis.pool.min-idle=0

spring.redis.pool.max-active=8

spring.redis.pool.max-wait=-1



application.yml配置参数示例

    spring:

      cache:

        #缓存名称

        #cache-names: guavaDemo

        #缓存最大数量500条, 缓存失效时间 6个小时

        #guava.spec: maximumSize=500,expireAfterWrite=360m

      # REDIS (RedisProperties)  

      redis :

        host : localhost # server host  

        port : 6379 # connection port  

        password : 123

        database : 1

        pool.max-idle : 8 # pool settings ...  

        pool.min-idle : 1

        pool.max-active : 8  

        pool.max-wait : -1  



源码

@ConfigurationProperties(prefix = "spring.redis")

public class RedisProperties {

    /**

     * Database index used by the connection factory.

     */

    private int database = 0;


    /**

     * Redis url, which will overrule host, port and password if set.

     */

    private String url;


    /**

     * Redis server host.

     */

    private String host = "localhost";


    /**

     * Login password of the redis server.

     */

    private String password;


    /**

     * Redis server port.

     */

    private int port = 6379;

    // 其它参数略

}



源码分析

此类被@ConfigurationProperties注解,表示从外部文件(如application.properties)注入属性值。application.properties中的参数会被自动封装到RedisProperties中。



调试--在RedisProperties中打个断点,比如设置database属性值的地方

把database 的属性值设置为1(默认为0,就是Redis数据库的第一个索引的库,1表示第二个索引的库)。在RedisProperties的setDatabase方法上打个断点,如下图

spring redis集群 yaml配置密码_python

启动系统,运行到断点了,如下图

spring redis集群 yaml配置密码_数据库_02

说明系统初始化时,确实运行到了RedisProperties的代码里面,并且根据application.properties文件的配置参数对RedisProperties的属性进行了注入。



RedisAutoConfiguration类



关键部分源码

/**

 * EnableAutoConfiguration Auto-configuration for Spring Data's Redis support.

 *

 */

@Configuration

// 如果有以下三个类,则可以初始化此类

@ConditionalOnClass({ JedisConnection.class, RedisOperations.class, Jedis.class })

// @EnableConfigurationProperties:对RedisProperties执行自动绑定属性值

@EnableConfigurationProperties(RedisProperties.class)

public class RedisAutoConfiguration {


    @Configuration

    //  如果有以下一个类,则可以初始化此类

    @ConditionalOnClass(GenericObjectPool.class)

    protected static class RedisConnectionConfiguration {


        // 初始化JedisConnectionFactory工厂类对象,参数来自RedisProperties

        // 配置参数有:线程池,sentinel,cluster

        @Bean

        @ConditionalOnMissingBean(RedisConnectionFactory.class)

        public JedisConnectionFactory redisConnectionFactory()

            throws UnknownHostException {

            return applyProperties(createJedisConnectionFactory());

        }



    // 配置类

    @Configuration

    protected static class RedisConfiguration {

        // 初始化bean

        @Bean

        @ConditionalOnMissingBean(name = "redisTemplate")

        public RedisTemplate<Object, Object> redisTemplate(

            RedisConnectionFactory redisConnectionFactory)

                throws UnknownHostException {

            RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>();

            template.setConnectionFactory(redisConnectionFactory);

            return template;

        }


        // 初始化bean

        @Bean       

@ConditionalOnMissingBean(StringRedisTemplate.class)

        public StringRedisTemplate stringRedisTemplate(

            RedisConnectionFactory redisConnectionFactory)

                throws UnknownHostException {

            StringRedisTemplate template = new StringRedisTemplate();

            template.setConnectionFactory(redisConnectionFactory);

            return template;

        }


    }



源码分析

此类被@EnableConfigurationProperties注解,所以SpringBoot会触发对RedisProperties执行自动绑定属性值。

此类会自动创建bean对象: redis连接池JedisConnectionFactory和redis模板类(RedisTemplate和StringRedisTemplate)。直接在应用中通过@Autowire就可以注入以上对象。

比如使用RedisTemplate如下

spring redis集群 yaml配置密码_python_03



项目启动类启动时如何关联到配置类RedisAutoConfiguration

启动类

spring redis集群 yaml配置密码_python_04

查看源码@SpringBootApplication被@EnableAutoConfiguration注解

spring redis集群 yaml配置密码_java_05

被@EnableAutoConfiguration注解时,SpringBoot启动时会扫描对应jar包中的META-INF/spring-autoconfigure-metadata.properties文件,并初始化里面的配置的类。查看spring-autoconfigure-metadata.properties,里面配置了上文的RedisAutoConfiguration类,所以我们可以直接在类中注入RedisTemplate

spring redis集群 yaml配置密码_数据库_06



查看我的实际项目中@EnableAutoConfiguration的扫描对象

项目启动时,主启动类被@EnableAutoConfiguration注解,会扫描其引入的jar包中的spring.factories文件。

打开spring.factories文件,可以发现里面有org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration这个类。所以会初始化此类进行相关的配置。

spring redis集群 yaml配置密码_python_07



★小结:application.yml配置文件中的属性是如何读入到程序中的?

1)项目主启动类上被@SpringBootApplication注解,@SpringBootApplication又被@EnableAutoConfiguration注解。@EnableAutoConfiguration会触发扫描项目引入的jar包中的spring.factories文件。主启动类再加上注解@EnableCaching后,这样就会触发初始化类org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration。

2)RedisAutoConfiguration被@EnableConfigurationProperties注解,会触发对RedisProperties执行自动绑定属性值。

3)RedisProperties类被@ConfigurationProperties注解,表示从外部文件(如application.properties)注入属性值。application.properties中的参数会被自动封装到RedisProperties中。



RedisCacheManager是如何被初始化的



总的思路

实际上SpringBoot中所有Bean都有默认的配置和实现,也就是什么都不写就可以直接运行。

但是如果程序员写一个Bean的实现,那么SpringBoot会采用程序员写的。



@EnableCaching和application.yml

缓存管理接口org.springframework.cache.CacheManager,SpringBoot就是通过此类实现缓存的管理。redis对应此接口的实现类是org.springframework.data.redis.cache.RedisCacheManager。下面介绍此类如何生成。

配置application.properties的spring.redis.* 属性后,主启动类加上注解@EnableCaching后,spring会执行RedisAutoConfiguration,初始化RedisTemplate和StringRedisTemplate。

猜测——虽然SpringBoot因为主启动类的@EnableAutoConfiguration注解会扫描引入的jar包的spring.factories文件,但是只有主启动类加上@EnableCaching注解后,诸如RedisAutoConfiguration的缓存类配置对象才会真的被初始化。



★SpringBoot如何对RedisCacheManager做的初始化--RedisCacheConfiguration会将RedisAutoConfiguration生成的RedisTemplate注入方法生成RedisCacheManager



RedisAutoConfiguration关键代码

@Configuration

@ConditionalOnClass({ JedisConnection.class, RedisOperations.class, Jedis.class })

@EnableConfigurationProperties(RedisProperties.class)

public class RedisAutoConfiguration {

/**

 * Standard Redis configuration.

 */

@Configuration

protected static class RedisConfiguration {

    ….

    @Bean

    @ConditionalOnMissingBean(name = "redisTemplate")

    public RedisTemplate<Object, Object> redisTemplate(

        RedisConnectionFactory redisConnectionFactory)

            throws UnknownHostException {

        RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>();

        template.setConnectionFactory(redisConnectionFactory);

        return template;

    }


    @Bean

    @ConditionalOnMissingBean(StringRedisTemplate.class)

    public StringRedisTemplate stringRedisTemplate(

        RedisConnectionFactory redisConnectionFactory)

            throws UnknownHostException {

        StringRedisTemplate template = new StringRedisTemplate();

        template.setConnectionFactory(redisConnectionFactory);

        return template;

    }


}


}



RedisCacheConfiguration关键代码

@Configuration

@AutoConfigureAfter(RedisAutoConfiguration.class)

@ConditionalOnBean(RedisTemplate.class)

@ConditionalOnMissingBean(CacheManager.class)

@Conditional(CacheCondition.class)

class RedisCacheConfiguration {


    private final CacheProperties cacheProperties;


    private final CacheManagerCustomizers customizerInvoker;


    RedisCacheConfiguration(CacheProperties cacheProperties,

        CacheManagerCustomizers customizerInvoker) {

        this.cacheProperties = cacheProperties;

        this.customizerInvoker = customizerInvoker;

    }


    @Bean

    public RedisCacheManager cacheManager(RedisTemplate<Object, Object> redisTemplate) {

        RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);

        cacheManager.setUsePrefix(true);

        List<String> cacheNames = this.cacheProperties.getCacheNames();

        if (!cacheNames.isEmpty()) {

            cacheManager.setCacheNames(cacheNames);

        }

        return this.customizerInvoker.customize(cacheManager);

    }


}



★RedisAutoConfiguration关键代码和RedisCacheConfiguration关键代码的分析

项目主启动类上被@SpringBootApplication注解,@SpringBootApplication又被@EnableAutoConfiguration注解。@EnableAutoConfiguration会触发扫描项目引入的jar包中的spring.factories文件,这样就会初始化类org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration。 RedisAutoConfiguration生成RedisTemplate。RedisCacheConfiguration通过类自身的cacheManager方法注入RedisTemplate并生成RedisCacheManager对象。

以上就是RedisCacheManager对象生成的过程。以下三点:

1)POM文件中引入spring-boot-starter-redis

2)主启动类上加上注解@EnableCaching

3)application.yml中的配置信息

这三点就能保证项目中引入Redis。RedisCacheManager对象由系统自动生成,自己也可以对RedisCacheManager对象做个性化的配置。



如何对RedisCacheManager做个性化的配置



个性化配置RedisCacheManager示例代码

@SpringBootApplication

@EnableCaching // 启动缓存

public class CacheApplication {

    private static final Logger log = LoggerFactory.getLogger(CacheApplication.class);


    public static void main(String[] args) {

        log.info("Start CacheApplication.. ");

        SpringApplication.run(CacheApplication.class, args);


    }


    /**

     * 重新配置RedisCacheManager

     * @param rd

     */

    @Autowired

    public void configRedisCacheManger(RedisCacheManager rd){

        rd.setDefaultExpiration(100L);

    }

}



分析

示例代码只是对key的有效期进行了配置。

也可以直接设计一个@Configuration注解的配置类,在其中使用@Bean注解生成一个RedisCacheManager类。系统也会采用自己写的这个RedisCacheManager。



★总结

Spring(SpringBoot)已经帮我们生成一个RedisCacheManager并进行了配置。我们再可以对这个RedisCacheManager进行二次配置。