1. 简介
之前其实有写过Spring Bean的生命周期:Spring 容器Bean与生命周期。
当时太过于关注细节的实现,而进入了Spring庞大的体系之中,对于还不太了解Spring的朋友不太友好,也不方便记忆。
所以,这一次以应用为目的来重新梳理一下Spring核心流程。
因为对于实用主义来说,暂时并不想关心那么多Spring的细节,现在只希望能做2件事情:
- 当遇到问题了能快速知道大致那个步骤出现了问题,知道debug入口,以便于快速定位问题
- 知道一些Spring的重要扩展点,以便于更好的根据自己的需求完成自定义
2. 开始
想想我们使用Spring最常用的操作是什么?
无非就是我们添加一些xml、properties、注解配置。
然后,Spring解析这些配置帮我们创建相应的对象,并且用一个容器(上下文,BeanFactory,ApplicationContext)统一来管理。
如果,我们想要知道Spring怎样根据我们的配置,创建出我们需要的对象,那就得对Spring怎样创建上下文的流程有所了解。
所以,下面我们来简单看一下Spring创建上下文的核心流程。
注意:不要太过于关注每一个分支的细节,需要的时候再细看,否则就会没完没了的陷入一个又一个的分支细节之中。
不关心细节,只关心流程。
所以我们这里先不管是使用xml配置的ClassPathXmlApplicationContext,还是使用注解的AnnotationConfigApplicationContext,亦或是其他的Context。
我们这里只关注一个抽象类:AbstractApplicationContext,并且只关注它的refresh方法。
3. AbstractApplicationContext的refresh
这是一个典型的模板方法模式,在refresh方法中定义了各种Context的创建流程,具体怎么实现,就看个人。
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
prepareRefresh();
// 告诉子类该刷新BeanFactory了
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
prepareBeanFactory(beanFactory);
try {
// 告诉子类BeanFactory准备完成了
postProcessBeanFactory(beanFactory);
// 统计信息
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// 调用BeanFactoryPostProcessor来进一步定义BeanFactory
invokeBeanFactoryPostProcessors(beanFactory);
// 创建注册BeanPostProcessor
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// 国际化设置
initMessageSource();
// 初始化事件多播
initApplicationEventMulticaster();
// 告诉子类可以实例化其他一些特殊Bean了
onRefresh();
// 检查注册事件监听器
registerListeners();
// 实例化所有剩下的非懒加载单例bean
finishBeanFactoryInitialization(beanFactory);
// 发布相应的事件
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// 消耗已经创建的bean
destroyBeans();
// 重置 'active' 标识
cancelRefresh(ex);
throw ex;
}
finally {
// 重置哪些可能已经不需要的公共缓存
resetCommonCaches();
contextRefresh.end();
}
}
}
4. refresh方法说明
顺序
调用方法
说明
1
prepareRefresh
处理properties的占位符,验证上下文环境中所有的properties是否可以解析,设置早期事件监听器
2
obtainFreshBeanFacotry
获取的BeanFactory,主要是为了得到BeanDefinition这么一个玩意儿,解析创建BeanDefinition不一定发生在这一步
3
prepareBeanFactory
BeanFactory定制化工作
4
postProcessBeanFactory
BeanFactory准备好了,如果还想在对BeanFactory进行定制可以在这里完成
5
invokeBeanFactoryPostProcessors
BeanFctoryPostProcessor在这个地方被执行,可以处理一些特殊的bean,如EventListenerMethodProcessor的EventListenerFactory
6
registerBeanPostProcessors
实例化和注册BeanPostProcessor,并处理它们的执行顺序
7
initMessageSource
国际化设置
8
initApplicationEventMulticaster
初始化事件多播器
9
onRefresh
用于初始化一些特定Bean,如SpringBoot重写该方法启动Tomcat容器
10
registerListeners
检查容器中所有的事件监听器,并注册
11
finishBeanFactoryInitialization
实例化剩下所有非懒加载的单例
12
finishRefresh
发布容器刷新完成事件
13
resetCommonCaches
清除公共缓存数据
AbstractApplicationContext为prepareBeanFactory提供了默认实现,主要完成下面的工作:
设置类加载器
设置SpEL解析器和属性注册解析器
利用BeanPostProcessor的特性给各种Aware接口的实现类注入ApplicationContext中对应的属性
设置各种Aware接口的实现类为忽略自动装配
手动注册BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext
探测LoadTimeWeaver,并完成动态织入
注册其他工具bean:ConfigurableEnvironment、系统属性、系统环境变量、ApplicationStartup(容器启动监控)
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { // 设置类加载器 beanFactory.setBeanClassLoader(getClassLoader()); if (!shouldIgnoreSpel) {//设置SpEL解析器 beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader())); } // 属性注册解析器 beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())); // 配置BeanFactory的回调类 beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this)); // 配置忽略自动装配的接口 beanFactory.ignoreDependencyInterface(EnvironmentAware.class); beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class); beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class); beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class); beanFactory.ignoreDependencyInterface(MessageSourceAware.class); beanFactory.ignoreDependencyInterface(ApplicationContextAware.class); beanFactory.ignoreDependencyInterface(ApplicationStartup.class); // BeanFactory中手动注册下面这些类 beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory); beanFactory.registerResolvableDependency(ResourceLoader.class, this); beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this); beanFactory.registerResolvableDependency(ApplicationContext.class, this); // 注册一个早期的BeanPostProcessor用来探测beans是不是一个ApplicationListeners beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this)); // 探测LoadTimeWeaver,如果是准备织入 if (!IN_NATIVE_IMAGE && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); // 设置临时类加载器 beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } // 注册默认的environment beans ConfigurableEnvironment if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment()); } // 注册系统属性bean if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties()); } //注册系统环境变量bean if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment()); } // 注册启动指标监测bean if (!beanFactory.containsLocalBean(APPLICATION_STARTUP_BEAN_NAME)) { beanFactory.registerSingleton(APPLICATION_STARTUP_BEAN_NAME, getApplicationStartup()); } }
TIPS:
- PropertySource,主要处理properties文件中的键值对和系统变量
- PropertySource会注册到上下文环境中,我们可以通过Environment这个类直接获取
- systemProperties与systemEnvironment有啥区别?就是System.getProperties()与System.getenv(),更具体就是systemProperties是user.dir这些,systemEnvironment是PATH、OS这些
5. 核心流程梳理
前面我们已经大致从代码层面简单的介绍了Spring上下文启动的流程,下面用一个图做一个简单的梳理。
6. Bean生命周期
前面在介绍Spring上下文启动的流程,我们为了尽量聚焦于核心的流程,并没有细化进入介绍和Bean生命周期的流程。
其实,对于定制化来说,我们更多的是针对Bean的定制化,你可以通过【】这篇文章了解Bean的生命周期。
当然,你也可以通过下面两个类来详细了解:
AnnotationConfigApplicationContext:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
ClassPathXmlApplicationContext:
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");