美文网首页
SpringBoot | 2.1 SpringBoot自动装配原

SpringBoot | 2.1 SpringBoot自动装配原

作者: 多氯环己烷 | 来源:发表于2021-07-18 21:19 被阅读0次

    前言

    前面第一章的内容基本上都是基于注解的Spring内容,想要学好SpringBoot这些Spring基础必不可少。从这章开始就是SpringBoot了,首先学习一下SpringBoot最重要的自动装配原理。

    一句话概括SpringBoot的自动配置原理就是:启动时加载所有,最终按照条件进行装配。

    本小节重点注解结构列图如下:

    @SpringBootApplication:主程序注解;

    • @SpringBootConfiguration:表名核心配置类;
      • @Configuration:表名配置类;
    • @ComponentScan:开启包扫描;
    • @EnableAutoConfiguration:启用自动配置;
      • @AutoConfigurationPackage:自动配置包,指定了自动配置包的默认规则;
        • @Import(AutoConfigurationPackages.Registrar.class):容器中导入Registrar组件;
      • @Import(AutoConfigurationImportSelector.class):【重要】按照条件配装规则@Conditional,按需配置;

    注:在说明注解时,第一点加粗为注解中文含义,第二点为一般加在哪身上,缩进或代码块为示例,如:

    @注解

    • 中文含义
    • 加在哪
    • 其他……
      • 语句示例
      //代码示例
      

    1. 引入配置文件与配置绑定

    在理解自动装配原理之前,还需要知道配置绑定相关注解。

    引入配置文件常用@ImportResource注解:

    @ImportResource

    • 配置引入;
    • 用于类上;
    • 用于引入配置文件,常用于老项目(需要保留大量xml文件的项目);
      • @ImportResource("classpath:beans.xml") 引入beans.xml配置文件。

    配置绑定的核心注解为@ConfigurationProperties,指读取properties文件中的内容,封装进JavaBean中,以供随时使用。

    @ConfigurationProperties

    • 属性配置
    • 用于POJO类上;

    配置绑定有两种形式:

    1.1 @ConfigurationProperties + @EnableConfigurationProperties

    • EnableConfigurationProperties:启用配置属性;
    • POJO类上写@ConfigurationProperties,在配置类上写@EnableConfigurationProperties
    • @EnableConfigurationProperties的两个核心功能:将按照制定规则与配置文件绑定、将组件导入容器中;
      • 例:将Car组件自动注册进容器中:
    //配置类
    @EnableConfigurationProperties(Car.class)
    public class MyConfig {
    }
    
    //POJO类
    @ConfigurationProperties(prefix = "mycar")
    public class Car {
      private String brand;
      private Integer price;
    }
    
    #配置文件
    mycar: 
      brand: 小鹏
      price: 100000
    

    1.2 @ConfigurationProperties + @Component

    • Component:组件注入;

    • POJO类上同时标注这两个注解;

    • @Component表示将POJO类作为组件注册进容器中,只有在容器中的组件, 才会拥有SpringBoot提供的强大功能;

      • 例:将Car组件自动注册进容器中:
      @Component
      @ConfigurationProperties(prefix = "mycar") //prefix表示前缀
      public class Car {
          private String brand;
          private Integer price;
      }
      
      #配置文件
      mycar: 
        brand: 小鹏
        price: 100000
      

    2. 自动配置原理【总述】

    SpringBoot自动配置的核心注解是@SpringBootApplication,这是个十分【重要】的注解。

    @SpringBootApplication

    • SpringBoot应用
    • 用在主启动类上;
    • 表名该应用是个SpringBoot应用,并且指定主启动类入口;
    • 是SpringBoot的核心注解,也是个合成注解,由3个注解组合而成@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan);
      • SpringBoot源码:
      @SpringBootConfiguration
      @EnableAutoConfiguration
      @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
            @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
      public @interface SpringBootApplication{
          ……
      }
      

    主程序注解结构列图【精髓】

    建议在下面学习底层注解时对照结构图。

    @SpringBootApplication:主程序注解;

    • @SpringBootConfiguration:表名核心配置类;
      • @Configuration:表名配置类;
    • @ComponentScan:开启包扫描;
    • @EnableAutoConfiguration:启用自动配置;
      • @AutoConfigurationPackage:自动配置包,指定了自动配置包的默认规则;
        • @Import(AutoConfigurationPackages.Registrar.class):容器中导入Registrar组件;
      • @Import(AutoConfigurationImportSelector.class):【重要】按照条件配装规则@Conditional,按需配置;

    下面将对@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan三个注解逐一分析。

    3. 引导加载自动配置类【三注解源码分析】

    这三个注解中,@EnableAutoConfiguration为核心。

    @SpringBootConfiguration

    • 配置注解
    • 标注类上;
    • 底层是一个@Configuration,代表当前类是一个配置类。在这里指核心配置类。

    @ComponentScan

    • 开启包扫描

    • 标注类上;

    • 可以指定扫描路径,扫描到的包里的注解才能生效。在这里自定义了两个扫描器。

      • 例:@ComponentScan(basePackages = {"com.dlhjw"})

    @EnableAutoConfiguration

    • 启用自动配置
    • 标注在配置类上;
    • 需要在配置类里写,表示开启属性配置功能,将指定的组件自动注册到容器中;
    • 重要注解,实现注解@SpringBootApplication的核心功能,其本身也是一个合成注解。
      • 注解源码,由@AutoConfigurationPackage@Import合成:
      @AutoConfigurationPackage
      @Import(AutoConfigurationImportSelector.class)
      public @interface EnableAutoConfiguration {
         ……
      }
      

    第一个注解:

    @AutoConfigurationPackage
    • 自动配置包
    • 用在类上;
    • 指定了自动配置包的默认规则:将主程序类MainApplication所在包下的所有组件用Registrar方法批量注册进容器里。
      • 注解源码:
      @Import(AutoConfigurationPackages.Registrar.class)  //给容器中导入一个组件
      public @interface AutoConfigurationPackage {
         ……
      }
      

    源码级分析:

    解释SpringBoot默认的扫描路径为主程序类MainApplication所在包及以下:

    Registrar方法源码如下:

    static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
       Registrar() {
       }
       
       //Registrar方法传递两个参数,第一个是注解的源信息AnnotationMetadata,这个注解标在主程序类MainApplication上(合成注解层层传递)
       public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
           //Registrar方法利用注解源信息获取到主程序MainApplication所在包名com.dlhjw.boot,封装成数组,注册进容器里。
           AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
       }
    
       public Set<Object> determineImports(AnnotationMetadata metadata) {
           return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata));
       }
    }
    
    • 即:@AutoConfigurationPackage注解的含义是:将指定的一个包下的所有组件导入进容器。

    第二个注解

    @Import(AutoConfigurationImportSelector.class)【重要】
    • 引入自动配置类;
    • 用在类上;
    • 在SpringBoot初始启动时导入127个自动配置类,按照条件配装规则@Conditional,按需配置;

    源码级分析:

    总体上:利用Selector机制给容器批量导入自动配置类;(底层 -> 实现)

    1. 首先从META-INF/spring.factories位置加载一个文件。即默认扫描当前系统里面所有META-INF/spring.factories位置的文件。其中最重要的是spring-boot-autoconfigure-2.3.4.RELEASE.jar包里的META-INF/spring.factories(SpringBoot兼容全场景的127个自动配置类就在这里,即xxxxAutoConfiguration);
    2. 接着使用Spring的工厂加载类loadSpringFactories得到所有的组件;
    3. 然后调用getCandidateConfigurations()获取到所有需要导入到容器中的配置类(默认导入导容器中的127个全类名组件)
    4. 接着利用getAutoConfigurationEntry(annotationMetadata)方法获取自动配置集合
    5. 【核心】最后对getAutoConfigurationEntry(annotationMetadata)获取到的配置进行封装,封装成selectImports(AnnotationMetadata am)方法,返回String数组,数组里说明了需要导入的自动配置类(组件)。

    4. 按需开启自动配置项【核心】

    虽然127个场景的所有自动配置启动的时候默认全部加载。xxxxAutoConfiguration
    但我们不一定全部都会用到,需要按照条件装配规则@Conditional,按需配置。

    例:给容器中加入文件上传解析器;

    @Bean
    @ConditionalOnBean(MultipartResolver.class)  //容器中有这个类型组件
    @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) //容器中没有这个名字 multipartResolver 的组件
    public MultipartResolver multipartResolver(MultipartResolver resolver) {
        //给@Bean标注的方法传入了对象参数,这个参数的值会从容器中找。
        //SpringMVC multipartResolver。防止有些用户配置的文件上传解析器不符合规范
        return resolver;
    }
    

    5. 修改默认配置【定制化配置】

    约定大于配置思想: SpringBoot默认会在底层配好所有的组件。但是如果用户自己配置了以用户的优先

    @ConditionalOnMissingBean

    • 条件装配
    • 用在方法上;
    • SpringBoot源码里经常会有@ConditionalOnMissingBean注解,表示当容器中没有该组件时,才会注册SpringBoot默认的。
      //源码里
      @Bean
      @ConditionalOnMissingBean
      public CharacterEncodingFilter characterEncodingFilter() {
      }
      

    用户配置方式(定制化配置):

    定制化配置基于属性绑定注解@EnableConfigurationProperties与修改默认配置方法

    1. 在config包下的配置文件类上标注引入配置文件注解@ImportResource

      @ImportResource("classpath:beans.xml")
      public class MyConfig(){
          //(*)
      }
      
    2. 在config包下的配置文件类里(*)处配置用户方式

      @Bean
      public CharacterEncodingFilter myFilter() {
          ……
      }
      
    3. application.properties 配置文件里配置(推荐)

    *6. 改变扫描路径

    SpringBoot默认扫描主程序所在包及其下面的所有子包里面的组件,但可以改变扫描路径。

    改变扫描路径:

    @SpringBootApplication(scanBasePackages="com.dlhjw")

    • 扫描com.dlhjw包下的注解;

    @ComponentScan("com.dlhjw")

    • 扫描com.dlhjw包下的注解;

    排除自动扫描:

    @SpringBootApplication(exclude={XX.class,YY.class})

    • 例:@SpringBootApplication(exclude=DataSourceAutoConfiguration.class)取消数据库配置。

    7. 自动配置总结

    一句话概括SpringBoot的自动配置原理就是:启动时加载所有,最终按照条件进行装配。

    即:

    • SpringBoot先加载所有的自动配置类 xxxxxAutoConfiguration
    • 每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。从xxxxProperties里面取,xxxProperties和配置文件进行了绑定;
    • 生效的配置类就会给容器中装配很多组件,只要容器中有这些组件,相当于这些功能就有了;
    • 由此可得:定制化配置基于属性绑定注解@EnableConfigurationProperties修改默认配置方法
      • 用户直接自己@Bean替换底层的组件(基于SpringBoot的@ConditionalOnMissingBean);
      • 用户去看这个组件是获取的配置文件什么值就去修改(基于SpringBoot的属性绑定注解@EnableConfigurationProperties);
    • 即:xxxxxAutoConfiguration ---> 组件 ---> xxxxProperties里面拿值 ---> application.properties

    主程序注解结构列图

    这里再列一下结构图,因为本小节的精髓都在这了。

    @SpringBootApplication:主程序注解;

    • @SpringBootConfiguration:表名核心配置类;
      • @Configuration:表名配置类;
    • @ComponentScan:开启包扫描;
    • @EnableAutoConfiguration:启用自动配置;
      • @AutoConfigurationPackage:自动配置包,指定了自动配置包的默认规则;
        • @Import(AutoConfigurationPackages.Registrar.class):容器中导入Registrar组件;
      • @Import(AutoConfigurationImportSelector.class):【重要】按照条件配装规则@Conditional,按需配置;

    其他说明:

    • 若在配置类里只有一个有参构造器,说明有参构造器里的所有属性都会从属性里确定;(4.2.x 设置静态页面)

    最后

    \color{blue}{\rm\small{新人制作,如有错误,欢迎指出,感激不尽!}}

    \color{blue}{\rm\small{欢迎关注我,并与我交流!}}

    \color{blue}{\rm\small{如需转载,请标注出处!}}

    相关文章

      网友评论

          本文标题:SpringBoot | 2.1 SpringBoot自动装配原

          本文链接:https://www.haomeiwen.com/subject/imhzpltx.html