• 全栈之路-杂篇-探究SpringBoot中的自动配置


      这个主要是解析springboot中的自动配置的原理是什么,以及为什么要有这个自动配置,从这两个方面进行解析,看七月老师如何来进行讲解这个自动配置

     一、@SpringBootApplication注解的理解

      @SpringBootApplication这个注解是启动类上的注解,在这个注解之中存在着许多玄机!在启动的时候,springboot如何将第三方的配置类加入到spring IOC容器中,那就是通过这个注解了,详细看一下这个注解的内部是什么样子的?

    @SpringBootApplication注解主要是由三个注解组成,分别是@SpringBootConfiguration、@EnableAutoConfiguration和@ComponentScan,@ComponentScan这个注解主要是扫描包,将带有@Component注解的类扫描出来,这个就简单的进行说明一下,重点来看一下@SpringBootConfiguration和@EnableAutoConfiguration这两个注解,其中最核心的注解是@EnableAutoConfiguration,这个主要是负责加载很多的第三方的SDK

    1、@SpringBootConfiguration注解

    这个注解其实看一下源码的话,其实就是一个@Configuration注解的封装

     

     2、@EnableAutoConfiguration注解(springboot自动配置最核心的机制)

    这个注解其实是跟@Configuration注解做的事情是类似的,目的也是将需要加载的bean配置好,然后再把配置好的bean放入到spring IOC容器中,只不过这其中的过程可能复杂一些,本质其实就是在做一个配置类的事情

    注意点:@EnableAutoConfiguration注解其实是你所引用的第三方的SDK,举例:maven中pom.xml文件中引入的第三方的包就是需要使用这个注解进行注入到spring IOC容器中的

     看一下@Import导入的那个AutoConfigurationImportSelector类,这个类中有一个核心的方法:

     1     @Override
     2     public String[] selectImports(AnnotationMetadata annotationMetadata) {
     3         if (!isEnabled(annotationMetadata)) {
     4             return NO_IMPORTS;
     5         }
     6         AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
     7                 .loadMetadata(this.beanClassLoader);
     8         AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
     9                 annotationMetadata);
    10         return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    11     }

    这个方法研究了一下,很复杂,经过采纳七月老师的讲解,联合自己研究代码,这个类的主要作用就是加载第三方的配置类,具体到哪一个文件的话,可以随便找一个第三方的SDK,比如springboot中的autoconfigure这个jar包,他会加载spring.factories这个文件中的key-value键值对,其实就是加载配置的一些第三方全路径的类。加入到spring IOC容器中

     

     二、@EnableAutoConfiguration注解模块装配机制

      这里并不是就是指指这一个特定的注解,而是以Enable开头的一类注解的模块装配机制,这个机制是这一类注解的通用原理,自己写一个启动类,来验证一下这个模块装配机制

    1、创建启动类LOLApplication

      当加上@ComponentScan注解之后,这个启动类是可以正常启动的,因为@ComponentScan注解是可以扫描包下的文件,只要有@Configuration+@Bean注解都会注入到spring IOC容器中的,所以这个配置类中的实现类是注入成功的

     1 @ComponentScan
     2 public class LOLApplication {
     3     public static void main(String[] args){
     4         ConfigurableApplicationContext context = new SpringApplicationBuilder(LOLApplication.class)
     5                 .run(args);
     6         // 验证是否加入到spring IOC容器中
     7         ISkill iSkill = (ISkill) context.getBean("irelia");
     8         iSkill.r();
     9     }
    10 }

    2、之前的配置类代码

      我们只注入一个实现类,避免麻烦,来简化这个验证机制

    1 @Configuration
    2 public class HeroConfiguration {
    3 
    4     @Bean
    5     public ISkill irelia (){
    6         return new Irelia();
    7     }
    8 }

    3、@Import注解的两种用法

    我们使用@Import注解来实现配置类注入成功,这里有两种用法:

    (1)直接导入配置类的方式

     1 @Import(HeroConfiguration.class)
     2 public class LOLApplication {
     3     public static void main(String[] args){
     4         ConfigurableApplicationContext context = new SpringApplicationBuilder(LOLApplication.class)
     5                 .web(WebApplicationType.NONE)
     6                 .run(args);
     7         // 验证是否加入到spring IOC容器中
     8         ISkill iSkill = (ISkill) context.getBean("irelia");
     9         iSkill.r();
    10     }
    11 }

    注意:这个需要使用SpringApplicationBuilder类的web方法,不启动web服务器,才能成功启动

    (2)使用selector来导入

     首先,需要新建LOLConfigurationSelector类

    1 public class LOLConfigurationSelector implements ImportSelector {
    2 
    3     @Override
    4     public String[] selectImports(AnnotationMetadata annotationMetadata) {
    5         return new String[] {HeroConfiguration.class.getName()};
    6     }
    7 }

    然后,在@Import注解中导入这个selector类

     1 @Import(LOLConfigurationSelector.class)
     2 public class LOLApplication {
     3     public static void main(String[] args){
     4         ConfigurableApplicationContext context = new SpringApplicationBuilder(LOLApplication.class)
     5                 .web(WebApplicationType.NONE)
     6                 .run(args);
     7         // 验证是否加入到spring IOC容器中
     8         ISkill iSkill = (ISkill) context.getBean("irelia");
     9         iSkill.r();
    10     }
    11 }

    4、创建自定义的Enable注解

    我们通过创建这个@EnableLOLConfiguration注解,来实现这个模块装配的注解,看一下注解的实现代码:

    1 @Retention(RetentionPolicy.RUNTIME)
    2 @Target(ElementType.TYPE)
    3 @Documented
    4 @Import(LOLConfigurationSelector.class)
    5 public @interface EnableLOLConfiguration {
    6 
    7 }

    看一下这个注解的使用,其实就跟其他注解一样,只是在类上加上这个注解就好

     1 @EnableLOLConfiguration
     2 public class LOLApplication {
     3     public static void main(String[] args){
     4         ConfigurableApplicationContext context = new SpringApplicationBuilder(LOLApplication.class)
     5                 .web(WebApplicationType.NONE)
     6                 .run(args);
     7         // 验证是否加入到spring IOC容器中
     8         ISkill iSkill = (ISkill) context.getBean("irelia");
     9         iSkill.r();
    10     }
    11 }

    这样的话,自定义的模块装配注解也实现了,让我们对比一下springboot中与我们自定义的模块装配注解有什么关联,有什么区别!

    三、对比总结

    1、对比记忆

    总体的思路是一致的,都是通过@Import注解导入配置类的全路径名,从而将配置类实例化,进而注入到spring IOC容器中,方便我们使用,对比一下selector类实现的ImportSelector接口的selectImports方法,其实总体是一样的:

    ## 自定义的LOLConfigurationSelector类代码:

    1 public class LOLConfigurationSelector implements ImportSelector {
    2 
    3     @Override
    4     public String[] selectImports(AnnotationMetadata annotationMetadata) {
    5         return new String[] {HeroConfiguration.class.getName()};
    6     }
    7 }

    ## springboot中@EnableAutoConfiguration注解中使用的@Import注解导入的AutoConfigurationImportSelector类代码(只看实现的selectImports方法):

     1 public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
     2         ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
     3 
     4     @Override
     5     public String[] selectImports(AnnotationMetadata annotationMetadata) {
     6         if (!isEnabled(annotationMetadata)) {
     7             return NO_IMPORTS;
     8         }
     9         AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
    10                 .loadMetadata(this.beanClassLoader);
    11         AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
    12                 annotationMetadata);
    13         return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    14     }
    15 }

    看源码,这个getAutoConfigurationEntry中实现,会找到getCandidateConfigurations方法,最终会看到SpringFactoriesLoader类的loadFactoryNames这个方法,这个就是提取spring.factories中的需要加载注入spring IOC容器的配置类的全路径名的信息

     2、总结

    这个模块装配注解用到的是Java中的SPI 机制,SPI的全名为Service Provider Interface,这种机制就是为了去应对系统中的变化,将这种变化做了隔离,关系依赖大概就是这个样子的:

    等同于 基于interface接口 + 策略模式 + 变化点(配置文件中)

     内容出处:七月老师《从Java后端到全栈》视频课程

    七月老师课程链接:https://class.imooc.com/sale/javafullstack

  • 相关阅读:
    webapi 导入CSV文件
    webapi 导出CSV文件
    .net 压缩文件夹和解压
    SSH免密码登陆以及穿越跳板机
    hbase shell删除键不听使唤
    百度分享插件wbuid属性无法@指定微博
    iOS safari BUG 总结
    论zeroclipboard的各种爽翻天用法
    Android Studio 使用技巧
    安装第三方包web.py发生的错误
  • 原文地址:https://www.cnblogs.com/ssh-html/p/12301262.html
Copyright © 2020-2023  润新知