Spring context的refresh函数执行过程分析
今天看了一下Spring Boot的run函数运行过程,发现它调用了Context中的refresh函数。所以先分析一下Spring context的refresh过程,然后再分析Spring boot中run的流程。
首先我们找到spring-context组件的AbstractApplicationContext类下的refresh函数:
@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()
protected void prepareRefresh() {
// Switch to active.
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true);
......
// Initialize any placeholder property sources in the context environment.
initPropertySources();
// Validate that all properties marked as required are resolvable:
// see ConfigurablePropertyResolver#setRequiredProperties
getEnvironment().validateRequiredProperties();
// Store pre-refresh ApplicationListeners...
if (this.earlyApplicationListeners == null) {
this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
}
else {
// Reset local application listeners to pre-refresh state.
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}
// Allow for the collection of early ApplicationEvents,
// to be published once the multicaster is available...
this.earlyApplicationEvents = new LinkedHashSet<>();
}
可以看到,这个函数做了一些准备工作,记录了refresh的开始时间,调用了留给子类重写的initPropertySources()函数。
验证了环境中必要的参数,并且将earlyListeners加入到应用程序的监听器列表中。
obtainFreshBeanFactory()
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
return getBeanFactory();
}
可以看见,refreshBeanFactory()和getBeanFactory()都是abstract函数,这两个函数设计出来就是为了让子类重写,根据子类实现具体功能,从函数名可以推断出这两个函数一个是用来初始化beanFactory,另外一个则是拿到beanFactory的实例,我们以AbstractRefreshableApplicationContext类的代码为例:
refreshBeanFactory:
@Override
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {//如果beanFactory已经被初始化过,则在此销毁
destroyBeans();
closeBeanFactory();
}
try {//这里其实就是new了一个DefaultListableBeanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
//客制化beanFactory,例如设置能否循环引用,能否重写bean等
customizeBeanFactory(beanFactory);
//将xml中的bean定义加载到spring-core的注册中心中
loadBeanDefinitions(beanFactory);
//设置beanFactory,供getBeanFactory取得
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
getBeanFactory:不难想象,这个函数从this类中拿到beanFactory实例并返回。
prepareBeanFactory(beanFactory)
篇幅原因就不贴这部分代码,实在太长,这个函数主要做了如下几件事:
1. 设置了beanFactory的classloader
2. 设置了SpEL的解析器
3. 设置了PropertyEditor,这个是用来将xml里面的占位符解析成具体值的,例如xml里面写<... value="${predefined-var}"/>,在PropertyEditor可以将${predefined-var}解析为某个具体值,并且到时候生成的beanDefinition对应的field就是这个具体的值。这一步会发生在xml被转换为beanDefinition之前
4. 增加ApplicationContextAwareProcessor的BeanPostProcessor,这个是为了让实现了ApplicationContextAware接口的bean在初始化后自身被注入当前的ApplicationContext,实现对自己所在的Context的感知
5. 忽略下述接口的依赖注入:
EnvironmentAware.class
EmbeddedValueResolverAware.class
ResourceLoaderAware.class
ApplicationEventPublisherAware.class
MessageSourceAware.class
ApplicationContextAware.class
观察上述接口的特征,发现这些都是Aware系列接口,用于使Bean感知环境中的参数(例如当前Context)。自动装配不会对这些接口进行处理,实际上实现了这些接口的类会在Spring中有专门的函数进行处理。
6. 对于一些特定的接口实现,定义默认的注入值:
BeanFactory.class
ResourceLoader.class
ApplicationEventPublisher.class
ApplicationContext.class
这些接口是用来获取关于Spring本身相关的信息的,例如Spring本身的BeanFactory等。
7. 注册一些环境相关的bean,例如systemProperties、systemEnvironment和environment
postProcessBeanFactory(beanFactory)
这个函数实际上是空的:
/**
* Modify the application context's internal bean factory after its standard
* initialization. All bean definitions will have been loaded, but no beans
* will have been instantiated yet. This allows for registering special
* BeanPostProcessors etc in certain ApplicationContext implementations.
* @param beanFactory the bean factory used by the application context
*/
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
}
通过上面的注释,我们可以知道,当所有的xml已经被载入并且产生了对应的beanDefinition时,这个函数将会被调用,此时bean的实例都没有产生,在此处可以对beanDefinition的属性进行修改、抑或是注册特别的beanPostProcessor用于对实例化的bean做最终处理。
这里函数留空是为了让用户能够子类化,然后在里面写入自己需要的修改,典型的模板设计模式
invokeBeanFactoryPostProcessors(beanFactory)
调用所有在容器中注册的BeanFactoryPostProcessor
registerBeanPostProcessors(beanFactory)
注册BeanPostProcessors,将所有在xml中定义的beanPostProcessors加入到当前BeanFactory的列表,以便在getBean的时候调用。
initMessageSource()
初始化消息源
initApplicationEventMulticaster()
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isTraceEnabled()) {
logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}
else {
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isTraceEnabled()) {
logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
}
}
}
通过Spring容器得到一个applicationEventMulticaster,如果Spring容器没有定义,则创建SimpleApplicationEventMulticaster作为applicationEventMulticaster。
通过SimpleApplicationEventMulticaster的代码我们也能推断出这个类的作用,就是向Context里面的EventListener发布消息:
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
Executor executor = getTaskExecutor();
//将当前的事件event发送给当前Context的每一个Listener
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
//如果有executor则使用executor执行
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
//否则直接在当前线程执行
invokeListener(listener, event);
}
}
}
onRefresh()
这是模板方法,留给子类实现并执行想要的操作
registerListeners()
这一步将注册Listener,供前面initApplicationEventMulticaster注册的EventMulticaster进行广播,代码如下:
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);
}
}
}
这段代码将静态注册的Listener和Spring中注册的Listener bean都添加到EventMulticaster中,这样从EventMulticaster中发布的消息,每个Listener都能监听到,典型的观察者模式。
值得注意的是,Context中包含earlyApplicationEvents,所有的Listener就绪后,会先接收到这个事件。
finishBeanFactoryInitialization(beanFactory)
通过上面的注释:Instantiate all remaining (non-lazy-init) singletons.
我们可以知道,这个方法初始化所有的非懒加载单例实例,代码很复杂,后面会开一个专题分析这段代码的。
finishRefresh()
从函数名可以知道这个是完成刷新,代码如下:
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);
}
实际上它:
1. 清空了资源缓存
2. 初始化了生命周期处理器,用于处理Bean生命周期
3. 使用生命周期处理器传播刷新事件
4. 在Context内发布刷新事件
5. 将本Context注册到ListBeansView中
至此,Context的refresh分析完毕,下一部该分析Spring Boot的run进行了什么操作了。
Spring context的refresh函数执行过程分析的更多相关文章
- 当spring 容器初始化完成后执行某个方法
在做web项目开发中,尤其是企业级应用开发的时候,往往会在工程启动的时候做许多的前置检查. 比如检查是否使用了我们组禁止使用的Mysql的group_concat函数,如果使用了项目就不能启动,并指出 ...
- Spring Context 你真的懂了吗
今天介绍一下大家常见的一个单词 context 应该怎么去理解,正确的理解它有助于我们学习 spring 以及计算机系统中的其他知识. 1. context 是什么 我们经常在编程中见到 contex ...
- 8 -- 深入使用Spring -- 5...2 使用@Cacheable执行缓存
8.5.2 使用@Cacheable执行缓存 @Cacheable可用于修饰类或修饰方法,当使用@Cacheable修饰类时,用于告诉Spring在类级别上进行缓存 ------ 程序调用该类的实例的 ...
- 【报错】spring整合activeMQ,pom.xml文件缺架包,启动报错:Caused by: java.lang.ClassNotFoundException: org.apache.xbean.spring.context.v2.XBeanNamespaceHandler
spring版本:4.3.13 ActiveMq版本:5.15 ======================================================== spring整合act ...
- MFC的执行过程分析
MFC程序的执行细节剖析 MFC程序也是Windows程序,所以它应该也有一个WinMain.可是在程序中看不到它的踪影.事实上在程序进入点之前.另一个(并且仅有一个)全局对象(theApp).这就是 ...
- Spring容器的refresh()介绍
Spring容器的refresh()[创建刷新]; 1.prepareRefresh()刷新前的预处理; 1).initPropertySources()初始化一些属性设置;子类自定义个性化的属性设置 ...
- Hi3559AV100 NNIE开发(2)-RFCN(.wk)LoadModel及NNIE Init函数运行过程分析
之后随笔将更多笔墨着重于NNIE开发系列,下文是关于Hi3559AV100 NNIE开发(2)-RFCN(.wk)LoadModel及NNIE Init函数运行过程分析,通过对LoadModel函数及 ...
- Spring @Transaction 注解是如何执行事务的?
前言 相信小伙伴一定用过 @Transaction 注解,那 @Transaction 背后的秘密又知道多少呢? Spring 是如何开启事务的?又是如何进行提交事务和关闭事务的呢? 画图猜测 在开始 ...
- 【转】Spring bean处理——回调函数
Spring bean处理——回调函数 Spring中定义了三个可以用来对Spring bean或生成bean的BeanFactory进行处理的接口,InitializingBean.BeanPost ...
随机推荐
- 攻防世界 csaw2013reversing2 CSAW CTF 2014
运行程序 flag显示乱码 IDA打开查看程序逻辑 1 int __cdecl __noreturn main(int argc, const char **argv, const char **en ...
- Java中的映射Map - 入门篇
前言 大家好啊,我是汤圆,今天给大家带来的是<Java中的映射Map - 入门篇>,希望对大家有帮助,谢谢 简介 前面介绍了集合List,这里开始简单介绍下映射Map,相关类如下图所示 正 ...
- Kubernetes 常见问题总结
Kubernetes 常见问题总结 如何删除不一致状态下的 rc,deployment,service 在某些情况下,经常发现 kubectl 进程挂起现象,然后在 get 时候发现删了一半,而另外的 ...
- SSL证书详解和CFSSL工具使用
公钥基础设施(PKI) 基础概念 CA(Certification Authority)证书,指的是权威机构给我们颁发的证书. 密钥就是用来加解密用的文件或者字符串.密钥在非对称加密的领域里,指的是私 ...
- 启用reuse_port参数让Nginx性能提升3倍
为什么启用 reuse_port 记得 2008 年做性能测试的时候,新进7台 lenovo 4核4G 服务器用于性能测试. 当时资源紧张,这7台服务器都装了双系统(Win2003/CentOS5)空 ...
- 全网最详细的Linux命令系列-cat命令
cat命令的用途是连接文件或标准输入并打印.这个命令常用来显示文件内容,或者将几个文件连接起来显示,或者从标准输入读取内容并显示,它常与重定向符号配合使用. 命令格式: cat [选项] [文件].. ...
- ES9的新特性:正则表达式RegExp
简介 正则表达式是我们做数据匹配的时候常用的一种工具,虽然正则表达式的语法并不复杂,但是如果多种语法组合起来会给人一种无从下手的感觉. 于是正则表达式成了程序员的噩梦.今天我们来看一下如何在ES9中玩 ...
- 关于MySQL日志,我与阿里P9都聊了些啥?
写在前面 周末,我与阿里P9资深技术专家(这里就不说名字了),聊起了MySQL这个话题,为啥会聊这个呢?因为他看到我出版了一部<MySQL技术大全:开发.优化与运维实战>,对书籍的评价也是 ...
- 如何在IDEA中进行时序图分析
方法一: 使用插件 SequenceDiagram (系统自动生成) 使用方法: 下载插件,我们可以在 Plugins 中找到 选中线程方法名,然后右键就可以创建此方法的时序图了 参数设置 生成效果以 ...
- JavaScript深入理解-PWA渐进式应用
WPA-渐进式 web 应用 PWA 是什么 渐进式 Web 应用,提升 web app 浏览体验. manifest 应用程序清单 基本介绍: web app manifest是 PWA 技术集合中 ...