目录
自动装配原理
开始深入源码
总结自动装配原理
首先明白一个概念,什么是自动装配?
我们在项目中建一个yaml或者properties文件,里面配置一些参数,如redis,在pom中引入启动器,之后就能用redis,自动把这些集成到spring中,这就是自动装配。
先来提前剧透:
加载spring.factories文件
标记好启动类
解析启动类的注解
加载EnableAutoConfiguration
自动装配原理
开始深入源码
启动类中
创建SpringApplication,调用run方法,这里面是核心逻辑。
为什么把启动类作为参数传进去? 因为后期需要获取这个启动类上的注解,进入run方法
primarySource就是启动类
这里new SpringApplication,注意看源码时不要忽略new,为什么?因为new,会调用构造方法,而很多初始化操作会在构造方法中定义。
进入
注意下面的两行代码
这里涉及到spring.factories这个文件是如何读取与加载的,包括用户自己怎么定义spring.factories
我们先进入setInitializers这行代码中
先获取工厂的名字,之后加载工厂
从缓存中获取,如果缓存中没有会从classpath中查找我们需要的配置文件,配置属性
指定了路径
方法的后半部分,就是把获取的属性,变成properties的格式
最后一步就是把查询到的数据,放到缓存中,方便下次数据的读取。
不需要重新查询,不需要重新加载配置文件等,加快速度
之后返回结果到刚才的方法,把获取的类名称,实例化
返回到
setListeners这行代码的处理逻辑和上面一样
public ConfigurableApplicationContext run(String... args) {
//时间的处理
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//上下文 就是一个全局变量,保存全局数据,供其他人使用
ConfigurableApplicationContext context = null;
//异常汇报器
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//获取监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
// spring,springBoot对于观察者模式,使用是非常多的
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//封装环境变量
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//上面都是一些赋值准备工作
//下面这行才是重要
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
......
}
spring,springBoot对于观察者模式使用是非常多的,如Listener,event
其中Environment接口有个实现类,standardEnvironment
这个standerEnvironment类会加载系统的环境和属性值,比如java版本,java classpath,编码格式等
进入到prepareContext方法中
前面讲过这个primarySource就是启动类,这里会去找primarySource,也就是找启动类
那么为什么要找启动类?是为了解析启动类上的注解,这是核心
之后load加载,进入方法
提示:Spring通过反射创建对象前,必须要有BeanDefinition(bean定义),BeanDefinition中保存的是属性值
创建BeanDefinition,设置一些属性值,之后调用loader.load方法
判断source类型,我们的是类启动器,所以是Class,满足第一个if判断
通过注解读取器来读取注解信息 ,注意这里只是加载好注解信息,做好标识,没有解析。之后返回到前面的prepareContext中,此时已经加载完了注解信息
返回到prepareContext中,已加载注解信息,待解析。
refresh在spring中是个很重要的方法,当带有refresh前缀时,表示它要调用refresh方法了,同时这个方法也是重要的。
如果点击进入refreshContext方法,其底层就是调用的refresh,如果不调用,是没法创建bean的
在详细分析refreshContext方法前,我们先来了解一些知识点
spring为什么这么火?
一是因为全生态,有所有的技术组件,我想用的技术,几乎都有相应的框架。
二是扩展性,代码不能是死的,可以根据你的业务需求来动态扩展
spring中有两个重要的扩展接口:postProcessor(后置处理器,个人认为增强器),是为了完成扩展功能的,做对应的扩展实现。有两个代表:BeanFactoryPostProcessor和BeanPostProcessor
SpringBoot自动装配的实质就是从spring.factories文件中获取到相应的Bean对象,并由Spring容器来帮我们进行管理
spring.factories文件中存的是类的完全限定名,我们可以通过反射来实例化出具体的Bean对象,而这些Bean对象会放到BeanFactory中。如果我想扩展,就用BeanFactoryPostProcessor
注意逻辑
无论是配置,还是注解,都是为了创建BeanDefinition,之后通过反射实例化获取Bean对象
refreshContext方法最后还是调用的refresh方法
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 准备刷新,初始化容器等
prepareRefresh();
// 获取最新的BeanFactory工厂,用来生产bean
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 设置属性
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// 很重要 增强器或者后置处理器 这里实现的自动装配
invokeBeanFactoryPostProcessors(beanFactory);
......
}
进入invokeBeanFactoryPostProcessors(beanFactory)方法
里面有
这个BeanDefinitionRegistryPostProcessor就是刚才BeanFactoryPostProcessor的子接口
这个方法就是去Bean工厂中,找有没有归属于这个BeanDefinitionRegistryPostProcessor的类对象,最后返回
这里会有个转换,在AnnotationConfigUtils中 定义了这个字符串
然后根据字符串匹配对应的类
比如上面的字符串就匹配后就会BeanDefinition中放入ConfigirationClassPostProcessor,生成BeanDefinition
实际解析的类是ConfigirationClassPostProcessor,本质是BeanFactoryPostProcessor,不是配置类,是配置的增强类
继续往下
这个方法内部会进行一些调用
通过BeanDefinitionRegistry的BeanDefinitionName方法获取到集合
之后循环判断,根据规则判断集合中的每一个BeanName是不是配置增强类,
经过判断,最后只获得启动类。
(如下图Debug,我们的启动类就叫springbootDataApplication)
之后就开始解析
解析每一个被@Configuration修饰的类,我们的启动类也被修饰了
用解析器,解析启动类
里面通过递归来逐层解析@PropertySource,@ComponentScan,@Import,@Bean等注解
解析完成后到下面这个方法,这个方法比较重要
各种方法调用
进入getCandidateConfigurations方法中
注意这里的SpringFactoryLoader,就是前面setInitializers那部分,读取spring.factory,然后
前面已经读取了spring.factory放到缓存中了。
根据类型把EnableAutoConfiguration取出来
注意取出来多少个EnableAutoConfiguration?一百多个
但是不会全部加载,pom里依赖什么,我加入什么
所以在getExclusions中判断去除哪些东西,remove它们。
最后configurations里就是我们需要用到的配置
之后获取实例
总结自动装配原理
1、当启动springboot应用程序的时候,会先创建SpringApplication的对象,在对象的构造方法中会进行某些参数的初始化工作,最主要的是判断当前应用程序的类型以及初始化器和监听器,在这个过程中会加载整个应用程序中的spring.factories文件,将文件的内容放到缓存对象中,方便后续获取。
2、SpringApplication对象创建完成之后,开始执行run方法,来完成整个启动,启动过程中最主要的有两个方法,第一个叫做prepareContext,第二个叫做refreshContext,在这两个关键步骤中完整了自动装配的核心功能,前面的处理逻辑包含了上下文对象的创建,banner的打印,异常报告期的准备等各个准备工作,方便后续来进行调用。
3、在prepareContext方法中主要完成的是对上下文对象的初始化操作,包括了属性值的设置,比如环境对象,在整个过程中有一个非常重要的方法,叫做load,load主要完成一件事,将当前启动类做为一个beanDefinition注册到registry中,方便后续在进行BeanFactoryPostProcessor调用执行的时候,找到对应的主类,来完成@SpringBootApplicaiton,@EnableAutoConfiguration等注解的解析工作。
4、在refreshContext方法中会进行整个容器刷新过程,会调用中spring中的refresh方法,refresh中有13个非常关键的方法,来完成整个spring应用程序的启动,在自动装配过程中,会调用invokeBeanFactorvPostProcessor方法,在此方法中主要是对ConfigurationClassPostProcessor类的处理,这次是BeanFactoryPostProcessor的子类也是BeanDefinitionRegistryPostProcessor的子类,在调用的时候会先调用BeanDefinitionRegistryPostProcessor中的postProcessBeanDefinitionRegistry方法,然后调用postProcessBeanFactory方法,在执行postProcesskeanDefinitionRegistry的时候回解析处理各种注解,包含@PropertySource,@ComponentScan,@ComponentScans,@Bean,@lmport等注解,最主要的是@Import注解的解析文章来源:https://www.toymoban.com/news/detail-412311.html
5、在解析@lmport注解的时候,会有一个getlmports的方法,从主类开始递归解析注解,把所有包含@lmport的注解都解析到,然后在processlmport方法中对lmport的类进行分类,此处主要识别的时候AutoConfigurationlmportSelect归属于ImportSelect的子类,在后续过程中会调用deferredlmportSelectorHandler中的process方法,来完整EnableAutoConfiguration的加载文章来源地址https://www.toymoban.com/news/detail-412311.html
到了这里,关于一文足够,SpringBoot自动装配底层源码的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!