zoukankan      html  css  js  c++  java
  • Spring Boot:通过spring-boot-starter-data-redis源码了解starter和autoconfigure模块-转载

    原文地址:https://blog.csdn.net/yeyinglingfeng/article/details/87790700

    注:本文Spring Boot为2.X版本
    在Spring Boot中,官方提供了spring-boot-autoconfigure包和starter包用来帮助我们简化配置,比如之前要建一个Spring mvc项目,需要我们配置web.xml,dispatcherservlet-servlet.xml,applicationContext.xml等等。而在Spring Boot中只需要在pom中引入

       <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
       </dependency>

        1
        2
        3
        4

    就能完成之前所有的工作了。简直so easy啊。
    但是只会用是不行的,还要知其所以然,本文以官方的starter:spring-boot-starter-data-redis为例,从源码层面上分析整个自动化配置的过程。以期对starter和autoconfigure这两个Spring Boot的核心模块进行梳理。
    了解原理后,我会通过模拟spring-boot-starter-data-redis,并使用Jedis来创建一个处理redis的自定义starter:my-redis-starter。源码下载 点我,最后会详细说明自定义starter的创建过程。

    在Spring Boot中使用默认的redis客户端只需要
    在pom.xml中引入

     <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

        1
        2
        3
        4

    然后在application.properties中配置ip,密码等必要参数

    spring.redis.host=106.15.108.50
    # Redis服务器连接端口
    spring.redis.port=6379
    # Redis服务器连接密码(默认为空)
    spring.redis.password=123456
    #等等

        1
        2
        3
        4
        5
        6

    就可以直接在我们的业务中调用org.springframework.data.redis.core.RedisTemplate来处理缓存的相关操作了。
    一.RedisTemplate的注入

    让我们先来看下RedisTemplate是如何被注入的。
    1.RedisProperties

    在application.properties中ctrl+左击redis的相关配置项,会打开spring-boot-autoconfigure2.0.2.RELEASEspring-boot-autoconfigure-2.0.2.RELEASE.jar中的RedisProperties。
    在这里插入图片描述
    打开org.springframework.boot.autoconfigure.data.redis.RedisProperties.class

    @ConfigurationProperties(prefix = "spring.redis")
    public class RedisProperties {

        /**
         * Database index used by the connection factory.
         */
        private int database = 0;

        /**
         * Connection URL. Overrides host, port, and password. User is ignored. Example:
         * redis://user:password@example.com:6379
         */
        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;

        /**
         * Whether to enable SSL support.
         */
        private boolean ssl;

        /**
         * Connection timeout.
         */
        private Duration timeout;

        private Sentinel sentinel;

        private Cluster cluster;

        private final Jedis jedis = new Jedis();

        private final Lettuce lettuce = new Lettuce();
        ......
    }

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48

    (1) @ConfigurationProperties(prefix = "spring.redis") 设置绑定属性的前缀,然后看下前面的一些属性,是不是很眼熟?前缀+属性名就是之前在application.properties中配置的,如果我们没有配置端口这种属性,那么这里也会提供部分默认配置。
    当然,只是这些是没办法让Spring Boot在启动时扫描到该类的,所以需要下一个类org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration.class。
    然后我们还能找到

    private final Jedis jedis = new Jedis();
    private final Lettuce lettuce = new Lettuce();

        1
        2

    一般用Java操作redis用的较多几个Java客户端为Jedis,Redisson,Lettuce。这里可知官方提供的spring-boot-starter-data-redis底层是用Jedis/Lettuce实现的,知道了这个我们也能够借鉴这个starter来使用其他的客户端来实现了。
    2.RedisAutoConfiguration

    打开org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration.class

    @Configuration
    @ConditionalOnClass(RedisOperations.class)
    @EnableConfigurationProperties(RedisProperties.class)
    @Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
    public class RedisAutoConfiguration {

        @Bean
        @ConditionalOnMissingBean(name = "redisTemplate")
        public RedisTemplate<Object, Object> redisTemplate(
                RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
            RedisTemplate<Object, Object> template = new RedisTemplate<>();
            template.setConnectionFactory(redisConnectionFactory);
            return template;
        }

        @Bean
        @ConditionalOnMissingBean
        public StringRedisTemplate stringRedisTemplate(
                RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
            StringRedisTemplate template = new StringRedisTemplate();
            template.setConnectionFactory(redisConnectionFactory);
            return template;
        }
    }

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24

    (1)@Configuration常见的配置注解,内部含有一个以上的@Bean,让Spring能扫描到内部的@Bean,当然在Spring Boot中,默认只会扫描到启动类所在包或其下级包的类,所以还会通过其他的设置来让这个类被扫描到,这个后面会详细说明。

        @Configuration用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器。

    (2)@ConditionalOnClass(RedisOperations.class),当存在RedisOperations类时才会进行扫描,这个类什么时候被引入classpath的之后会提到。
    (3)@EnableConfigurationProperties(RedisProperties.class)让RedisProperties 类被扫描到的关键。这时,如果RedisAutoConfiguration被扫描到,则同时也会去扫描RedisProperties类。
    (4)@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })通过@Import注解方式生成类实例并注入Spring容器。

        @Import注解通过导入的方式实现把实例加入springIOC容器中

    让我们打开JedisConnectionConfiguration简要看下

    @Configuration
    @ConditionalOnClass({ GenericObjectPool.class, JedisConnection.class, Jedis.class })
    class JedisConnectionConfiguration extends RedisConnectionConfiguration {
        private final RedisProperties properties;
        private final List<JedisClientConfigurationBuilderCustomizer> builderCustomizers;
        JedisConnectionConfiguration(RedisProperties properties,
                ObjectProvider<RedisSentinelConfiguration> sentinelConfiguration,
                ObjectProvider<RedisClusterConfiguration> clusterConfiguration,
                ObjectProvider<List<JedisClientConfigurationBuilderCustomizer>> builderCustomizers) {
            super(properties, sentinelConfiguration, clusterConfiguration);
            this.properties = properties;
            this.builderCustomizers = builderCustomizers
                    .getIfAvailable(Collections::emptyList);
        }
        @Bean
        @ConditionalOnMissingBean(RedisConnectionFactory.class)
        public JedisConnectionFactory redisConnectionFactory() throws UnknownHostException {
            return createJedisConnectionFactory();
        }
        ......
    }

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21

        @Import注解会通过JedisConnectionConfiguration构造方法将JedisConnectionConfiguration的实例注入到Spring容器中,这里有一个RedisProperties参数,实际上就是在(3)中注入的RedisProperties,这样JedisConnectionConfiguration就获得了RedisProperties,也就获得了之前我们在application.propertie中配置的redis服务器连接属性。
        通过@Configuration和@Bean的定义可知,会扫描到redisConnectionFactory()方法并返回实体,并注入到Spring容器,对应的类为RedisConnectionFactory。(JedisConnectionConfiguration实现了RedisConnectionFactory接口,所以可以这样)

        @Bean
        @ConditionalOnMissingBean(RedisConnectionFactory.class)
        public JedisConnectionFactory redisConnectionFactory() throws UnknownHostException {
            return createJedisConnectionFactory();
        }

        private JedisConnectionFactory createJedisConnectionFactory() {
            JedisClientConfiguration clientConfiguration = getJedisClientConfiguration();
            if (getSentinelConfig() != null) {
                return new JedisConnectionFactory(getSentinelConfig(), clientConfiguration);
            }
            if (getClusterConfiguration() != null) {
                return new JedisConnectionFactory(getClusterConfiguration(),
                        clientConfiguration);
            }
            return new JedisConnectionFactory(getStandaloneConfig(), clientConfiguration);
        }
        private JedisClientConfiguration getJedisClientConfiguration() {
            JedisClientConfigurationBuilder builder = applyProperties(
                    JedisClientConfiguration.builder());
            RedisProperties.Pool pool = this.properties.getJedis().getPool();
            if (pool != null) {
                applyPooling(pool, builder);
            }
            if (StringUtils.hasText(this.properties.getUrl())) {
                customizeConfigurationFromUrl(builder);
            }
            customize(builder);
            return builder.build();
        }
            1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11
            12
            13
            14
            15
            16
            17
            18
            19
            20
            21
            22
            23
            24
            25
            26
            27
            28
            29
            30

        ① getJedisClientConfiguration()方法,该方法从之前注入的RedisProperties中获取了 Jedis客户端连接池。
        ②createJedisConnectionFactory会根据配置的redis参数判断用单机/哨兵/集群模式来创建JedisConnectionFactory实例。

    总结:创建并注入了JedisConnectionFactory实例,JedisConnectionFactory实例中包含有Jedis的客户端连接池,之后就能用其创建连接了。

    (5)redisTemplate方法

    @Bean
    @ConditionalOnMissingBean(name = "redisTemplate")
    public RedisTemplate<Object, Object> redisTemplate(
            RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

        1
        2
        3
        4
        5
        6
        7
        8

    终于找到注入redisTemplate的地方了= =。

        这是个被@Bean注解的方法,因此会被Spring扫描并注入。
        @ConditionalOnMissingBean(name = "redisTemplate")当Spring容器中不存在RedisTemplate实例时才会进行扫描注入,很明显是为了防止重复注入。
        该方法有一个RedisConnectionFactory参数。
        而我们知道(4)中redisConnectionFactory方法最后会注入一个JedisConnectionFactory实例,而JedisConnectionFactory又是继承于RedisConnectionFactory。同志们,你们懂我的意思了吧∠( ᐛ 」∠)_。

    总结:该方法会将先前注入的redisConnectionFactory赋给新建的redisTemplate实例,
    然后将redisTemplate实例注入Spring容器。

    但是这里出现一个问题了
    开始时通过@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })注入了Lettuce和Jedis两个连接配置实例,
    而这两个中又都已@Bean的形式注入了JedisConnectionFactory和LettuceConnectionFactory两个实例(这两个实例的类又都是继承于
    RedisConnectionFactory的),并且注入时都是对应RedisConnectionFactory类的。那么redisTemplate方法最后是使用哪个实例来创建RedisTemplate的呢?
    在这里插入图片描述
    通过debug我们知道实际用的是LettuceConnectionFactory实例。
    这么看是按照@Import中的排序来的,
    这里LettuceConnectionConfiguration在前,所以会先扫描LettuceConnectionConfiguration。相关代码

        @Bean
        @ConditionalOnMissingBean(RedisConnectionFactory.class)
        public LettuceConnectionFactory redisConnectionFactory(
                ClientResources clientResources) throws UnknownHostException {
            LettuceClientConfiguration clientConfig = getLettuceClientConfiguration(
                    clientResources, this.properties.getLettuce().getPool());
            return createLettuceConnectionFactory(clientConfig);
        }

        1
        2
        3
        4
        5
        6
        7
        8

    LettuceConnectionConfiguration中会创建LettuceConnectionFactory实例,并将其注入为redisConnectionFactory类的实例,
    然后在JedisConnectionConfiguration中的类似代码:

        @Bean
        @ConditionalOnMissingBean(RedisConnectionFactory.class)
        public JedisConnectionFactory redisConnectionFactory() throws UnknownHostException {
            return createJedisConnectionFactory();
        }

        1
        2
        3
        4
        5

    也会创建一个JedisConnectionFactory 实例,并将其注入为redisConnectionFactory类的实例。
    双方都有@ConditionalOnMissingBean(RedisConnectionFactory.class)约束,所以当LettuceConnectionConfiguration中RedisConnectionFactory类被注入了对应的实例后,JedisConnectionConfiguration对应的代码就不会再执行了,所以最后RedisConnectionFactory类的实例实际上是LettuceConnectionFactory。
    只要把@Import中的顺序换一下就能改变RedisConnectionFactory类的实例了。

    可能有的童鞋会问,“如果把@ConditionalOnMissingBean(RedisConnectionFactory.class)去掉呢?”
    这样的话JedisConnectionConfiguration中的@Bean是否能覆盖掉之前的那个,实现重复注入呢?抱歉,这样会报错(大致意思是RedisConnectionFactory已经有一个对应的bean了,不能再注入第二个)。

    这个我们可以做个小测试
    新建一个Spring Boot项目,勾一个web即可。
    新建B,C类。
    B,C类用来模拟LettuceConnectionConfiguration和JedisConnectionConfiguration类
    这里类上没有添加@Configuration注解也是为了不被Spring扫描到,然后通过@Import才会进行注入。

    public class B{
        @Bean
        @ConditionalOnMissingBean(TestInfoA.class)
        public TestInfoB testInfoA() {
            return new TestInfoB();
        }
    }

    public class C{
        @Bean
        @ConditionalOnMissingBean(TestInfoA.class)
        public TestInfoC testInfoA() {
            return new TestInfoC();
        }
    }

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15

    新建
    TestInfoA,TestInfoB,TestInfoC。其中TestInfoA为接口,TestInfoB和TestInfoC都实现了TestInfoA。用来模拟RedisConnectionFactory接口,JedisConnectionFactory和LettuceConnectionFactory。

    public interface TestInfoA {
    }

    public class TestInfoB implements TestInfoA{
    }

    public class TestInfoC implements TestInfoA{
    }

        1
        2
        3
        4
        5
        6
        7
        8

    新建TestInfo,模拟RedisTemplate

    public class TestInfo {
        private TestInfoA info;

        public TestInfoA getInfo() {
            return info;
        }

        public void setInfo(TestInfoA info) {
            this.info = info;
        }
    }

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11

    新建TestConfig,用来模拟RedisAutoConfiguration

    @Configuration
    @Import({B.class,C.class})
    public class TestConfig {
        @Bean
        @ConditionalOnMissingBean(name = "testInfo")
        public TestInfo testInfo(TestInfoA param) {
            TestInfo info = new TestInfo();
            info.setInfo(param);
            return info;
        }
    }

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11

    为了更好的展示,所以这里的结构完全仿照redis-starter的,实际上也不用那么复杂就是了,然后在TestConfig中的testInfo方法中打个断点,为了看TestInfoA实际上是TestInfoB还是TestInfoC类,运行。
    在这里插入图片描述
    可以看到实际上是TestInfoB,而@import中也是B在前。
    然后改成@Import({C.class,B.class})
    然后结果
    在这里插入图片描述
    可以看出是按照出现在@Improt中的顺序来注入的。
    然后测试下把B,C类的@ConditionalOnMissingBean(TestInfoA.class)注释掉
    报错

        The bean ‘testInfoA’, defined in class path resource
        [com/my/startingProcedure/my/C.class], could not be registered. A bean
        with that name has already been defined in class path resource
        [com/my/startingProcedure/my/B.class] and overriding is disabled.

    总结:redisTemplate方法中的RedisConnectionFactory其实是LettuceConnectionFactory。
    然后我们就可以通过这个注入的RedisTemplate来操作redis了。
    3. spring-boot-autoconfigure

        Spring Boot可以依据classpath里面的依赖内容来自动配置bean到IOC容器。
        但是要开启这个自动配置功能需要添加@EnableAutoConfiguration注解。

    上面指的自动配置功能事实上就是spring-boot-autoconfigure模块
    然后让我们打开一个Spring Boot项目的启动项,是否注意到有一个@SpringBootApplication注解,这个是默认就有的。然后点开

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @SpringBootConfiguration
    @EnableAutoConfiguration
    @ComponentScan(excludeFilters = {
            @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
            @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
    public @interface SpringBootApplication {
    }

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11

    发现@EnableAutoConfiguration,也就是说Spring Boot是默认开启自动配置功能的,即spring-boot-autoconfigure模块是被默认引用的。
    然后让我们看下spring-boot-autoconfigure.jar中的该文件
    在这里插入图片描述
    相信能够找到RedisAutoConfiguration
    在这里插入图片描述
    在这里插入图片描述
    EnableAutoConfiguration是不是和刚才讲到的注解一模一样呢?_(:з」∠*)_。

    这里还涉及到了Spring Boot的启动过程

    public ConfigurableApplicationContext run(String... args) {
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            ConfigurableApplicationContext context = null;
            Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
            configureHeadlessProperty();
            SpringApplicationRunListeners listeners = getRunListeners(args);
            listeners.starting();
            try {
                ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                        args);
                ConfigurableEnvironment environment = prepareEnvironment(listeners,
                        applicationArguments);
                configureIgnoreBeanInfo(environment);
                Banner printedBanner = printBanner(environment);
                context = createApplicationContext();
                exceptionReporters = getSpringFactoriesInstances(
                        SpringBootExceptionReporter.class,
                        new Class[] { ConfigurableApplicationContext.class }, context);
                prepareContext(context, environment, listeners, applicationArguments,
                        printedBanner);
                refreshContext(context);
                afterRefresh(context, applicationArguments);
                stopWatch.stop();
                if (this.logStartupInfo) {
                    new StartupInfoLogger(this.mainApplicationClass)
                            .logStarted(getApplicationLog(), stopWatch);
                }
                listeners.started(context);
                callRunners(context, applicationArguments);
            }
            catch (Throwable ex) {
                handleRunFailure(context, ex, exceptionReporters, listeners);
                throw new IllegalStateException(ex);
            }

            try {
                listeners.running(context);
            }
            catch (Throwable ex) {
                handleRunFailure(context, ex, exceptionReporters, null);
                throw new IllegalStateException(ex);
            }
            return context;
        }

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45

    在Spring Boot启动时,会在refreshContext(context);阶段完成配置类的解析、各种BeanFactoryPostProcessor和BeanPostProcessor的注册、国际化配置的初始化、web内置容器的构造等等,这时会读取pom中引入jar的配置文件/META-INF/spring.factories,所以这里EnableAutoConfiguration下的所有类都会被实例化并注入Spring容器。
    所以RedisAutoConfiguration就被扫描到了。

    再来回顾下RedisAutoConfiguration

    @Configuration
    @ConditionalOnClass(RedisOperations.class)
    @EnableConfigurationProperties(RedisProperties.class)
    @Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
    public class RedisAutoConfiguration {
    }

        1
        2
        3
        4
        5
        6

    会发现@ConditionalOnClass(RedisOperations.class),如果想要被扫描到还需要在classpath中存在RedisProperties.class,这个又在哪呢?
    点开RedisProperties.class,会发现其存在于spring-data-redis-2.1.3.RELEASE.jar中
    在这里插入图片描述
    但我们貌似没有引入spring-data-redis,这个是哪里来的呢?先让我们先看下之前pom中的引入,

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

        1
        2
        3
        4

    然后ctrl+左键继续查看
    在这里插入图片描述
    能看到版本了,再继续查看。
    在这里插入图片描述
    在这里插入图片描述
    发现其会引入spring-data-redis,因此只有当我们在pom中引入spring-boot-starter-data-redis时,RedisAutoConfiguration才会真正的开启扫描。这也体现了Spring Boot的即插即用和方便快捷的自动配置。
    然后下面还有一个io.lettuce,而之前在RedisAutoConfiguration中我们知道redisTemplate方法最终会把一个LettuceConnectionFactory实例注入Spring容器,而在这里实际上就已经大致表明了RedisAutoConfiguration会使用Lettuce客户端了。
    4.总结

    当要使用Spring Boot提供的redis客户端功能时,注入RedisTemplate的流程大致如下。
    1.pom中引入spring-boot-starter-data-redis,并配置application.properties。
    2.pom会根据spring-boot-starter-data-redis来引入spring-data-redis。
    3.spring-data-redis中包含RedisOperations类。
    4.启动Spring Boot,在refreshContext(context);中会初始化beanFactory,读取配置信息,初始化Spring容器,注入bean。因为@EnableAutoConfiguration开启的关系,会读取配置中EnableAutoConfiguration相关的类,并实例化注入Spring 容器。
    5.根据配置文件扫描到RedisAutoConfiguration。当RedisOperations存在时RedisAutoConfiguration才会被扫描。
    6.通过@EnableConfigurationProperties(RedisProperties.class)和@ConfigurationProperties(prefix = "spring.redis"),把application.properties中的对应属性进行绑定,并注入RedisProperties配置类。
    7.RedisAutoConfiguration中的@Import会引入LettuceConnectionConfiguration和JedisConnectionConfiguration
    8.LettuceConnectionConfiguration和JedisConnectionConfiguration被扫描,扫描到内部的@Bean,使用上一步中注入的RedisProperties bean作为参数来实例化LettuceConnectionFactory和JedisConnectionFactory,并以RedisConnectionFactory类注入Spring容器。
    8.扫描并注入RedisAutoConfiguration类内的@Bean,其中会使用RedisConnectionFactory bean作参数实例化RedisTemplate。
    9.将RedisTemplate实例注入。
    10.然后就能通过引用RedisTemplate来操作redis了。
    五.创建自定义starter

    my-redis-starter项目代码下载 点我
    完成后的项目结构
    在这里插入图片描述
    1.新建maven项目

    在这里插入图片描述
    结构最简单的就行
    在这里插入图片描述
    2.引入spring-boot-autoconfigure

    在pom中引入spring-boot-autoconfigure jar。

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
            <version>1.5.4.RELEASE</version>
        </dependency>
    </dependencies>

        1
        2
        3
        4
        5
        6
        7

    因为需要用到Spring Boot的autoconfigure功能进行自动化配置。
    3.引入相关外部jar

    引入jedis-2.9.0.jar,commons-pool2-2.4.2.jar
    因为相关的jar已经放在项目下了(/src/WEB-INF/lib),所以直接引入即可。
    在这里插入图片描述
    4.新建配置类

    新建MyRedisProperties.java,用来绑定配置文件中的属性值。

    @ConfigurationProperties(prefix = "my.redis")
    public class MyRedisProperties {
        //Redis服务器IP
        private String ADDR = "192.168.0.41";
           
        //Redis的端口号
        private int PORT = 6379;
        
        //访问密码
        private String AUTH = "admin";
        
        public String getADDR() {
            return ADDR;
        }

        public void setADDR(String aDDR) {
            ADDR = aDDR;
        }

        public int getPORT() {
            return PORT;
        }
        
        public void setPORT(int pORT) {
            PORT = pORT;
        }

        public String getAUTH() {
            return AUTH;
        }

        public void setAUTH(String aUTH) {
            AUTH = aUTH;
        }

        /**
         * 初始化Redis连接池
         */
        public JedisPool getJedisPool(){
            JedisPoolConfig config = new JedisPoolConfig();
            //省略具体设置
            
            JedisPool myJedisPool = new JedisPool(config, ADDR, PORT, TIMEOUT, AUTH);
            
            return myJedisPool;
        }
    }

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47

    这里把一些参数设置省略了,只保留了最重要的url,port,password3个属性,详细可下载源码查看。
    当前类会将application.properties中的my.redis.ADDR,my.redis.PORT ,my.redis.AUTH 绑定到对应的属性中,并且提供了一个创建连接池的方法。
    5.新建redis操作模板类

    新建JedisTemplete.java

    public class JedisTemplete {
        private JedisPool jedisPool;
            
        public JedisPool getJedisPool() {
            return jedisPool;
        }

        public void setJedisPool(JedisPool jedisPool) {
            this.jedisPool = jedisPool;
        }

        /**
         * 获取Jedis实例
         * @return
         */
        public Jedis getJedis() {
            try {
                if (jedisPool != null) {
                    Jedis resource = jedisPool.getResource();
                    return resource;
                } else {
                    return null;
                }
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }

        /**
         * 释放jedis资源
         * @param jedis
         */
        public void close(final Jedis jedis) {
            if (jedis != null) {
                jedis.close();
            }
        }
        
        public String getValue(String key) {
            return getJedis().get(key);
        }
    }

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43

    使用连接池连接redis服务器,并提供了一个根据key查询value的方法。
    6.新建自动化配置类

    新建MyRedisAutoConfiguration.java

    @Configuration
    @ConditionalOnClass(MyRedisProperties.class)
    @EnableConfigurationProperties(MyRedisProperties.class)
    public class MyRedisAutoConfiguration {
        
        @Bean
        @ConditionalOnMissingBean(name="jedisTemplete")
        public JedisTemplete jedisTemplete(MyRedisProperties myRedisProperties) {
            JedisTemplete jedisTemplete = new JedisTemplete();
            
            jedisTemplete.setJedisPool(myRedisProperties.getJedisPool());
            
            return jedisTemplete;
        }
    }

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15

        @ConditionalOnClass(MyRedisProperties.class),当存在MyRedisProperties.class时才会进行扫描。
        @EnableConfigurationProperties(MyRedisProperties.class),进行属性绑定,当当前类被扫描时,才会去创建MyRedisProperties实例,并绑定application.properties中对应的属性,然后注入Spring容器。
        jedisTemplete方法,因为注解@Bean,所以在MyRedisAutoConfiguration 被扫描到时,也会扫描该方法并生成实例注入Spring容器。
        public JedisTemplete jedisTemplete(MyRedisProperties myRedisProperties)
        myRedisProperties参数,就是通过@EnableConfigurationProperties(MyRedisProperties.class)注入的MyRedisProperties类实例。
        通过myRedisProperties获取到JedisPool。
        创建JedisTemplete 实例,并将连接池赋给它。
        返回创建的JedisTemplete 实例,因为注解@Bean,所以会将其注入Spring容器,对应类为JedisTemplete。

    7.新建spring.factories配置文件

    新建src/main/resources/META-INF/spring.factories文件。

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.auto.config.MyRedisAutoConfiguration

        1

    写入刚才创建的MyRedisAutoConfiguration类全名,注意前半段为EnableAutoConfiguration,这样在Spring Boot启动时,才会在配置文件中扫描到MyRedisAutoConfiguration,进而去扫描该类。
    8.测试

    (1)新建一个Spring Boot项目,在pom中引入刚才创建的maven项目。

    <dependency>
        <groupId>com.my.redis</groupId>
        <artifactId>my-redis-starter</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>

        1
        2
        3
        4
        5

    (2)创建一个Controller,用来查看jedisTemplete是否被注入,能否连接到redis服务器并获取到数据。

    @RestController
    @RequestMapping(value="/print")
    public class PrintController {
        @Autowired
        private JedisTemplete jedisTemplete;
        
        @ResponseBody
        @RequestMapping(value="/getRedis")
        public String getRedis() {
            return jedisTemplete.getValue("123");
        }
    }

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12

    (3)配置application.properties
    添加配置信息

    my.redis.ADDR=redis服务器的url
    my.redis.PORT=端口
    my.redis.AUTH=密码

        1
        2
        3

    (4)然后让我们先在redis中存一个值,key:123,value:321。
    在这里插入图片描述
    (5)启动测试
    在这里插入图片描述
    成功获取。

    总结:自定义starter时
    1.首先需要在pom中引入spring-boot-autoconfigure
    2.新建XXXAutoConfiguration,然后引入需要注入的类。
    3.配置spring.factories。

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=XXXAutoConfiguration类全路径
    //当有多个时
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=/
    A,/
    B,/
    C
    ————————————————
    版权声明:本文为CSDN博主「团子ing」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/yeyinglingfeng/article/details/87790700

  • 相关阅读:
    Vagrant安装virtualbox
    博客园页面定制CSS
    Node.js安装详细步骤教程(Windows版)
    已完成题目
    20.10.07总结
    纯IPv6主机上搭建网站
    Azure Database for MySQL Connection Security -(3) Private Endpoint
    Azure Database for MySQL Connection Security -(2) VNET rules and service endpoint
    Azure Database for MySQL Connection Security -(1) Public network access and Firewall rules
    python教程:删除列表中某个元素的3种方法
  • 原文地址:https://www.cnblogs.com/jimcsharp/p/13271840.html
Copyright © 2011-2022 走看看