Spring源码之九finishRefresh详解

公众号搜索【程序员田同学】,专职程序员兼业余写手,生活不止于写代码

Spring IoC 的核心内容要收尾了,本文将对最后一个方法 finishRefresh 进行介绍,位于refresh 方法中的第九个位置。

本章实际是对发布订阅模式的一种补充,这是Spring在刷新事件完成后发布事件。

由于存在上下文关系,本文也会对 initApplicationEventMulticaster 方法、registerListeners 方法进行回顾。

我们回到refresh 方法中。

@Override
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、加载 Bean、注册 Bean
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context.
//3、设置 BeanFactory 的类加载器,添加几个 BeanPostProcessor,手动注册几个特殊的 bean
prepareBeanFactory(beanFactory); try {
//4、模板方法
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context.
//执行BeanFactory后置处理器
invokeBeanFactoryPostProcessors(beanFactory); // 5、Register bean processors that intercept bean creation.
//注册bean后置处理器
registerBeanPostProcessors(beanFactory); // Initialize message source for this context.
//国际化
initMessageSource(); // Initialize event multicaster for this context.
//初始化事件广播器
initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses.
//6、模板方法--springboot实现了这个方法
onRefresh(); // Check for listener beans and register them.
//7、注册监听器
registerListeners(); // Instantiate all remaining (non-lazy-init) singletons.
//8、完成bean工厂的初始化**方法重要**********************************************
finishBeanFactoryInitialization(beanFactory); //9、 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();
}
}
}

我们首先知道这个三个方法的作用:

initApplicationEventMulticaster():初始化应用的事件广播器

/**
* Initialize the ApplicationEventMulticaster.
* Uses SimpleApplicationEventMulticaster if none defined in the context.
* @see org.springframework.context.event.SimpleApplicationEventMulticaster
*/
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
// 1.判断BeanFactory是否已经存在事件广播器(固定使用beanName=applicationEventMulticaster)
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
// 1.1 如果已经存在,则将该bean赋值给applicationEventMulticaster
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isTraceEnabled()) {
logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}
else {
// 1.2 如果不存在,则使用SimpleApplicationEventMulticaster
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() + "]");
}
}
}

最终只做了一件事,初始化应用的事件广播器。(具体什么是事件广播器及其作用可见上上篇文章,具体就不在吃赘述了)

registerListeners():注册监听器。见上上篇文章

finishRefresh():完成上下文的刷新工作,本文重点

首先概览finishRefresh方法

	protected void finishRefresh() {
// Clear context-level resource caches (such as ASM metadata from scanning).
//清除资源缓存
clearResourceCaches(); // Initialize lifecycle processor for this context.
// // 1.为此上下文初始化生命周期处理器
initLifecycleProcessor(); // Propagate refresh to lifecycle processor first.
// 2.首先将刷新完毕事件传播到生命周期处理器(触发isAutoStartup方法返回true的SmartLifecycle的start方法)
getLifecycleProcessor().onRefresh(); // Publish the final event.
// 3.推送上下文刷新完毕事件到相应的监听器
publishEvent(new ContextRefreshedEvent(this)); // Participate in LiveBeansView MBean, if active.
LiveBeansView.registerApplicationContext(this);
}

1、2、3是重点内容

1.为此上下文初始化生命周期处理器

	protected void initLifecycleProcessor() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
// 1.判断BeanFactory是否已经存在生命周期处理器(固定使用beanName=lifecycleProcessor)
if (beanFactory.containsLocalBean(LIFECYCLE_PROCESSOR_BEAN_NAME)) {
this.lifecycleProcessor =
beanFactory.getBean(LIFECYCLE_PROCESSOR_BEAN_NAME, LifecycleProcessor.class);
if (logger.isTraceEnabled()) {
logger.trace("Using LifecycleProcessor [" + this.lifecycleProcessor + "]");
}
}
else {
// 1.2 如果不存在,则使用DefaultLifecycleProcessor
DefaultLifecycleProcessor defaultProcessor = new DefaultLifecycleProcessor();
defaultProcessor.setBeanFactory(beanFactory);
this.lifecycleProcessor = defaultProcessor;
// 并将DefaultLifecycleProcessor作为默认的生命周期处理器,注册到BeanFactory中
beanFactory.registerSingleton(LIFECYCLE_PROCESSOR_BEAN_NAME, this.lifecycleProcessor);
if (logger.isTraceEnabled()) {
logger.trace("No '" + LIFECYCLE_PROCESSOR_BEAN_NAME + "' bean, using " +
"[" + this.lifecycleProcessor.getClass().getSimpleName() + "]");
}
}
}

2.首先将刷新完毕事件传播到生命周期处理器

private void startBeans(boolean autoStartupOnly) {
// 1.获取所有的Lifecycle bean
Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans(); // 将Lifecycle bean 按阶段分组,阶段通过实现Phased接口得到
Map<Integer, LifecycleGroup> phases = new HashMap<>();
// 2.遍历所有Lifecycle bean,按阶段值分组
lifecycleBeans.forEach((beanName, bean) -> {
// autoStartupOnly=true代表是ApplicationContext刷新时容器自动启动;autoStartupOnly=false代表是通过显示的调用启动
// 3.当autoStartupOnly=false,也就是通过显示的调用启动,会触发全部的Lifecycle;
// 当autoStartupOnly=true,也就是ApplicationContext刷新时容器自动启动,只会触发isAutoStartup方法返回true的SmartLifecycle if (!autoStartupOnly || (bean instanceof SmartLifecycle && ((SmartLifecycle) bean).isAutoStartup())) {
// 3.1 获取bean的阶段值(如果没有实现Phased接口,则值为0)
int phase = getPhase(bean);
// 3.2 拿到存放该阶段值的LifecycleGroup
LifecycleGroup group = phases.get(phase);
if (group == null) {
// 3.3 如果该阶段值的LifecycleGroup为null,则新建一个
group = new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly);
phases.put(phase, group);
}
// 3.4 将bean添加到该LifecycleGroup
group.add(beanName, bean);
}
});
// 4.如果phases不为空
if (!phases.isEmpty()) {
List<Integer> keys = new ArrayList<>(phases.keySet());
// 4.1 按阶段值进行排序
Collections.sort(keys);
// 4.2 按阶段值顺序,调用LifecycleGroup中的所有Lifecycle的start方法
for (Integer key : keys) {
phases.get(key).start();
}
}
}

3.推送上下文刷新完毕事件到相应的监听器

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null"); // Decorate event as an ApplicationEvent if necessary
// 1.如有必要,将事件装饰为ApplicationEvent
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
}
} // Multicast right now if possible - or lazily once the multicaster is initialized
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
// 2.使用事件广播器广播事件到相应的监听器
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
} // Publish event via parent context as well...
// 3.同样的,通过parent发布事件.....
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}

这里面调用的publishEvent方法,和我们自定义的监听器调用的publishEvent是同一个方法,ContextRefreshedEvent是Spirng的一个事件称为上下文刷新完毕事件,如果我们在上下文刷新完成后要写一个发布事件,实现ApplicationListener接口即可。

我们在此举一个简单的例子。

这样,当 Spring 执行到 finishRefresh 方法时,就会将 ContextRefreshedEvent 事件推送到 MyRefreshedListener 中。

读者可以结合自定义事件对比一个和Spring提供的刷新上下文事件的区别,以便于更好的理解Spring的事件监听机制。

跟 ContextRefreshedEvent 相似的还有:ContextStartedEvent、ContextClosedEvent、ContextStoppedEvent。

好啦,Spirng的refresh方法到这里就结束啦,一共是九篇博客,实际上这也是Spirng的IOC的全部内容了,如果读者能把九篇的完全消化,那么spring的IOC也就理解的七七八八了。

Spring源码之九finishRefresh详解的更多相关文章

  1. 【集合框架】JDK1.8源码分析之ArrayList详解(一)

    [集合框架]JDK1.8源码分析之ArrayList详解(一) 一. 从ArrayList字表面推测 ArrayList类的命名是由Array和List单词组合而成,Array的中文意思是数组,Lis ...

  2. 我的书籍《深入解析Java编译器:源码剖析与实例详解》就要出版了

    一个十足的技术迷,2013年毕业,做过ERP.游戏.计算广告,在大公司呆过,但终究不满足仅对技术的应用,在2018年末离开了公司,全职写了一本书<深入解析Java编译器:源码剖析与实例详解> ...

  3. nginx源码分析线程池详解

    nginx源码分析线程池详解 一.前言     nginx是采用多进程模型,master和worker之间主要通过pipe管道的方式进行通信,多进程的优势就在于各个进程互不影响.但是经常会有人问道,n ...

  4. Java并发包源码学习系列:详解Condition条件队列、signal和await

    目录 Condition接口 AQS条件变量的支持之ConditionObject内部类 回顾AQS中的Node void await() 添加到条件队列 Node addConditionWaite ...

  5. [spring源码学习]九、IOC源码-applicationEventMulticaster事件广播

    一.代码实例 回到第IOC的第七章context部分,我们看源码分析部分,可以看到在spring的bean加载之后的第二个重要的bean为applicationEventMulticaster,从字面 ...

  6. Golang源码分析之目录详解

    开源项目「go home」聚焦Go语言技术栈与面试题,以协助Gopher登上更大的舞台,欢迎go home~ 导读 学习Go语言源码的第一步就是了解先了解它的目录结构,你对它的源码目录了解多少呢? 目 ...

  7. nginx源码编译安装(详解)

    nginx编译安装 安装步骤: 官网下载合适的版本,建议选择稳定版本. 官网地址:https://nginx.org wget https://nginx.org/download/nginx-1.2 ...

  8. 【源码解析】BlockManager详解

    1 Block管理模块的组件和功能 BlockManager:BlockManager源码解析 Driver和Executor都会创建 Block的put.get和remove等操作的实际执行者 Bl ...

  9. 【Linux】 源码安装make命令详解,避免踩坑

    正常的编译安装/卸载: 源码的安装一般由3个步骤组成:配置(configure).编译(make).安装(make install).   configure文件是一个可执行的脚本文件,它有很多选项, ...

随机推荐

  1. Redis性能分析思路

    Redis性能分析有几个大的方向.分别是 (1)基准对比 (2)配置优化 (3)数据持久化 (4)键值优化 (5)缓存淘汰 (6)Redis集群 基准对比 在没有业务实例运行的情况下,在服务器上通过测 ...

  2. 从服务之间的调用来看 我们为什么需要Dapr

    Dapr 相关的文章我已经写了20多篇了[1] . 当向其他人推荐Dapr 的时候,需要回答的一个问题就是: Dapr 似乎并不是特别令人印象深刻.它提供了一组"构建块",解决了与 ...

  3. 2022.02.05 DAY2

    前言 今天陪老姐送对象去安庆了,上午还去了西风禅寺求了个签,第一次拿到中评签,看来今年还需要继续努力哈哈哈.一直到晚上才有时间去做点题目,今天依旧是leetcode. 题目 leetcode 1 两数 ...

  4. Collection和Map集合

    前言 集合的长度是可变的.集合存储的都是对象.而且对象的类型可以不一致. Collection集合和Map集合是所有集合的父接口. Collection接口定义 体系结构的根接口,代表一组对象,有两个 ...

  5. 【转】MySql根据经纬度获取附近的商家

    创建geo表 create table geo( geo_id INT NOT NULL AUTO_INCREMENT, lng float NOT NULL, lat float NOT NULL, ...

  6. Python中处理日期时间库的使用方法

    常用的库有time.datetime.其中datetime库是对time库的封装,所以使用起来更加便捷.date是指日期时间(年月日)处理,time往往更加细小的单位(小时分秒等)的时间处理. 一.d ...

  7. 还在做廉价的劳动力?部署PXE实现Kickstart无人值守安装

    搭建PXE实现Kickstart无人值守安装 1.搭建PXE远程安装服务器 2.实现kicstart无人值守安装 1.PXE介绍及搭载: PXE是有Intel公司开发的网络引导技术,工作在Client ...

  8. 理解ASP.NET Core - 基于JwtBearer的身份认证(Authentication)

    注:本文隶属于<理解ASP.NET Core>系列文章,请查看置顶博客或点击此处查看全文目录 在开始之前,如果你还不了解基于Cookie的身份认证,那么建议你先阅读<基于Cookie ...

  9. Solution -「NOI.AC 省选膜你赛」array

    题目 题意简述   维护一个长度为 \(n\) 的序列 \(\{a_n\}\),并给出 \(q\) 个操作: 将下标为 \(x\) 的数修改为 \(y\). 给定 \(l,r,k\),求最大的 \(m ...

  10. ESXI 7.0.0 U2 部署

    文章目录 什么是ESXI? ESXi 的优势 功能特性 部署ESXI 创建虚拟机 开始安装 打开浏览器输入ip进行管理 什么是ESXI? ESXI官网:https://www.vmware.com/c ...