• SpringBoot的Condition注解


    参考资料


    视频链接

    运行环境


    • win10
    • IDEA专业版
    • SpringBoot 2.6.2

    SpringBoot 自动配置原理


    一、Condition


    Condition 是在 Spring 4.0 增加的条件判断功能,作用是实现选择性的创建Bean操作

    思考:
    SpringBoot是如何知道要创建哪个 Bean的?比如SpringBoot是如何知道要创建 RedisTemplate的?
    需求:
    在Spring的IOC容器中有一个User的Bean,现要求

    0x001 导入 Jedis 坐标后,加载该Bean,没导入则不加载

    测试的项目结构

    在这里插入图片描述

    User.java

    package com.uni.springbootcondition.domain;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class User {
        private int id;
        private String username;
        private String password;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    ClassCondition.java

    package com.uni.springbootcondition.condition;
    
    import org.springframework.context.annotation.Condition;
    import org.springframework.context.annotation.ConditionContext;
    import org.springframework.core.type.AnnotatedTypeMetadata;
    
    public class ClassCondition implements Condition {
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    
            // 需求:导入Jedis坐标后创建Bean
            // 思路:判断Redis.clients.jedis.Jedis.class文件是否存在
            boolean flag = true;
            try {
                Class<?> cls = Class.forName("redis.clients.jedis.Jedis");
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
                flag = false;
            }
            return flag;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    UserConfig.java

    package com.uni.springbootcondition.config;
    
    import com.uni.springbootcondition.condition.ClassCondition;
    import com.uni.springbootcondition.domain.User;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Conditional;
    import org.springframework.context.annotation.Configuration;
    
    
    @Configuration
    public class UserConfig {
        @Bean
        @Conditional(ClassCondition.class)
        public User user(){
            return new User();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    SpringbootConditionApplication.java

    package com.uni.springbootcondition;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
    import org.springframework.context.ConfigurableApplicationContext;
    
    @SpringBootApplication
    public class SpringbootConditionApplication {
    
        public static void main(String[] args) {
    
            // 启动 SpringBoot的应用,返回 Spring的 IOC容器
            ConfigurableApplicationContext context = SpringApplication.run(SpringbootConditionApplication.class, args);
    
            Object user = context.getBean("user");
            System.out.println(user);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    0x010 将类的判断定义为动态的。判断哪个字节码文件存在可以动态指定。

    项目结构:

    在这里插入图片描述

    ClassCondition.java

    package com.uni.springbootcondition.condition;
    
    import org.springframework.context.annotation.Condition;
    import org.springframework.context.annotation.ConditionContext;
    import org.springframework.core.type.AnnotatedTypeMetadata;
    
    import java.util.Map;
    
    public class ClassCondition implements Condition {
        /**
         * @param context 上下文对象。 用于获取环境, IOC容器,ClassLoader对象
         * @param metadata 注解元对象。 可以用于获取注解定义的属性值
         * @return
         */
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            // 需求:导入通过注解属性值value指定坐标后创建Bean
            // 思路:获取注解属性值
            Map<String, Object> map = metadata.getAnnotationAttributes(ConditionOnClass.class.getName());
            String[] value = (String[])map.get("value");
            boolean flag = true;
            try {
                for (String className : value) {
                    Class<?> cls = Class.forName(className);
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
                flag = false;
            }
            return flag;
        }
    }
    
    
    
    • 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

    ConditionOnClass.java

    package com.uni.springbootcondition.condition;
    
    import org.springframework.context.annotation.Conditional;
    
    import java.lang.annotation.*;
    
    
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Conditional(ClassCondition.class)
    public @interface ConditionOnClass {
        String[] value();
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    UserConfig.java

    package com.uni.springbootcondition.config;
    
    import com.uni.springbootcondition.condition.ClassCondition;
    import com.uni.springbootcondition.condition.ConditionOnClass;
    import com.uni.springbootcondition.domain.User;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Conditional;
    import org.springframework.context.annotation.Configuration;
    
    
    
    @Configuration
    public class UserConfig {
        @Bean
        @ConditionOnClass("redis.clients.jedis.Jedis")
        public User user(){
            return new User();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    其实在SpringBoot中,自动实现了之前的需求

    在这里插入图片描述

    在这里插入图片描述

    源码:ConditionalOnBean.java

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by FernFlower decompiler)
    //
    
    package org.springframework.boot.autoconfigure.condition;
    
    import java.lang.annotation.Annotation;
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    import org.springframework.context.annotation.Conditional;
    
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Conditional({OnBeanCondition.class})
    public @interface ConditionalOnBean {
        Class<?>[] value() default {};
    
        String[] type() default {};
    
        Class<? extends Annotation>[] annotation() default {};
    
        String[] name() default {};
    
        SearchStrategy search() default SearchStrategy.ALL;
    
        Class<?>[] parameterizedContainer() default {};
    }
    
    
    • 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

    当 Spring IOC容器 存在某个Bean时才执行

    比如 Redis的自动配置功能,源码org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguraion.java

    
    @Bean
    @ConditionalOnMissingBean(
        name = {"redisTemplate"}
    )
    @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
    
    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        return new StringRedisTemplate(redisConnectionFactory);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    @ConditionalOnMissingBean表示当某个Bean不存在时,源码中第3行,将name属性值指定为redisTemplate

    在第13行也有用到,就是不存在的话就创建一个Redis实例,通过使用注解实现对Bean是否存在的判断,若不存在就进行创建对象的处理,从而实现了自动配置

    0x011 小结

    • 自定义条件

      • 定义条件类:自定义实现 Condition 接口,重写 matches 方法, 在matches方法中进行逻辑判断,返回boolean值。 matches方法的两个参数:1) context:上下文对象,可以获取属性值,获取类加载器,获取BeanFactory等;2) metada:元数据对象,用于获取注解属性
      • 判断条件:在初始化Bean时,使用@Conditional(条件类.class)注解
    • SpringBoot 提供的常用条件注解

      注解名描述
      ConditionalOnProperty判断配置文件中是否有对应属性和值才初始化Bean
      ConditionalOnClass判断环境中是否有对应字节码文件才初始化Bean
      ConditionalOnMissingBean判断环境中没有对应Bean才初始化Bean

    二、切换内置web服务


    源码位置:org.springframework.boot.autoconfigure.web.embedded

    在这里插入图片描述

    EmbeddedWebServerFactoryCustomizerAutoConfiguration.java 源码中自动配置Tomcat部署的实现

    @ConditionalOnClass({Tomcat.class, UpgradeProtocol.class})
    public static class TomcatWebServerFactoryCustomizerConfiguration {
        public TomcatWebServerFactoryCustomizerConfiguration() {
        }
    
        @Bean
        public TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties) {
            return new TomcatWebServerFactoryCustomizer(environment, serverProperties);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    可以观察到它用到了 @ConditionalOnClass 注解,就是当存在Tomcat的类还有另外一个UpgradeProtocol的类才会进行配置,其他的几个部署模式也是这样的。

    所以在SpringBoot中切换内置web服务器,只需要修改pom.xml中的依赖,排除默认的Tomcat包,添加其他的包比如Jetty,就完成了。

    pom.xml

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <!-- 排除 Tomcat 依赖 -->
        <exclusions>
            <exclusion>
                <artifactId>spring-boot-starter-tomcat</artifactId>
                <groupId>org.springframework.boot</groupId>
            </exclusion>
        </exclusions>
    </dependency>
    <!-- 引入 Jetty的 依赖 -->
    <dependency>
        <artifactId>spring-boot-starter-jetty</artifactId>
        <groupId>org.springframework.boot</groupId>
    </dependency>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    开启SpringBoot后,通过查看启动信息可以发现,内置的web服务器切换成功

    在这里插入图片描述

    三、Enable*注解


    SpringBoot 提供了很多 Enable开头的注解,这些注解都是用于动态启动某些功能的。而其底层原理是使用@Import注解导入一些配置类,实现Bean的动态加载。

    思考:SpringBoot工程是否可以直接获取Jar包中定义的Bean?

    测试的项目结构
    在这里插入图片描述

    其中 springboot-enable-other用于模拟其他jar包,先在其放到enable项目内部,类似于给项目导入jar包,然后在到外面的SpringBoot项目里获取里面项目的Bean,测试看看能否获取到。

    UserConfig.java

    package com.uni.domain;
    
    public class User {
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    EnableUser.java 模拟一个EnableUser注解,用于导入第三方包User的Bean

    package com.uni.config;
    
    import org.springframework.context.annotation.Import;
    
    import java.lang.annotation.*;
    
    
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import(UserConfig.class)
    public @interface EnableUser {
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    SpringBootEnableAction.java

    package com.uni.springbootenable;
    
    import com.uni.config.EnableUser;
    import com.uni.config.UserConfig;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Import;
    
    /**
     * @ComponentScan 扫描范围:当前引导类所在及其子包
     * 1. 使用 @ComponentScan 扫描 com.uni.config包
     * 2. 使用 @Import 注解,加载类,这些类都会被 Spring创建,并放入IOC容器
     * 3. 可以对 @Import注解进行封装
     */
    @SpringBootApplication
    //@ComponentScan("com.uni.config")
    //@Import(UserConfig.class)
    @EnableUser
    public class SpringbootEnableApplication {
    
        public static void main(String[] args) {
            ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);
    
            // 获取 Bean
            Object user = context.getBean("user");
            System.out.println(user);
        }
    }
    
    
    • 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

    在@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}
    )}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    其中的 @EnableAutoConfiguration 则实现了自动配置

    这体现了之前 能直接获取 redisTemplate Bean的原因,在pom.xml中导入 redis后,改包提供了类似于之前手写的@EnableUser注解,注解中用到了@Import注解,直接将类注入到了Spring的IOC容器,从而使得SpringBoot能识别到对应的类

    四、Import 注解


    @Enable*底层依赖于 @Import注解导入一些类 ,使用@Import导入的类会被Spring加载到IOC容器中。而@Import提供四种用法

    测试的项目结构:

    在这里插入图片描述

    EnableUser.java

    package com.uni.config;
    
    import org.springframework.context.annotation.Import;
    
    import java.lang.annotation.*;
    
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import(UserConfig.class)
    public @interface EnableUser {
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    MyImportBeanDefinationRegister.java

    package com.uni.config;
    
    import com.uni.domain.User;
    import org.springframework.beans.factory.support.AbstractBeanDefinition;
    import org.springframework.beans.factory.support.BeanDefinitionBuilder;
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
    import org.springframework.core.type.AnnotationMetadata;
    
    public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition();
            registry.registerBeanDefinition("user", beanDefinition);
        }
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    MyImportSelector.java

    package com.uni.config;
    
    import org.springframework.context.annotation.ImportSelector;
    import org.springframework.core.type.AnnotationMetadata;
    
    public class MyImportSelector implements ImportSelector {
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            return new String[]{"com.uni.domain.User", "com.uni.domain.Role"};
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    UserConfig.java

    package com.uni.config;
    
    import com.uni.domain.Role;
    import com.uni.domain.User;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class UserConfig {
        @Bean
        public User user(){
            return new User();
        }
        @Bean
        public Role role(){return new Role();}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    SpringbootEnableApplication.java

    package com.uni.springbootenable;
    
    import com.uni.config.EnableUser;
    import com.uni.config.MyImportBeanDefinitionRegistrar;
    import com.uni.config.MyImportSelector;
    import com.uni.config.UserConfig;
    import com.uni.domain.Role;
    import com.uni.domain.User;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Import;
    import org.springframework.context.annotation.ImportSelector;
    
    import java.util.Map;
    
    /**
     * Import 四种用法
     * 1. 导入Bean
     * 2. 导入配置类
     * 3. 导入ImportSelector的实现类
     * 4. 导入ImportBeanDefinitionRegistrar 类
     */
    @SpringBootApplication
    //@Import(User.class)
    //@Import(MyImportSelector.class)
    @Import(MyImportBeanDefinitionRegistrar.class)
    public class SpringbootEnableApplication {
    
        public static void main(String[] args) {
            ConfigurableApplicationContext context = SpringApplication.run(SpringbootEnableApplication.class, args);
    
            // 第一种方法的测试
    
    //        User user = context.getBean(User.class);
    //        System.out.println(user);
    //        Map map = context.getBeansOfType(User.class);
    //        System.out.println(map);
    
            // 第二、三种方法的测试
    
    //        User user = context.getBean(User.class);
    //        Role role = context.getBean(Role.class);
    //        System.out.println(user + "\n" + role);
    
            // 第四种方法的测试
            System.out.println(context.getBean(User.class));
        }
    }
    
    
    • 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
    • 49
    • 50
    • 51

    @Import 注解的四种用法

    • 导入Bean
    • 导入配置类
    • 导入ImportSelector的实现类
    • 导入ImportBeanDefinitionRegistrar 类

    SpringBoot启动类注解 @SpringBootApplication 是通过上面来实现导入其他类的,步骤依次为:

    • @SpringBootApplication

    • @EnableAutoConfiguration

    • @Import(AutoConfigurationImportSelector.class)

    • interface DeferredImportSelector extends ImportSelector

    五、@EnableAutoConfiguration 注解


    • @EnableAutoConfiguration 注解内部使用 @import(AutoConfigurationImportSelector.class) 来加载配置类
    • 配置文件位置:META-INF/spring.factories, 改配置文件中定义了大量的配置类,当SpringBoot应用启动时,会自动加载这些配置类,初始化Bean

    一层一层地查找源码:

    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述

    通过源码的提示可以看到,自动加载类的配置文件在 META-INF/spring.factories的位置,现在找到org.springframework.boot.autoconfigure中的spring-boot-autoconfigure-2.5.6.jar 可以找到存在 META-INF文件夹,而里面就有一个 spring.factories的文件,如下图

    在这里插入图片描述

    spring.factories部分内容如下

    在这里插入图片描述

    可以发现它包含了Redis类,从而就加载到了容器中,这就是为什么SpringBoot可以直接获取到redisTemplate的原因

    六、案例


    需求:自定义 redis-starter。 要求当导入 redis坐标时,SpringBoot自动创建Jedis的Bean

    导入 MyBatis起步依赖

    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>1.3.2</version>
    </dependency>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    观察原生的jar包实现,里面会提供具体的自动配置类,以及供SpringBoot扫描的spring.factories

    在这里插入图片描述

    实现步骤:

    1. 创建 redis-spring-boot-autoconfigure模块
    2. 创建 redis-spring-boot-starter模块, 依赖 redis-spring-boot-autoconfigure的模块
    3. redis-spring-boot-autoconfigure 模块中初始化 Jedis 的 Bean。并定义 META-INF/spring.factories文件
    4. 在测试模块中引入自定义的redis-starer依赖,测试获取Jedis的Bean,操作Redis

    项目结构
    在这里插入图片描述

    在负责起步依赖的redis-spring-boot-starter模块中,只负责在pom.xml里添加confguration模块的依赖(注:该模块的test类需删除,否则会报错)

    pom.xml

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
    
        <!-- 引入 configuation -->
        <dependency>
            <groupId>com.uni</groupId>
            <artifactId>redis-spring-boot-configuration</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    redis-spring-boot-configuration模块中,负责配置 jedis 模拟 Redis的自动配置(注:该模块的test类需删除,否则会报错)

    pom.xml

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
    
        <!-- 添加 Jedis 依赖 -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
    </dependencies>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    RedisProperties.java 用于Redis连接的配置实体类,将作为Bean注入到Spring 容器中

    package com.uni.redis.config;
    
    import org.springframework.boot.context.properties.ConfigurationProperties;
    
    
    @ConfigurationProperties(prefix="redis")
    public class RedisProperties {
        private String host = "localhost";
        private int port = 6379;
    
        public String getHost() {
            return host;
        }
    
        public void setHost(String host) {
            this.host = host;
        }
    
        public int getPort() {
            return port;
        }
    
        public void setPort(int port) {
            this.port = port;
        }
    }
    
    
    • 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

    RedisAutoConfiguration.java 根据之前注入的redisProperties配置Bean来创建一个Jedis Bean 并注入到容器中

    package com.uni.redis.config;
    
    import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import redis.clients.jedis.Jedis;
    
    @Configuration
    @EnableConfigurationProperties(RedisProperties.class)
    @ConditionalOnClass(Jedis.class)
    public class RedisAutoConfiguration {
        @Bean
        @ConditionalOnMissingBean(name = "jedis")
        public Jedis jedis(RedisProperties redisProperties){
            System.out.println("RedisAutoConfiguration...");
            return new Jedis(redisProperties.getHost(), redisProperties.getPort());
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    根据之前的源码查找,在声明自动配置类后,需要在resources/META-INF下创建一个spring.factories配置文件,用于声明实现了自动扫描的配置类

    spring.factories

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
      com.uni.redis.config.RedisAutoConfiguration
    
    
    • 1
    • 2
    • 3

    至此,已完成了模拟Redis自动配置的模块,现在到另一个SpringBoot模块中,引入实现的redis-spring-boot-starter起步依赖

    pom.xml

        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <scope>test</scope>
            </dependency>
    
            <!-- 自定义的 Redis的starter -->
            <dependency>
                <groupId>com.uni</groupId>
                <artifactId>redis-spring-boot-starter</artifactId>
                <version>0.0.1-SNAPSHOT</version>
            </dependency>
        </dependencies>
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    SpringbootTestApplication.java

    package com.uni.springboottest;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.ConfigurableApplicationContext;
    import redis.clients.jedis.Jedis;
    
    @SpringBootApplication
    public class SpringbootTestApplication {
    
        public static void main(String[] args) {
            ConfigurableApplicationContext context = SpringApplication.run(SpringbootTestApplication.class, args);
            Jedis jedis = context.getBean(Jedis.class);
            jedis.set("role", "uni");
            String role = jedis.get("role");
            System.out.println(role);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    测试结果:

    在这里插入图片描述

    七、收获与总结


    又一次温习了SpringBoot是提供了快速开发Spring项目的作用而不是增强功能。

    本次写了好几个Demo,跟着视频教程,使用注解去实现Bean的注入,以及起步依赖之类的,最主要的还是第一次查看了SpringBoot启动类的注解,了解到里面自动配置的一些机制,第一次了解到可以将类作为参数传给一个注解。

    本次内容比较多,目前笔者没有完全吸收,不过笔者觉得这一次的学习能有助于SpringBoot的使用,至少在将来写项目的时候,可以配置好依赖。

  • 相关阅读:
    爬虫解析——Xpath
    LeetCode233数字1的个数-容易理解的组合数学方法
    python socket编程1 - socket创建参数说明及参考规范
    Solon v1.11.0 发布,Hello Java
    C++初阶-内存管理
    独立站饰品Facebook广告经验分享
    MySQL - 2059 - Authentication plugin ‘caching_sha2_password‘ cannot be loaded
    【华为OD机试真题 python】 城市聚集度【2022 Q4 | 200分】
    用动图详细讲解——栈
    059:mapboxGL监听键盘事件,通过eastTo控制左右旋转
  • 原文地址:https://blog.csdn.net/t194978/article/details/134223270