系列文章目录
springboot是如何实现自动装配的(一)注解分析
springboot是如何实现自动装配的(二)条件装配
springboot是如何实现自动装配的(三)静态资源配置源码
文章目录
前言
本章深入源码来分析springboot自动装配的原理。来看看神奇的自动装配是怎么一回事。一、从何处入手
我们从启动类的注解入手,先看启动类。@SpringBootApplication
public class SourceAnalysisApplication {
public static void main(String[] args) {
SpringApplication.run(SourceAnalysisApplication.class, args);
}
}
启动类上只有一个注解,这是一个合成注解,我们来看一下里面都包含了什么。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
这么多注解抛去上面的四个元注解 @Target @Retention @Documented @Inherited 还剩三个,其中@SpringBootConfiguration点开源码我们看的出来他就是一个配置类
@ComponentScan 是一个包扫描规则,显然也不是我们要找的
那么就剩下一个了,就是==@EnableAutoConfiguration==
@EnableAutoConfiguration
@EnableAutoConfiguration通过名字我们就知道,它是开启自动配置。那么它究竟干了什么呢?我们进去看一看。
去掉上面的元注解。实际上就是两个注解
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
@AutoConfigurationPackage
我们来看看@AutoConfigurationPackage里面是个啥东西
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
同样去掉元注解,它就是个@Import,@Import注解的作用就是给容器中导入一个组件,导入哪个组件呢,就是括号里面的AutoConfigurationPackages.Registrar.class类,那这个类是干什么用的呢,我们进去看一下。
首先它是一个内部类,主要就是调用了一个方法进行批量导入组件。
这个方法有两个参数,第一参数代表注解元信息。
由于我们是通过@AutoConfigurationPackage注解导入的这个类。这个注解又是@EnableAutoConfiguration里的合成注解而@EnableAutoConfiguration又是@SpringBootApplication注解里的合成注解。最后SpringBootApplication注解标在了我们的启动类上。
说了这么多废话其实就是启动类的元信息。我们来看DEBUG信息
然后这货又干了些啥呢。它new了一个PackageImports类,按照名字来看是包要导入的东西。
整个就相当于把我们的元注解信息注入进去,然后获取包名(.getPackageName方法),那获取到的是什么?就必然是我们启动类的包名啊!验证一下,选择这行代码,右键选择
查看结果,与预期的一致。
然后转换成一个数组,最后注册进来。
总结一下@AutoConfigurationPackage这个注解就是把某个包下面的所有组件注册进来。
@Import(AutoConfigurationImportSelector.class)
接下来分析@EnableAutoConfiguration的第二个注解,同样第二个注解也是导入一个类AutoConfigurationImportSelector.class。我们来看这个类
我们看到这个方法实际上是调用getAutoConfigurationEntry()这个方法得到所有配置,然后得到配置数组返回出去的。那么我们先搞清楚getAutoConfigurationEntry()这个方法是干什么的。
getAutoConfigurationEntry
我们DEBUG这个方法,发现他调用了getCandidateConfigurations方法,即获取候选配置。然后发现获取了多少呢?127个。
也就是说这127个组件默认要导入到容器中。
好继续,我们来看getCandidateConfigurations又是怎么获取候选配置的呢,继续打断点调试。
我们进入getCandidateConfigurations方法内部,
我们发现它调用了Spring的工厂加载器,利用工厂进行加载。
我们再继续进入。
发现是用loadSpringFactories得到所有的组件
那么loadSpringFactories又是怎么工作的呢,继续打断点。
如上图一开始我们就走到了classLoader.getResources这个方法来加载资源文件,参数的常量值为"META-INF/spring.factories",就是说从这个位置加载一个文件。就是默认扫描当前系统里面所有这个位置的文件。
我们来看看我们引入的包里面有没有这个文件。
当然是有的有,有的没有。我们直接看最核心的包,就是上图第二个包
spring-boot-autoconfigure-2.3.0.RELEASE.jar
这个包名一看便知道,是自动配置。看看它的spring.factories文件里面是什么
我们发现Auto Configure注释下面的写了好多 XXXAutoConfiguration。总共数量是127个,这回知道了,原来上面说的加载的127个配置,是在这里写死的。
总结
@AutoConfigurationPackage注解
1.给容器中导入一个组件@Import(AutoConfigurationPackages.Registrar.class)
2.利用Registrar类的registerBeanDefinitions方法给容器注册一系列组件。
将指定包路径下的所有组件注册进来,即启动类包路径。
@Import(AutoConfigurationImportSelector.class)注解
1.利用 protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata)给容器中批量导入一些组件。
2.调用List configurations = getCandidateConfigurations(annotationMetadata, attributes)获取所有需要导入到容器的配置。
3.利用工厂加载器private static Map<String, List> loadSpringFactories(@Nullable ClassLoader classLoader) 得到所有的组件。
4.从META-INF/spring.factories位置来加载一个文件。
spring-boot-autoconfigure-2.3.0.RELEASE.jar这个包下的META-INF/spring.factories文件中配有写死的127个默认加载的自动配置。
但是,并不是所有自动的这些配置文件对应的组件都要装配到容器中,下一篇我们来继续分析一下springboot是如何按需装配的。