spring启动分两个阶段
1、配置文件解析、注解解析阶段, 生产 BeanDefinition对象 spring配置文件中使用的占位符替换工作就是在这个阶段完成的 (数据源的配置)
2、spring用之前产生的BeanDefinition对象完成bean的初始化和组装工作
比如 事务,proxy等就是在这个阶段完成的
本文我只想缕清其大致过程,不关注具体实现细节,大致过程弄清楚了,其余细节有兴趣可以去翻翻详细源码
下面以springboot的启动过程为例缕缕以上两个过程,
我们知道启动springboot项目很简单,初始化SpringApplication对象,并调用它的run方法
进入到run方法
图中
标识1主要记录容器启动时长,并打印日志
标识2做了一些前置的准备工作,大致了解一下就可以了
标识3用来刷新容器,此处我们重点分析,跟进去最终发现调用的是AbstractApplicationContext的refresh方法
1、this.postProcessBeanFactory(beanFactory) 跟进去是空实现,留给子类自己去实现
2、this.invokeBeanFactoryPostProcessors(beanFactory) 调用所有的BeanFacotryPostProcessor,其中ConfigurationClassPostProcessor值得重点关注,后续讲到
3、 this.registerBeanPostProcessors(beanFactory)注册BeanPostProcessors到容器中
4、this.initMessageSource();初始化messageSource
5、initApplicationEventMulticaster(); 初始化 事件分发器
6、onRefresh(); 通知子类刷新容器
7、registerListeners(); 注册事件监听器
8、finishBeanFactoryInitialization(beanFactory);初始化单例对象
9、finishRefresh();收尾工作,初始化LifecycleProcessor并调用器onRefresh方法
第一步看是怎么完成配置类的加载、解析、创建BD对象并注册到容器中的
我们进入到invokeBeanFactoryPostProcessors方法里面
我们看到调用了PostProcessorRegistrationDelegate的invokeBeanFactoryPostProcessors方法其中通过this.getBeanFactoryPostProcessors()获取到前面准备好的BeanFactoryPostProcessors列表跟进去瞧瞧
有个很显眼的PostProcessor对象BeanDefinitionRegistryPostProcessor调用了他的postProcessBeanDefinitionRegistry方法跟进去发现调用其子类实现ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry
processConfigBeanDefinitions看到这个方法心里就有谱了,跟进去代码比较多,但是可以看到其中非常重要的一段代码
1、首先实例化ConfigurationClassParser对象并调用其parse方法用来解析 Configuration配置类并将其存放在内部的一个Map对象configurationClasses中以备下一步使用
2、实例化ConfigurationClassBeanDefinitionReader调用其loadBeanDefinitions方法并将上一步解析得到的Map对象的keySet作为参数传入
遍历ConfigurationClass列表并调用loadBeanDefinitionsForConfigurationClass方法如下
不管怎么说此处就是分析ConfigurationClass并将ConfigurationClass所描述的配置类构建成BeanDefinition对象并注册到容器中,这里说的都是Configuration注解的类,那我们还有很多Component,Service,Controller等注解的类呢,它们是怎么加载和解析的呢?
我们再来看看ConfigurationClassParser的parser方法最后调用doProcessConfigurationClass
扫描ComponentScans定义下的所有类componentScanParser.parse()方法解析成BeanDefinitionHolder集合最后又调用processConfigurationClass方法接上前面的流程完成BeanDefintion的注册,到此完成上述过程中的第一步
再来看第二步实例化及初始化Bean并将Bean注册到容器中的过程,通过代码的跟入发现都调用了AbstractBeanFactory的getBean方法,顺着一直跟进到getSingleton方法,终于在里面找到addSingleton方法将Bean的名称和对象传进来,看看下面这段代码
首先在缓存Map中获取,如果为空则调用singletonFactory.getObject()方法创建一个新对象,最后如果是新创建的对象则调用addSingleton进行注册,存储到本地的Map对象中,然后等我们调用context.getBean,其实就是在这个Map对象中获取的
在创建新对象的时候会调用initializeBean方法我们来看看这个方法的实现
这里可以看到获取所有的BeanPostProcessor,并调用postProcessBeforeInitialize()和postProcessAfterInitialization()方法对bean进行增强处理。
总结
简单的说第一阶段就是加载配置类文件,然后调用解析器解析配置文件生成BeanDefinition对象,并调用BeanFactoryPostProcessor进行处理之后存储在Map中
然后第二阶段根据BeanDefinition的定义通过反射进行Bean的实例化,并在初始化工作中调用BeanPostProcessor进行增强和组装工作。
好了,这篇文章就到这里了。