springboot自动配置的原理入门分析
我们需要从程序的启动进行分析
 我们之所以知道使用哪个类作为程序启动的入口,就是因为这个类被@SpringBootApplication修饰了
@SpringBootApplication
 
我们先看这个注解,因为我们用的是idea,使用ctrl跟进去看这个注解的源码,我们先看这个注解又被哪些注解修饰了
 
 元注解我们可以不用管,因为这个是java自己提供的,我们的关注点应该是这三个注解
- @SpringBootConfiguration
- @EnableAutoConfiguration
- @ComponentScan
那这三个注解是做什么的呢?
 换句话说,是谁起到了自动装配的作用,导致我们一启动springboot,他就为我们自动装配了那么多组件?
1、@SpringBootConfiguration
 
我们跟入到这个注解进行查看
 
 发现它还被@Configuration注解修饰了,而这个注解的作用就是表明被该注解修饰的类是一个配置类,并且会在容器中给这个被修饰的类注册一个组件。
 这就表面我们的@SpringBootApplication修饰的类也会被注册一个组件
然后我们看SpringBootApplication中的另两个个注解
2、@ComponentScan
 
这个注解就是一个包扫描注解
3、@EnableAutoConfiguration
 
因为其他两个注解和自动配置都没有什么关系,那么只有这个注解可能和自动装配有关系了,正如它的名字的意思“启动自动配置”
然后跟如到这个注解源码
 发现它也是一个合成注解
 
@AutoConfigurationPackage
 
我们先看这个注解帮助我们做了些什么
 这个注解翻译过来就是“自动配置包”
 然后我们跟如到这个注解的源码
 
发现这个注解其实就是一个@Import注解,而Import注解的作用就是给被该注解修饰的类在容器中创建出这个组件,这个组件的名字就是对应参数中类的全类名
我们跟进到这个类中查看,给容器中导入了一个什么组件
 
 这是一个@Import的高级用法,如果想要详细了解点击超链接
 Import导入的是一个实现了ImportBeanDefinitionRegistrar的类,这个类覆盖重写的registerBeanDefinitions方法的作用就是用来批量注册组件
 这个方法
registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry)
第一个参数表示:所有标注了@Import的类的注解信息
 第二个参数表示:所有需要添加到容器中的bean
而这个方法调用了,这个方法进行批量注册
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
那到底这个register是怎样批量注册的呢?
 批量注册了写什么?
我们跟进到register的源码查看
 
 先不看内容,先看注释
 
 大致意思就是将会注册给定的包名称中的组件
这个方法的第一个参数就是我们上面说的方法的第二个参数
 这个方法的第二个参数就是要注册的包名
 
然后这个方法
 
利用了new PackageImports(metadata).getPackageNames().toArray(new String[0])来将获取到的包名转化为一个字符串数组
我们进行打断点观察
 
 可以看到打断点然后获取这个字符串数组就是我们的主程序所在的包,这时因为其实@SpringBootApplication就包含了@Import这个注解的功能,AnnotationMetadata metadata这个参数表示的就是主程序入口这个类的信息,这些信息中就包含了当前类所在的包位置
 通过debug的模式也可以看到
 
 这个参数中包含了当前类的全限定类名的信息。
总结:@AutoConfigurationPackage的作用
 
如此我们就知道了@AutoConfigurationPackage就是将我们主程序入口的这个类所在的包下的所有组件导入到容器中
@Import(AutoConfigurationImportSelector.class)
 
这个也是import的一个高级 用法,AutoConfigurationImportSelector这个类是一个实现了DeferredImportSelector接口的类,而这个接口继承了ImportSelector
我们跟进到这个类中查看这个类的注册组件的方法
 
 这个方法就是注册组件的方法,而这个方法的返回值是一个字符串数组,这个返回的字符串数组就是我们要注册的所有组件。
我们可以看到是使用下面这个方法返回了一个类,然后调用这个类的一个获取配置的方法再转换为字符数组返回出去
 所以我们只需要关注这个方法是干什么就可以了
 
 然后跟进到这个方法
 
然后在这个方法打断点进行观察
 我们先大致看一下,有一个方法翻译过来叫获取候选配置,这个候选配置是一个List<String>集合叫configurations,然后这个configurations,被一些排除方法、删除方法,过滤了之后将这个集合传入到AutoConfigurationEntry的构造方法中返回了出去
我们启动debug,可以看到这个集合总共又133个
 
 这133个组件就是默认要导入到容器中的。
 我们接着深入查看获取configurations这个参数的方法
 
 然后我们可以看到这个方法是通过spring的工厂加载器加载了一些名字,我们接着深入这个加载名字的方法中
 
 然后接着深入loadSpringFactories这个方法去(不要问为什么,因为我也不知道,老师这么讲,我的功力还不够,只能先理解老师说的是什么)
 
 最终利用这个loadSpringFactories方法加载返回得到了一个Map
 也就是说我们只要明白了这个方法是如何获取组件的我们就能了解那133个组件是怎么来的了
 我们接着打断点观察,当第一次加载,它会先获取资源,从这个位置
 
 相当于从META-INF/spring.factories这个位置获取的资源,就相当于默认扫描当前系统所有这个位置的文件。
我们可以看到有的jar包中有这个路径的内容,有的没有,如果有那么就会被加载
 
 虽然其他的包中也有这个文件,但是只有这个包spring-boot-autoconfigure-2.4.3.jar里面的/META-INF/spring.factories才是核心的配置。
然后我们观察这个核心的文件可以看到这个
 
 这个不正是我们的那个注解所在的全类名了吗,springboot获取到了这么多全类名,经过一系列筛选判断,最终留下的就是这个第21行后的所有内容。我们上面获取到的133个组件就是这写内容
这里面所列举的就是springboot全场景的自动配置,这里全部列举出来了。
所以其实就是文件中写死了springboot 已启动就要给容器中加载的所有配置类
但是其实我们的springboot的容器中其实并不会配置这么多组件,它会按需配置。
根据配置中的路径我们找到对应的类,其实这些类再spring-boot-autoconfigura的jar中
 
 我们以这个aop自动配置类为例,点开查看发现了
 
 他是按条件创建的,这个条件就是导入了对应的依赖,能够找到对应的class信息,存在这个类,这一堆的配置才能生效。
得益于这些按条件装配,虽然我们加载了全部的组件信息,但是它会跟据条件装配
如此就完成了简单的springboot原理的入门分析
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号