[4-06-09].第10节:自动配置- 分析@SpringBootApplication启动类
SpringBoot学习大纲
一、启动类加载器@SpringBootApplication:
1.1.@SpringBootApplication注解源码:
- 1.
@SpringBootApplication
注解是引导加载自动配置类 - 2.@SpringBootApplication =
@SpringBootConfiguration + @EnableAutoConfiguration + @ComponentScan()
- 3.下面依次介绍
@SpringBootConfiguration + @EnableAutoConfiguration + @ComponentScan()
这3个注解
1.2.注解1:分析@SpringBootConfiguration注解
- 1.根据
@SpringBootConfiguration
注解的源码可以看到,在源码中有注解@Configuration
,出现了@Configuration
,就代表当前类是一个配置类
- 2.也就是说在SpringBoot项目中mainApplication这个程序也是SpringBoot中的一个配置类
1.3.注解2:分析@ComponentScan注解:
- 1.
@ComponentScan
代表的就是包扫描
- 2.
@ComponentScan("com.jianqun")
:可以设置包扫描路径 - 3.如果有多个包,就可以写成数组的形式,如
@SpringBootApplication(scanBasePackages = {"com.jianqun",“xx.cc.ddd”)}
或者也可以使用@ComponentScan ()指定扫描路径
1.4.注解3:分析@EnableAutoConfiguration注解:
a.功能:
- 1.@EnableAutoConfiguration注解是用于开启自动配置的核心,有了这个注解,就可以使得 spring-boot-autoconfigure下官方写好的所有配置类生效
b.源码
-
1.在@EnableAutoConfiguration注解的源码中可看到:
@EnableAutoConfiguration
=@AutoConfigurationPackage + @Import()
-
2.下面的内容分别来分析
@AutoConfigurationPackage和@Import()
注解在自动配置中的原理
c.分析 @EnableAutoConfiguration -->
@AutoConfigurationPackage:
注解 @AutoConfigurationPackage的
源码
- 1.功能:
@AutoConfigurationPackage
就是自动配置包,它用于指定默认的包规则
- 2.在
@AutoConfigurationPackage
注解的源码中又包括了@import
注解
- 3.通过源码,可以看到使用@import注解给容器导入了名称是AutoConfigurationPackages.Registar组件,那AutoConfigurationPackages.Registar是什么???就需要分析AutoConfigurationPackages.Registar源码了
AutoConfigurationPackages.Registrar
源码分析:
- 1.通过下图中的Registrar源码可以看到包含了两个方法,分别是registerBeanDefinitions()方法和determineImports()方法
- 2.方法1分析:对于
registerBeanDefinitions(参数1,参数2)
方法,它传递了两个参数;
- 对于参数1: AnnotationMetadata metadata,第一个参数意思是传递的是注解的元信息
- 这里所指的注解是:@AutoConfigurationPackage,代表的就是这个注解标在哪个位置,都有哪些属性的元信息;
- 这个注解是标在了
MainApplication这个类
位置上; 当元信息拿来后,用new PackageImports(metadata).getPackageNames()
获取到包名称,然后toArray(new String[0])
转封装成数组
- 最后
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]))
代码的意思就是把某MainApplication这个包里面的所有组件批量注册到容器中
- 对于参数1: AnnotationMetadata metadata,第一个参数意思是传递的是注解的元信息
总结: @AutoConfigurationPackage
作用:
- 作用1是:指定了默认包规则
- 作用2是:把默认包路径下的所有组件注册到容器中,即把
启动类MainApplication这个包里面的所有组件批量注册到容器中了
d.分析 @EnableAutoConfiguration -->
@Import(AutoConfigurationImportSelector.class)
@Import(AutoConfigurationImportSelector.class)作用
- 1.这里的@Import(AutoConfigurationImportSelector.class)注解是
批量导入组件的作用,可以使用程序来定义导入的一堆组件,把这些类的全类名加载进来,最终实现组件的批量导入
- 2.
具体是怎么批量导入这些组件的????就需要分析AutoConfigurationImportSelector的源码了,看看具体是怎么把全类名加载进来实现组件批量导入
定位AutoConfigurationImportSelector的源码
- 1.下图为AutoConfigurationImportSelector类的源码
- 2.在AutoConfigurationImportSelector类的源码中可以看到:主要包括了
selectImports(AnnotationMetadata annotationMetadata)
等方法
分析selectImports(AnnotationMetadata annotationMetadata)方法源码
- 1.从selectImports(AnnotationMetadata annotationMetadata)方法的源码中,可以看到返回值是一个字符串数组类型的。在这个返回的数组中存储的就是
那些需要导入到容器中那一堆组件
- 2.那返回的数组中存储的
那一堆组件
是怎么来的呢??那就需要分析源码中的getAutoConfigurationEntry(annotationMetadata)
方法了!!
分析selectImports(参数) 方法中的getAutoConfigurationEntry(annotationMetadata)方法源码
- 1.如下是
getAutoConfigurationEntry(annotationMetadata)
方法的源码:
- 2.在源码中可以看到:
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes
),通过调用 getCandidateConfigurations()方法用于获取到那些需要导入到容器中的那一堆组件
- 3.如下debugger中可以看出configurations变量一共存储的131个组件,这131个组件就是需要导入容器中的
分析getCandidateConfiguration()源码:
- 1.点击getCandidateConfiguration() 方法,进入方法内部:
getCandidateConfiguration()方法在SpringBoot2和SpringBoot3中的实现方式有了变化!!!!下面对此分别进行分析
============== 分析getCandidateConfiguration()方法在SpringBoot2中的实现
------------ 开始
==============
- 1、getCandidateConfiguration() 方法源码
- 在getCandidateConfiguration() 方法中利用了Spring的工厂加载器
SpringFactoriesLoader.loadFactoryNames()
又加载了一些东西,加载的这些就是上面List中存储的那131个组件
- 2.具体这些组件loadFactoryNames()方法时怎么加载来的就需要分析
SpringFactoriesLoader.loadFactoryNames()
方法的源码了
- 在getCandidateConfiguration() 方法中利用了Spring的工厂加载器
- 2、SpringFactoriesLoader.loadFactoryNames()方法的源码
- 如下是loadFactoryNames()方法的源码:
- 在
SpringFactoriesLoader.loadFactoryNames()
这个方法中的源码中可以看到,通过调用了loadSpringFactories()方法来获取这一堆要加载的组件
- 那loadSpringFactories()方法具体具体是从哪里得到的这一堆要注入容器的组件呢???? 那就要具体分析loadSpringFactories()方法的源码了
- 如下是loadFactoryNames()方法的源码:
- 3、loadSpringFactories()源码分析:
- 通过源码分析,可以看到通过loadSpringFactories()方法,得到所有要加载的组件,返回值是
Map<String, List<String>>
类型的
- 可以从源码中看到
lassLoader.getResources()方法会从META-INF/spring.factories位置来加载一个文件
,默认扫描我们当前系统里面所有META-INF/spring.factories
位置的文件
- 比较核心的jar包,即
spring-boot-autoconfigure-2.5.2.RELEASE.jar包
,在它的里面也有META-INF/spring.factories
- 最终可以看出,在配置文件spring.factories中写死了
spring-boot一启动就要给容器中加载的所有配置类
(xxxxAutoConfiguration
) - 虽然所有的自动配置类在springboot启动的时候都加载了,但
还是按照条件配置规则(@ConditionalOnxxx)来进行启动
,只有条件成立,才能生效,最终实现了按需配置
- 通过源码分析,可以看到通过loadSpringFactories()方法,得到所有要加载的组件,返回值是
============== 分析getCandidateConfiguration()方法在SpringBoot2中的实现
------------ 结束
==============
============== 分析getCandidateConfiguration()方法在SpringBoot3中的实现
------------ 开始
==============
- 1、getCandidateConfiguration()方法源码:
- 在getCandidateConfiguration() 方法中利用
ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader())
又加载了一些东西,这些就是List中存储的那一堆药加载的组件 - 这一堆要加载的组件具体是怎么加载进来的,就要分析ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader())源码了
- 在getCandidateConfiguration() 方法中利用
- 2、ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader())源码
- 在load()方法的源码中,代码annotation.getName()是获取到注解的全类名,然后在META-INF/spring/%s.imports中,对%s进行替换
- 在这里,annotation.getName()的值是:org.springframework.boot.autoconfigure,对s%进行替换后是:
META-INF/spring/org.springframework.boot.autoconfigure.imports
,也就是说SpringBoot在底层就会加载导入META-INF/spring/org.springframework.boot.autoconfigure.imports
定义的所有类,一共有142个,这就实现了组件的批量导入
==============分析getCandidateConfiguration()方法在SpringBoot3中的实现
------------结束
==============
- 在load()方法的源码中,代码annotation.getName()是获取到注解的全类名,然后在META-INF/spring/%s.imports中,对%s进行替换
总结:
- 1.对于SpringBoot2版本的项目在启动的时候,利用注解@Import(AutoConfigurationImportSelector.class)在底层实际上是从位置为
META-INF/spring.factories
位置批量导入一堆组件- 2.对于SpringBoot3版本的项目在启动的时候,利用注解@Import(AutoConfigurationImportSelector.class)在底层实际上是从位置为
META-INF/spring/org.springframework.boot.xxxx.imports
中导入一堆组件- 3.不论是SpringBoot2还是SpringBoot3每个自动配置类
按照条件进行生效
二、自动配置流程:
2.1.定位xxxxAutoConfiguration位置:
- 1.引入开发所需要的场景启动器后,就会导入相关场景的所有依赖 -> 每个场景启动器都引入了一个
spring-boot-starter
,这个是核心场景启动器 -> 核心场景启动器引入了spring-boot-autoconfigure包
- 2.spring-boot-autoconfigure里面包含了所有场景的所有配置
- 3.在所有场景的配置中,都有xxxxAutoConfiguration自动配置类:
2.2.总结:
- SpringBoot先加载所有的自动配置类
- 每个自动配置类都是按照条件进行生效的
- 生效的配置类就会给容器中装配很多个组件
- 只要容器中有这些组件,就相当于这些功能就具有了
三、属性绑定:
3.1.分析@EnableConfigurationProperties(ServerProperties.class):
- 1.每个自动配置类都可能有注解
@EnableConfigurationProperties(ServerProperties.class)
,用来把配置文件中配的指定前缀的属性值封装到 xxxProperties属性类中 - 2.在容器中
使用@Bean放一堆组件
3.2.举例:
- 1.以Tomcat为例:把服务器的所有配置都是以server开头的,配置都封装到了属性类中
- 2.给容器中放的所有组件的一些核心参数,都来自于xxxProperties。xxxProperties都是和配置文件绑定,只需要改配置文件的值,核心组件的底层参数都能修改
四、自定义类绑定配置提示:
4.1.自定义类:
4.2.配置绑定:
a.问题描述:
- 我们在配置文件与自定义类绑定到额时候,没有任何提示,不是很友好,我们需要引入依赖进行解决
b.引入依赖:
c.测试:
五、总结SpringBoot开发时的核心流程:
- 1.导入starter,就会导入autoconfigure包
- 2.autoconfigure 包里面 有一个文件
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
,里面指定的所有启动要加载的自动配置类 - 3.@EnableAutoConfiguration 会自动的把上面文件里面写的所有自动配置类都导入进来
- 4.然后xxxAutoConfiguration 回按照条件注解进行按需加载
- 5.xxxAutoConfiguration给容器中导入一堆组件,组件都是从 xxxProperties中提取属性值,xxxProperties又是和配置文件进行了绑定