zoukankan      html  css  js  c++  java
  • SpringBoot高级-自动配置原理剖析一

    前言:在使用SpringBoot开发时,发现它非常的方便,很多东西就是导入依赖,拿来即用,并不清楚其中的原理是什么,所有心理多少会有些疑惑,了解自动配置原理之后,我们可以更好的使用SpringBoot,并且学习其中的设计思想。

    示例1:我们对SpringBoot引导类进行修改,通过IOC容器,获取redisTemplate这个Bean,看能否获取?

    @SpringBootApplication
    public class SpringbootConditionApplication {
    
        public static void main(String[] args) {
            //启动SpringBoot的应用,返回Spring的IOC容器
            ConfigurableApplicationContext context = SpringApplication.run(SpringbootConditionApplication.class, args);
    
            //获取Bean,redisTemplate
            Object redisTemplate = context.getBean("redisTemplate");
            System.out.println(redisTemplate);
        }
    }
    

    控制台结果:可知默认的时候,SpringBoot不会为我们加载redisTemplate这个Bean。

    下一步,我们在pom.xml中加入redis的起步依赖

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

    再次启动程序:可以成功的获取redisTemplate的Bean

    小结:通过上述示例,我们可以了解到,通过添加依赖,IOC容器就可以获取到相关(redisTemplate)的Bean,由此就可以使用具体的方法了。SpringBoot具体是怎么实现的呢?我们继续往下做示例。

    重点:Condition是在Spring4.0增加的条件判断功能,通过这个功能,SpringBoot可以实现选择性的创建Bean操作。

    示例2:实现在Spring容器中有一个User的Bean。

    1、新建一个User类

    /**
     * @description:User类:属性略
     * @date: 2020/10/9 15:25
     * @author: winson
     */
    public class User {
    }
    

    2、新建一个User的配置类,实现Bean注册到Spring容器中

    /**
     * @description:User配置类
     * @date: 2020/10/9 15:25
     * @author: winson
     */
    @Configuration
    public class UserConfig {
        @Bean
        public User user() {
            return new User();
        }
    }
    

    3、测试user的Bean是否成功注入到IOC容器中

    @SpringBootApplication
    public class SpringbootConditionApplication {
    
        public static void main(String[] args) {
            //启动SpringBoot的应用,返回Spring的IOC容器
            ConfigurableApplicationContext context = SpringApplication.run(SpringbootConditionApplication.class, args);
    
            /*//获取Bean,redisTemplate
            Object redisTemplate = context.getBean("redisTemplate");
            System.out.println(redisTemplate);*/
    
            Object user = context.getBean("user");
            System.out.println(user);
        }
    }
    

    4、结果:测试成功。

    接着示例2,在此基础上做示例3。

    示例3:示例2完成了自动注入user的Bean,即使我们不想要user的Bean,它还是存在于IOC容器中了,现在我们将程序继续改造,实现当导入Jedis坐标后,才加载user的Bean,没导入,IOC容器中不加载user的Bean。这里我们先介绍一个注解@Conditional


    All classes that must in order for the component to be registered.:注册组件必须使用的所有类。
    使用@Conditional注解的类,必须按照条件泛型要求,即:泛型条件类,必须Condition接口的子类

    此时我们再来看Condition接口,该接口有一个方法matches(),返回值是一个boolean类型的

    由此可知,使用@Conditional注解时,参数(类)可以为一个数组,此数组中的类必须是Condition接口的子类(实现此接口并重写matches()方法,如果matches()返回的是true,@Conditional注解生效,否则不生效),由此我们就可以继续做测试了。
    1、修改UserConfig配置类

    /**
     * @description:User配置类
     * @date: 2020/10/9 15:25
     * @author: winson
     */
    @Configuration
    public class UserConfig {
    
        //添加@Conditional注解
        @Conditional(ClassCondition.class)
        @Bean
        public User user() {
            return new User();
        }
    
    }
    

    2、新建一个条件类,该条件类必须实现Condition接口,并重写其matches()方法

    /**
     * @description:条件类
     * @date: 2020/10/9 16:24
     * @author: winson
     */
    public class ClassCondition implements Condition {
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            return false;
        }
    }
    

    3、再次启动程序,测试结果可知,当条件类ClassCondition返回false后,我们再从IOC容器中获取user类的Bean,就会失败。

    4、再修改条件类ClassCondition,使其返回true。

    public class ClassCondition implements Condition {
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            return true;
        }
    }
    

    5、重启程序可知,又可以获取user的Bean了

    6、回到示例3的需求:实现当导入Jedis坐标后,才加载user的Bean,没导入,IOC容器中不加载user的Bean。
    6.1、修改ClassCondition类,思路见代码注释

    /**
     * @description:条件类
     * @date: 2020/10/9 16:24
     * @author: winson
     */
    public class ClassCondition implements Condition {
        //1、需求:导入Jedis坐标后创建Bean
        //思路:判断redis.clients.jedis.Jedis文件是否存在
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            boolean flag = true;
            try {
                Class<?> cls = Class.forName("redis.clients.jedis.Jedis");
            } catch (ClassNotFoundException e) {
                flag = false;
            }
            return flag;
        }
    }
    

    7、测试
    7.1、pom.xml中先不导入Jedis依赖

    7.2、导入Jedis依赖

            <dependency>
                <groupId>Jedis</groupId>
                <artifactId>jedis</artifactId>
                <version>2.9.0</version>
            </dependency>
    

    7.3、测试结果,导入Jedis依赖后,成功从IOC容器中获取到了user的Bean

  • 相关阅读:
    laravel 服务提供者
    乐观锁和悲观锁
    MySQL索引原理及慢查询优化
    Laravel Session保存机制和terminate中间件
    laravel session踩坑
    理解 JavaScript 的 async/await(转)
    知识点
    js异步
    Office使用笔记
    YUM常用命令
  • 原文地址:https://www.cnblogs.com/elnimo/p/13785979.html
Copyright © 2011-2022 走看看