【源码】走一遍refresh()方法的源码就能弄懂Spring容器的创建过程
Spring框架的两大核心思想,一个是IOC控制反转,另一个就是AOP面向切面编程。而IOC控制反转指的是将创建对象的操作交给Spring容器来解决,容器同一对所有的对象进行管理,我们无需关心创建对象时,对象之间的依赖关系,只需要在使用对象时从容器中获取即可。要想从容器中获取对象来使用,那就必须首先创建出这个Spring容器,这篇文章就来从源码角度出发,来看看Spring容器的创建过程。
创建Bean可以使用XML配置文件,也可以使用注解的方式,而平时开发中使用注解来创建、管理Bean已经成为主流。接下来我们通过加载配置类的方式来创建一个基于注解的Spring容器。我们就以AnnotationConfigApplicationContext
这个类为入口,来看看Spring容器到底是怎么被创建出来的。
我们说IOC容器就像一个“大箱子”,里面保存着大量的已经创建好的Bean对象,我们在使用的时候直接“开箱即用”。但是IOC在一开始创建的时候是空空如也的,它需要将一些Bean对象创建并保存起来。Java项目中这么多的类,不能一股脑的,把有用没用的类当看作是Bean对象全都给注册到容器里面来,那么IOC是如何识别该把哪些类封装成Bean注册到IOC容器里面来呢?这就需要特定的注解了。比如@Service、@Controller、@Repository等等,Spring需要一个注解配置读取器,读取加了特定注解的类,转换为BeanDefinition对象。这个BeanDefinition对象存储了bean对象的所有信息。这里你可以把注解配置读取器类比于JVM中的类加载器,BeanDefinition对象类比于JVM方法区中的类模板。光知道需要读取哪些标注了特定的注解类还不够,首先Spring容器需要先根据包目录找到这些类,所以还需要一个类路径扫描器。当我们根据类路径扫描器找到了标注特定的注解的类之后,我们就要将这个类通过注解配置读取器转换为BeanDefinition对象了,此时我们要想生成Bean对象,还需要有一个BeanFactory工厂,来帮助我们创建出Bean对象。
所以,根据上面我们说的,我们先来看一下这一行创建Spring容器的代码:
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
我们加载了一个配置类,这个配置类配置了类路径扫描的包名,以及适应注解标注了需要注册哪些类
我们可以从IOC容器创建的源码中可以看到,首先是根据传入的配置类使用注解配置读取器读取配置类信息,来解析用户传入的 Spring 配置类,其实也是将配置类解析成一个 BeanDefinition
然后注册到容器中
总结一下就是:IOC容器创建可以分为初始化容器+刷新容器
在调用IOC容器的构造方法初始化一个容器时,会传入一个配置类。基于注解的IOC容器会先执行父类初始化方法,实例化一个BeanFactory工厂、BeanDefinitionReader注解配置读取器、和一个ClassPathBeanDefinitionScanner扫描器,同时向容器中添加两个后置处理器,一个ConfigurationClassPostProcessor
配置类后置处理器,它是一个BeanFactory后置处理器,用来完成Bean的扫描和注册工作;另一个是AutowiredAnnotationBeanPostProcessor
自动装配有关的后置处理器,它是一个Bean后置处理器,用来自动注入标注了@Autowire
注解的Bean。有了这俩个后置处理器,然后就会把配置类注册到容器中,因为配置类本身也使用的@Configuration
注解进行了标识,所以配置类本身也是一个Bean。至此IOC容器的创建的第一阶段初始化容器算是结束了。
我们通过源码可以看到实际上创建容器的核心是refresh()
方法,是用来刷新容器的。当refresh()
方法刷新完容器,那么这个Spring容器才算是真正的创建了出来。弄懂了refresh()
方法你也就弄懂了容器的创建过程。
refresh()
方法源码中总共包含了12个方法,也就是说容器的刷新有12个步骤。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
//1.刷新前的预处理
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
//2.获取 beanFactory,即前面创建的【DefaultListableBeanFactory】
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
//3.预处理 beanFactory,向容器中添加一些组件
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
//4.子类通过重写这个方法可以在 BeanFactory 创建并与准备完成以后做进一步的设置
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
//5.执行 BeanFactoryPostProcessor 方法,beanFactory 后置处理器
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
//6.注册 BeanPostProcessors,bean 后置处理器
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
//7.初始化 MessageSource 组件(做国际化功能;消息绑定,消息解析)
initMessageSource();
// Initialize event multicaster for this context.
//8.初始化事件派发器,在注册监听器时会用到
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
//9.留给子容器(子类),子类重写这个方法,在容器刷新的时候可以自定义逻辑,web 场景下会使用
onRefresh();
// Check for listener beans and register them.
//10.注册监听器,派发之前步骤产生的一些事件(可能没有)
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
//11.初始化所有的单实例 bean
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
//12.发布容器刷新完成事件
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
接下来我们就一步一步的来分析refresh()
方法
步骤1:刷新前的预处理
refresh()
方法会在刷新前,调用prepareRefresh()
方法进行刷新前的预处理工作
prepareRefresh()
方法的预处理工作主要包括,像是记录容器启动的时间,打印一些容器启动日志,初始化一些属性并校验一些参数(有必要的话,留给子类去实现)等等,同时还可以创建一个早期应用事件链表,用来存放早期的一些应用事件,当后面事件派发器创建出来的时候,就会立即派发这些早期的事件。
步骤2:获取BeanFactory
refresh()
方法执行完刷新前的预处理之后,会调用obtainFreshBeanFactory()
方法获取BeanFactory
obtainFreshBeanFactory()
方法会首先调用refreshBeanFactory()
方法来刷新BeanFactory
,会创建一个DefaultListableBeanFactory
对象,并设置序列化id
,用来唯一标识这个BeanFactory
。
然后obtainFreshBeanFactory()
然后调用getBeanFactory()
获取刚才创建出的DefaultListableBeanFactory
实例对象,然后将这个BeanFactory
对象返回
步骤3:预处理 beanFactory
在第二步我们获取到了BeanFactory,在使用BeanFactory之前我们需要对它进行一些预处理。比如设置BeanFactory的类加载器、各种后置处理器、以及自动装配的相关设置,这一步主要是给BeanFactory中添加一些组件。
步骤4:BeanFactory的后置处理
refresh()
方法调用postProcessBeanFactory(beanFactory)
方法,可以在BeanFactory
准备工作完成后进行后置处理工作。
子类通过重写这个方法来在BeanFactory
创建并预准备完成以后来做进一步的设置。
步骤5:执行BeanFactory后置处理器
refresh()
调用invokeBeanFactoryPostProcessors(beanFactory)
执行BeanFactoryPostProcessor
的方法
BeanFactoryPostProcessor
是BeanFactory
的后置处理器,它会在BeanFactory
标准初始化之后执行,标准初始化指的就是前面的前4步。也就是说,在BeanFactory准备好后,并且进行完预处理以及后置处理以后,就会执行BeanFactory的后置处理器。
首先会调用getBeanFactoryPostProcessors()
方法获取到所有的BeanFactoryPostProcessor
正是因为有BeanFactoryPostProcessor
这个接口的存在,才可以使得我们在Spring扫描完所有的bean转成BeanDefinition的时候,可以做一些自定义的后置操作。
BeanFactoryPostProcessor
这个接口又有一个子接口BeanDefinitionRegistryPostProcessor
BeanFactoryPostProcessor
这个接口可以把ConfigurableListableBeanFactory
暴露给我们,使得我们可以对BeanFactory进行一些后置处理。而子接口BeanDefinitionRegistryPostProcessor
可以把BeanDefinitionRegistry
这个注册器暴露给我们,使得我们可以获取到注册器,进行一些后置处理,比如按照我们的实际需要动态的注册Bean。
在获取到所有的获取到所有的BeanFactoryPostProcessor
之后:
(1)先执行BeanDefinitionRegistryPostProcessor
这个Bean定义注册器的后置处理器
首先判断当前的BeanFactory
是否是BeanDefinitionRegistry
如果是,那么就会遍历所有的BeanFactoryPostProcessor
,判断是否是对应的BeanDefinitionRegistryPostProcessor
类型,并加入到集合中
1)首先处理实现了PriorityOrdered
优先级接口的BeanDefinitionRegistryPostProcessor
,后置处理器会回调它对应的后置处理方法
2)然后处理实现了Ordered
顺序接口的BeanDefinitionRegistryPostProcessor
3)最后处理没有实现任何接口的常规BeanDefinitionRegistryPostProcessor
(2)执行完BeanDefinitionRegistryPostProcessor
之后,再执行BeanFactoryPostProcessor
获取到BeanFactoryPostProcessor
类型的beanFactoryPostProcessor
后置处理器
接下来和上面执行BeanDefinitionRegistryPostProcessor
类型的后置处理器一样,也是先处理实现了PriorityOrdered
接口BeanFactoryPostProcessor
,再处理实现了Ordered
接口的BeanFactoryPostProcessor
,最后处理没有实现任何接口的BeanFactoryPostProcessor
。
按照实现的接口分为,分别回调后置处理器对应的后置处理方法
步骤6:向容器中注册Bean后置处理器
refresh()
调用registerBeanPostProcessors(beanFactory)
方法,注册Bean的后置处理器,后置处理器是用来拦截bean创建过程的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dByQKjpu-1632686549486)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210926221735976.png)]
不同接口类型的BeanPostProcessor
,即继承了BeanPostProcessor
接口的不同子接口,在Bean创建前后的执行时机是不一样的
首先获取所有的BeanPostProcessor
Bean的后置处理器也是按照实现PriorityOrdered
接口、Ordered
接口、没有实现任何接口进行分类,并加入到对应的集合中
1)分好类之后,也是先注册实现了priorityOrdered
优先级接口的BeanPostProcessor
,调用registerBeanPostProcessors()
方法进行注册
registerBeanPostProcessors()
方法调用addBeanPostProcessor()
方法将BeanPostProcessor
添加到BeanFactory
中
2)再注册实现了Ordered
优先级接口的BeanPostProcessor
3)最后注册没有实现任何优先级接口的BeanPostProcessor
步骤7:初始化MessageSource组件
refresh()
调用initMessageSource()
初始化MessageSource
组件,这个组件用来处理国际化,以及消息绑定和消息解析
步骤8:初始化事件派发器
refresh()
方法调用initApplicationEventMulticaster()
方法初始化事件派发器
在整个容器创建过程中,Spring 会发布很多容器事件,如容器启动、刷新、关闭等,这个功能的实现依赖于ApplicationEventMulticaster
广播器组件,通过它来派发事件通知。
首先获取BeanFactory
然后从BeanFactory
中id
为applicationEventMulticaster
且类型是ApplicationEventMulticaster
的事件派发器,也就说会先判断是否有自定义的事件派发器
如果上一步容器中没有配置事件派发器,就会创建一个SimpleApplicationEventMulticaster
类型的事件派发器,并将这个事件派发器添加到BeanFactory
中,以后其它组件可以直接自动注入来使用
步骤9:调用onRefresh()自定义容器刷新时逻辑
onRefresh()
这个方法和步骤4中的postProcessBeanFactory()
一样,都是留给子容器(子类)的,子类重写这个方法,可以在容器刷新的时候可以自定义逻辑,一般web场景下会使用。
步骤10:注册监听器
refresh()
调用registerListeners()
方法,将项目中所有的ApplicationListener
监听器注册到容器中。如果用户想监听容器事件,需要实现 ApplicationListener
接口并放入到容器中,在这里会被 Spring 扫描到,添加到 ApplicationEventMulticaster
广播器里,以后就可以发布事件通知,对应的 Listener 就会收到消息进行处理。
首选从容器中获取到所有的ApplicationListener
,并将每一个监听器添加到事件派发器中
我们在步骤1中刷新前预处理阶段,已经创建了一个保存早期事件的链表,并且在步骤8中已经初始化了事件派发器,在注册监听器的这一阶段,如果有一些早期的事件,就会直接先派发这些早期事件。
步骤11:初始化所有的单实例Bean
refresh()
方法调用finishBeanFactoryInitialization()
方法,初始化剩下的所有单实例bean
在上面的步骤中,Spring 的大多数组件都已经初始化完毕了,剩下来的这个步骤就是初始化所有剩余的单实例 bean,在Spring中初始化一个 bean 对象是非常复杂的,如循环依赖、bean后置处理器运用、aop 代理等。执行完这个方法,容器里面的bean就都初始化完毕了。
finishBeanFactoryInitialization()
方法先做一些BeanFactory
相关的设置,实际上是调用preInstantiateSingletons()
方法实例化剩下的所有单实例bean
首先获取到所有定义的bean,然后依次进行创建和初始化对象
然后遍历所有的bean,获取bean的定义信息
接着会判断当前bean是否不是抽象的,且是否是单实例的,而且是否不是懒加载的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2bYJ6wL4-1632686549524)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20210927005007194.png)]
如果是,则进一步判断当前bean
是否是FactoryBean
,即判断是否实现了FactoryBean
接口的bean
,如果是工厂bean,就会调用接口中getObject()
方法来创建对象
如果不是工厂bean就会调用getBean()
方法来创建对象
步骤12:发布容器刷新完成事件
refresh()
方法调用finishRefresh()
方法完成BeanFactory
的初始化创建工作,IOC容器就创建完成了
整个容器初始化完毕之后,会在这里进行一些扫尾工作,比如初始化生命周期处理器,发布容器刷新完成事件等。
finishRefresh()
方法首先调用initLifecycleProcessor()
来初始化和生命周期有关的处理器
initLifecycleProcessor()
方法默认从容器中找是否有自定义的id
为lifecycleProcessor
且类型为LifecycleProcessor
的组件,如果没有找到就会创建DefaultLifecycleProcessor
并将其注册到容器中
然后调用publishEvent()
方法发布容器刷新完成事件
最后调用registerApplicationContext()
来暴露一些特定的Bean,并完成了容器的创建
上面的12个步骤就是容器刷新的核心流程!
稍微总结一下Spring容器创建的过程:
- Spring容器在启动的时候,会先保存所有注册你来的Bean的定义信息
- Spring会在合适的时机创建这些Bean。如果用到了某个Bean的时候,就会利用getBean()方法来创建这个Bean,创建好以后保存在容器中,方便给其它组件使用。然后在刷新容器的时候会统一创建剩下的所有Bean。
- Spring容器中存在各种类型的后置处理器,有BeanFactory的后置处理器,有Bean的后置处理器,不同的后置处理器执行的时机也不相同。每一个Bean创建完成,都会使用各种后置处理器来进行处理,增强Bean的功能。
- Spring容器中还有很多监听器,负责监听事件,事件派发器负责事件的派发。