源码浅析——容器刷新流程概览
本文是spring源码浅析系列的第一篇。全文总共分为四部分,第一部分总结一下 Spring的好处,毕竟如果不好用,它也不会迅速在开发框架里迅速脱颖而出。第二部分主要介绍一下Spring组织代码的底层软件开发逻辑,可以让你知其然更知其所以然。第三部分主要通过spring组成部分的接口与类的继承关系的对比,来说明spring中两驾马车BeanFactory和ApplicationContext间的区别和联系。第四部分,也是本文扣题部分,讲解spring应用最终成功创建依赖于bean及其运行容器,是经由什么样一个原理完成创建并发生关联的,这部分有些环节介绍的较为详细,有些部分一笔带过,原因是有些通过方法名称你基本知道它存在的作用,同时实现逻辑也并不复杂,所以就只做简略概括。文中使用版本是Spring Boot 2.1.2.RELEASE(即Spring 5.1.4),以默认配置启动,分析一下框架的工作原理。
同时对于框架学习也表达下自己的看法,其实你厘清这种框架运行原理的东西的难度,相比于你理解一些高难度的排序算法是简单很多的。原理的理解更类似于背八股文,你多记忆记忆就有感觉了,甚至你可能对于依赖倒置这种软件开发中的思想有一定理解的话,你其实已经掌握了核心要点。但是为什么还要把这个单独提出一个系列来说呢?答案在于,毕竟卓越东西构建的思想体系和一些问题的解决思维能够存留下来且历久弥新,其中必有我们可以借鉴和学习的地方,那才是真正想摆到你面前想让你消化咀嚼出的东西。
SpringBoot几乎是现在java开发最广泛使用的主流框架,大厂更是趋之若鹜,我司的很多系统也都是基于SpringBoot进行搭建,如此出圈缘于何故?
- (1)简化配置,不需要编写太多的xml配置文件;
- (2)基于Spring构建,使开发者快速入门,门槛很低;
- (3)SpringBoot可以创建独立运行的应用而不需要依赖于容器;
- (4)内置tomcat服务器,不需要打包成war包,可以直接放到tomcat中运行;
- (5)提供maven极简配置,以及可视化的相关监控功能,比如性能监控,应用的健康程度等;
- (6)为微服务SpringCloud奠定了基础,使得微服务的构建变得简单;
- (7)Spring可以整合很多各式各样的框架,并能很好的集成;
- (8)活跃的社区与论坛,以及丰富的开发文档;
Spring以容器管理所有的bean对象,容器的实体是一个BeanFactory对象。但我们常用的容器是另外一个ApplicationContext,它的内部持有了BeanFactory,所有和BeanFactory相关的操作都会委托给BeanFactory来完成。看到委托这个词,如果你以php为主语言开发过东西,甚至还有了好长一段时间的yii2框架,我想你对委托这个词一定不会陌生。其实这是软件系统开发设计中一个经常用到的思想,就是控制反转(Inversion Of Control,Ioc),而依赖注入(Dependence Injection,DI)是控制反转思想的一种实现方式。而对于yii2的服务定位器,实际就是依赖注入在具体框架上的一种具体实现,就是让类实例化的过程由原来软件开发架构中的上层依赖于底层,变成由底层依赖于上层,底层依赖于抽象层,底层依赖于接口。这样的实现有什么好处呢?很明显如果让上层依赖于底层,那么,底层很可能很小的改动,都会牵一发而动全身,整个耦合链路的上层部分都需要修改,这明显有违软件行业“高内聚,低耦合”的指导思想。实际这里提到的yii2的服务定位器以及我们现在当前篇章讲的无论是xml方式还是注解方式,完成依赖类的实例化最终建立起程序运行的基本环境,实际支撑起作用的都是控制反转容器(Ioc Container),特别是当项目比较大时,依赖关系可能很复杂,而Ioc Container提供了动态的创建、注入依赖单元、映射依赖关系等功能,减少了许多代码量。
ApplicationContext的继承关系图
ApplicationContext
是一个接口,ClassPathXmlApplicationContext
和AnnotationConfigApplicationContext
是两个比较常用的实现类,前者基于xml
来使用,后者是基于注解来使用。而在 SpringBoot
默认是应用了后一种。而xml
方式和注解方式的异同点,或者是注解对于xml
方式的增强点在虽然两者都能让开发者从宏观上对于项目有概况的了解,但是xml
方式直接runtime
时才会暴露出问题,而注解方式在compile
期间就能让你对配置进行验证。
BeanFactory的继承关系图
从图中标明关系我们可以获得如下一些信息
BeanFactory | ApplicationContext | |
---|---|---|
关系 | 两者都是spring的核心接口,都可以当做spring容器。其中ApplicationContext是BeanFactory的子接口,包含BeanSpring的所有特性。BeanFactory是Spring的底层接口,包含各种Bean定义,读取配置文档,管理bean配置文档,管理bean加载、实例化、控制bean的生命周期,维护bean之间的依赖关系。ApplicationContext作为BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了完整的框架功能:①继承MessageSource,因此支持国际化。②统一的资源文件访问方式。③提供在监听器中注册bean的事件。④同时加载多个配置文件。⑤载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。 | same |
异同 | 加载机制:BeanFactory采用的是延迟加载形式来注入Bean的,即只在使用某个Bean时(调用getBean),才对该Bean进行加载实例化。但是这样会导致我们不能发现一些Spring配置的问题。如果Bean的某个属性没有注入,BeanFactory加载后,直至第一次使用调用getBean方法才会抛出异常内存占用少,启动速度快创建方式:以编程方式都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间区别是BeanFactory是手动注册 | 加载机制:ApplicationContext,它是容器启动时,一次性创建所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。ApplicationContext启动后预载入所有的单实例Bean,通过载入单实例bean,确保当你需要的时候,你就不用等待,因为它们已经创建好了,它还可以为Bean配置lazy-init=true来让bean延迟实例化;内存占用多,启动速度慢创建方式:以声明方式,如使用ContextLoader都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间区别是ApplicationContext是自动注册 |
下面进入正式的源码分析。
一、概述
代码语言:javascript复制@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
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();
}
}
}
二、prepareRefresh()
刷新前的预处理:
1.initPropertySources()初始化一些属性设置
2.getEnviroment().validateRequiredProperties();//检查属性的合法等
3.earlyApplicationEvents = new LinkedHashSet();//保存容器中的一些早期的事件
三、obtainFreshBeanFactory()
1. refreshBeanFactory();//刷新【创建】BeanFactory
- 创建了一个this.beanFactory = new DefaultListableBeanFactory();
- 设置id
2.getBeanFactory()返回刚才GenericAppliationContext创建的BeanFactory对象
3.将创建的BeanFactory【DefaultListableBeanFactory】返回
四、prepareBeanFactory(beanFactory)
BeanFactory的预准备的工作(BeanFactory进行的一些设置):
1.设置BeanFactory的类加载器、支持表达式解析器
2.添加部分BeanPostProcessor【ApplicationContextAwareProcessor】
3.设置忽略的自动装配的接口EnviromentAware、EmbededValueResolveAware、ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware
4.注册可以解析的自动装配:我们能直接在任何组件中自动注入:BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext
5.添加BeanPostProcessor【ApplicationListenerDetector】
6.添加编译的Aspect
7.组BeanFactory中注册一些能用的组件
- enviroment【ConfigurableEnviroment】
- systemProperties【Map】
- systemEnviroment【Map】
五、postProcessBeanFactory(beanFactory)
BeanFactory
准备工作完成后进行的后置处理工作:调用BeanDefinitionRegistryPostProcessor
的postProcessBeanDefinitionRegistry(registry)
方法和BeanFactoryPostProcessor
的postProcessBeanFactory(beanFactory)
方法,允许beanFactory
准备完成后对beanFactory
进行一些修改,比如在bean
初始化之前对beanFactory
中的beanDefinition
进行修改。@ComponentScan
和@EnableAutoConfiguration
这两个注解都是在此实现的。 这里有非常重要的后置处理器:ConfigurationClassPostProcessor
,作用是在这里解析@Configuration
标签、@PropertySource
、@ComponentScan
、@import
、@ImportResource
、@Bean
这些注解都和@Configuration
相关,都在此处解析。我们常用的@Component
注解和SpringBoot
的自动配置,都在这里被实现ConfigurationClassPostProcessor
会以我们在Spring
启动时传入的启动类作为解析@Configuration
的根节点SpringApplication.run(SpringTest.class,args)
,递归扫描其他@Configuration
节点,最终把所有用户自定义的bean
以Map<beanName,beanDefinition>
的形式保存在容器中。
- 子类通过重写这个方法来在
BeanFactory
创建并准备完成后做进一步的设置
六、invokeBeanFactoryPostProcessors(beanFactory)
1.先执行BeanDefinitionRegistryPostProcessor的方法
- 获取所有
BeanDefinitionRegistryPostProcessor
- 看先执行实现了
PriorityOrdered
优先级接口的BeanDefinitionRegistryPostProcessor
、postProcessor.postProcessBeanDefinitionRegistry(registry)
- 在执行实现了
Ordered
顺序接口的BeanDefinitionRegistryPostProcessor
、postProcessor.postProcessBeanDefinitionRegistry(registry)
- 最后执行没有实现任何优先级或者是顺序接口的
BeanDefinitionRegistryPostProcessors
、postProcessor.postProcessBeanDefinitionRegistry(registry)
2.再执行BeanFactoryPostProcessor的方法
- 获取所有
BeanFactoryPostProcessor
- 看先执行实现了
PriorityOrdered
优先级接口的BeanFactoryPostProcessor
、postProcessor.postProcessBeanFactory
- 在执行实现了
Ordered
顺序接口的BeanFactoryPostProcessor
、postProcessor.postProcessBeanFactory()
- 最后执行没有实现任何优先级或者是顺序接口的
BeanFactoryPostProcessor
、postProcessor.postProcessBeanFactory()
七、registerBeanPostProcessors(beanFactory)
注册BeanPostProcessor
到容器中,你可能要问,什么是BeanPostProcessor
? 答:BeanPostProcessor是一种接口,定义了回调方法的接口,可以通过实现这些方法,从而达到在Bean实例化期间修改Bean的属性。如果想自已实现自定义的类,可以用下面的方式:
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof UserComponent) {
UserComponent userComponent = (UserComponent) bean;
userComponent.setUserName("1060460048@qq.com-postProcessBeforeInitialization");
return userComponent;
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof UserComponent) {
UserComponent userComponent = (UserComponent) bean;
userComponent.setUserName("1060460048@qq.com-postProcessAfterInitialization");
return userComponent;
}
return bean;
}
}
针对于两个初始化方法进行一点说明:
postProcessBeforeInitialization
:在 Bean 实例后调用初始化方法之前进行处理。postProcessAfterInitialization
:在 Bean 实例化后调用初始化方法之后进行处理。
总结此步作用:注册BeanPostProcessor到BeanFactory中,但是没有执行
八、initMessageSource()
初始化MessageSource组件,目的是为了对于消息绑定、消息解析、多语言版本等功能做支持,流程如下:
1.获取BeanFactory
2.先看容器中是否有id
为MessageSource
的,类型为MessageSource
的组件,如果有则赋值给messageSource
,如果没有则自己创建一个DelegatingMessageSource
,意在将多语言版本处理委托给父级MessageSource
,如果还查找不到,则做降级处理,只处理默认的字符串或者是抛出异常
九、initApplicationEventMulticaster()
这里和上面initMessageSource
的原理类似,都要查找指定名称的Bean,找不到则使用默认的SimpleApplicationEventMulticaster
。
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
//查找是否存在id为applicationEventMulticaster的bean对象
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isDebugEnabled()) {
logger.debug("Using ApplicationEventMulticaster [" this.applicationEventMulticaster "]");
}
}
else {
//默认使用SimpleApplicationEventMulticaster,并注册为单例
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate ApplicationEventMulticaster with name '"
APPLICATION_EVENT_MULTICASTER_BEAN_NAME
"': using default [" this.applicationEventMulticaster "]");
}
}
}
十、onRefresh()
初始化themeSource
主题源,默认使用ResourceBundleThemeSource
十一、registerListeners()
注册ApplicationListener beans到广播集合中,并执行earlyApplicationEvents中的事件。
代码语言:javascript复制 /**
* Add beans that implement ApplicationListener as listeners.
* Doesn't affect other listeners, which can be added without being beans.
*/
protected void registerListeners() {
// Register statically specified listeners first.
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let post-processors apply to them!
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String listenerBeanName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
// Publish early application events now that we finally have a multicaster...
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if (earlyEventsToProcess != null) {
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}
十二、finishBeanFactoryInitialization(beanFactory)
这个方法可以说是基于我们前面介绍过的obtainFreshBeanFactory
、invokeBeanFactoryPostProcessors
、registerBeanPostProcessors
之后核心的核心,该方法会实例化所有剩余的非懒加载单例bean
。除了一些内部bean、实现BeanFactoryPostProcessor
接口的bean
、实现BeanPostProcessor
接口的bean
,其它非懒加载单例bean
都会在这个方法中被实例,同时BeanPostProcessor
的触发也发生在这个方法中。
总之,这步在为创建最终的bean实例做准备,引入了FactoryBean这一特殊的bean,同时也获取BeanDefinition的MergedBeanDefinition,最后将BeanDefinition统一转化成RootBeanDefinition。此部分还解决了一个非常关键的循环引用的问题,利用的是引用进来被使用的缓存。而本部分涉及的缓存主要有以下部分:
mergedBeanDefinition
缓存,作用是把beanName
处理进bean
定义中beanDefinitionMap
缓存,作用是把beanName
处理进BeanDefinition
singletonObject
缓存,作用是把未进行初始化、属性赋值的beanName
处理进单例bean
对象singletonFactories
缓存,作用是把beanName
处理进ObjectFactory
singletonsCurrenctlyCreation
缓存,把当前正在创建的bean
对象合并进beanName
集合
十三、finishRefresh()
代码语言:javascript复制/**
* Finish the refresh of this context, invoking the LifecycleProcessor's
* onRefresh() method and publishing the
* {@link org.springframework.context.event.ContextRefreshedEvent}.
*/
protected void finishRefresh() {
// Clear context-level resource caches (such as ASM metadata from scanning).
clearResourceCaches();
// Initialize lifecycle processor for this context.
initLifecycleProcessor();
// Propagate refresh to lifecycle processor first.
getLifecycleProcessor().onRefresh();
// Publish the final event.
publishEvent(new ContextRefreshedEvent(this));
// Participate in LiveBeansView MBean, if active.
LiveBeansView.registerApplicationContext(this);
}
本文至此,已完成对Springboot核心代码至finishRefresh部分的解读。但其实走到这一步,也才把容器准备好,接下来要往容器中注入bean,下篇文章将就此进行分析,感谢阅读。