全栈之路-杂篇-探究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

posted @ 2020-02-14 22:50  ssc在路上  阅读(249)  评论(0编辑  收藏  举报