Spring源码十四:Spring生命周期

上一篇我们在Spring源码十三:非懒加载单例Bean中看到了Spring会在refresh方法中去调用我们的finishBeanFactoryInitialization方法去实例化,所有非懒加载器单例的bean。并实例化后的实例放到单例缓存中。到此我们refresh方法已经接近尾声。


Spring的生命周期

今天我们来看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、解析类Xml、初始化BeanFactory
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 这一步主要是对初级容器的基础设计
			// Prepare the bean factory for use in this context. 	3、准备BeanFactory内容:
			prepareBeanFactory(beanFactory); // 对beanFactory容器的功能的扩展:
			try {
				// Allows post-processing of the bean factory in context subclasses. 4、扩展点加一:空实现,主要用于处理特殊Bean的后置处理器
				postProcessBeanFactory(beanFactory);
				// Invoke factory processors registered as beans in the context. 	5、spring bean容器的后置处理器
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation. 	6、注册bean的后置处理器

				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.	7、初始化消息源
				initMessageSource();
				// Initialize event multicaster for this context.	8、初始化事件广播器
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses. 9、扩展点加一:空实现;主要是在实例化之前做些bean初始化扩展
				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();
			}
		}
	}


finishRefresh

我们进入finishRefresh方法中去:

/**
	 * 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.
		// 将Application容器注册到LiveBeanView中
		LiveBeansView.registerApplicationContext(this);
	}

上述注释已经比较完整了,finishRefresh方法我们在整理总结下:主要是初始化和生命周期相关的一些组件,我们可以先到initLifecycleProcessor的方法,看下具体做了些什么?

/**
	 * 初始化生命周期组件,如果beanFactory没有定义,
	 * 则使用DefaultLifecycleProcessor初始化一个
	 * 默认的生命周期组件,注册到容器中
	 * Initialize the LifecycleProcessor.
	 * Uses DefaultLifecycleProcessor if none defined in the context.
	 * @see org.springframework.context.support.DefaultLifecycleProcessor
	 */
	protected void initLifecycleProcessor() {
		// 获取BeanFactory容器
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
		// 判断容器中是否存在lifecycleProcessor
		if (beanFactory.containsLocalBean(LIFECYCLE_PROCESSOR_BEAN_NAME)) {
			// 获取lifecycleProcessor,注册当前容器(AbstractApplicationContext)
			this.lifecycleProcessor =
					beanFactory.getBean(LIFECYCLE_PROCESSOR_BEAN_NAME, LifecycleProcessor.class);
			if (logger.isTraceEnabled()) {
				logger.trace("Using LifecycleProcessor [" + this.lifecycleProcessor + "]");
			}
		}
		else {
			// 如果容器中没有lifecycleProcessor,则初始化DefaultLifecycleProcessor
			DefaultLifecycleProcessor defaultProcessor = new DefaultLifecycleProcessor();
			defaultProcessor.setBeanFactory(beanFactory);
			// 设置成员属性
			this.lifecycleProcessor = defaultProcessor;
			// 注册到Spring容器中
			beanFactory.registerSingleton(LIFECYCLE_PROCESSOR_BEAN_NAME, this.lifecycleProcessor);
			if (logger.isTraceEnabled()) {
				logger.trace("No '" + LIFECYCLE_PROCESSOR_BEAN_NAME + "' bean, using " +
						"[" + this.lifecycleProcessor.getClass().getSimpleName() + "]");
			}
		}
	}

可以看到在方法initLifecycleProcessor中,首先看下beanFactory中是否有名称为lifecycleProcessor的bean,存在直接获并复制给abstractApplicationContext的成员变量lifecycleProcessor,如果不存在,Spring框架会自动创建DefaultLifecycleProcessor类型的对象,赋值给成员变量,最后再注入到Spring容器中


DefaultLifecycleProcessor

进入DefaultLifecycleProcessor类中:

可以看到它主要实现了LifecycleProcessor, BeanFactoryAware这两个接口,我们来看下它的类图,如下所示:

看左边这块,DefaultLifecycleProcessor最终也是实现了Lifecycle接口与LifecycleProcessor接口我们展开看下这两个接口:

在Spring中,如果实现了接口Lifecycle,Spirng会在容器启动时,调用这些Bean中的start方法开启他们的生命周期,同样:如果在Spring容器关闭时也会调用stop方法来结束他们的生命周期。

而LifecycleProcessor在此基础上又新增了onRefrsh方法和onClose方法,主用来处理对应Bean的状态。

方法调用如下:


// 	// Propagate refresh to lifecycle processor first.
		
//		getLifecycleProcessor().onRefresh();

	/**
	 * 调用DefaultLifecycleProcessor.onRefresh()
	 */
	@Override
	public void  onRefresh() {
		startBeans(true);
		this.running = true;
	}



// Internal helpers

	private void startBeans(boolean autoStartupOnly) {
		// 1、获取所有lifecycle类型的Bean
		Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
		Map<Integer, LifecycleGroup> phases = new HashMap<>();
		lifecycleBeans.forEach((beanName, bean) -> {
			if (!autoStartupOnly || (bean instanceof SmartLifecycle && ((SmartLifecycle) bean).isAutoStartup())) {
				int phase = getPhase(bean);
				LifecycleGroup group = phases.get(phase);
				if (group == null) {
					group = new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly);
					phases.put(phase, group);
				}
				group.add(beanName, bean);
			}
		});
		// 遍历 Map,排序后启动
		if (!phases.isEmpty()) {
			List<Integer> keys = new ArrayList<>(phases.keySet());
			Collections.sort(keys);
			for (Integer key : keys) {
				phases.get(key).start();
			}
		}
	}

可以看到在onRefresh方法中,像我们刚才分析的一样,就会从Spring容器中获取所有实现了Lifecycle接口的bean,然后调用start方法来开启它们的生命周期,也就是开启Spring的生命周期了。


publishEvent

接着往下看:

		// 发布已经刷新完成的事件
		publishEvent(new ContextRefreshedEvent(this));

/**
	 * Publish the given event to all listeners.
	 * 发布事件,通知所有监听器
	 * <p>Note: Listeners get initialized after the MessageSource, to be able
	 * to access it within listener implementations. Thus, MessageSource
	 * implementations cannot publish events.
	 * @param event the event to publish (may be application-specific or a
	 * standard framework event)
	 */
	@Override
	public void publishEvent(ApplicationEvent event) {
		publishEvent(event, null);
	}

/**
	 *
	 * Publish the given event to all listeners.
	 * 					用于将指定的事件发布给所有监听器
	 * @param event the event to publish (may be an {@link ApplicationEvent}
	 *                 	参数表示要发布的事件,可以是 ApplicationEvent
	 * or a payload object to be turned into a {@link PayloadApplicationEvent})
	 *               	或一个负载对象(将被转换为 PayloadApplicationEvent)。
	 * 	 *
	 * @param eventType the resolved event type, if known
	 * @since 4.2
	 */
	protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
		Assert.notNull(event, "Event must not be null");

		// Decorate event as an ApplicationEvent if necessary
		// 判断传入的 event 是否为 ApplicationEvent 的实例:
		//		如果是,则直接赋值给 applicationEvent。
		//		如果不是,则将其包装为 PayloadApplicationEvent。
		ApplicationEvent applicationEvent;
		if (event instanceof ApplicationEvent) {
			applicationEvent = (ApplicationEvent) event;
		}
		else {
			applicationEvent = new PayloadApplicationEvent<>(this, event);
			// 如果 eventType 为空且事件被包装为 PayloadApplicationEvent,则从包装的事件中获取事件类型
			if (eventType == null) {
				eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
			}
		}

		// 事件分发
		// Multicast right now if possible - or lazily once the multicaster is initialized:
		// 判断 earlyApplicationEvents 是否不为空
		// 如果不为空,说明广播器还未初始化,则将事件添加到 earlyApplicationEvents 队列中,等待广播器初始化后再分发。
		// 如果为空,说明广播器已初始化, 立即分发事件。
		if (this.earlyApplicationEvents != null) {
			this.earlyApplicationEvents.add(applicationEvent);
		}
		else {
			// 直接通过 getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
			getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
		}

		// Publish event via parent context as well...
		// 如果当前上下文有父上下文 (this.parent 不为空),则同样向父上下文发布事件:
		if (this.parent != null) {
			//如果父上下文是 AbstractApplicationContext 的实例,则调用其 publishEvent 方法,同时传递事件和事件类型。
			// 如果不是,则直接调用父上下文的 publishEvent 方法。
			if (this.parent instanceof AbstractApplicationContext) {
				((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
			}
			else {
				this.parent.publishEvent(event);
			}
		}
	}

根据我们两篇关于事件驱动的源码分析,事件ContextRefreshedEvent将会注册到广播器中,广播器会把该事件广播器相应的监听器去处理,这个事件相当于告诉Spring整个容器已经刷新了,也就说Spring容器ApplicationContext已经初始化完毕了。

总结

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/778301.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【前端实现】在父组件中调用公共子组件:注意事项逻辑示例 + 将后端数组数据格式转换为前端对象数组形式 + 增加和删除行

【前端】在父组件中调用公共子组件的实现方法 写在最前面一、调用公共子组件子组件CommonRow.vue父组件ParentComponent.vue 二、实现功能1. 将后端数组数据格式转换为前端对象数组形式2. 增加和删除row 三、小结 &#x1f308;你好呀&#xff01;我是 是Yu欸 &#x1f30c; 2…

【论文解读】AGENTLESS:揭开基于LLM的软件工程代理的神秘面纱,重塑软件工程自动化新基线

&#x1f4dc; 文献卡 英文题目: Agentless: Demystifying LLM-based Software Engineering Agents;作者: Chunqiu Steven Xia; Yinlin Deng; Soren Dunn; Lingming ZhangDOI: 10.48550/arXiv.2407.01489摘要翻译: 大型语言模型&#xff08;LLM&#xff09;的最新进展显著推进…

nginx(三)—从Nginx配置熟悉Nginx功能

一、 Nginx配置文件结构 ... #全局块events { #events块... }http #http块 {... #http全局块server #server块{ ... #server全局块location [PATTERN] #location块{...}location [PATTERN] {...}}server{...}... #http全局块 …

智慧矿山建设规划方案(121页Word)

智慧矿山建设项目方案摘要 一、项目背景及现状分析 项目背景 随着信息技术的迅猛发展&#xff0c;智慧化、数字化已成为矿山行业转型升级的必然趋势。智慧矿山建设项目旨在通过集成先进的信息技术手段&#xff0c;实现对矿山生产、管理、安全等全过程的智能化监控与管理&…

Python统计实战:时间序列分析之一元线性回归预测和指数曲线预测

为了解决特定问题而进行的学习是提高效率的最佳途径。这种方法能够使我们专注于最相关的知识和技能&#xff0c;从而更快地掌握解决问题所需的能力。 &#xff08;以下练习题来源于《统计学—基于Python》。请在Q群455547227下载原始数据。&#xff09; 练习题 下表是某只股票…

大数的排列组合公式C代码

我们知道&#xff0c;计算排列A(n,m)和组合C(n,m)可以用先求阶乘的方式实现&#xff0c;但是当数很大时求阶乘很容易溢出&#xff0c;所以这里给出非阶乘的方式求排列组合 求排列 分母和分子可以抵消&#xff0c;最后代码如下 unsigned long long A(int n, int m) {unsigned…

代码随想录-Day49

300. 最长递增子序列 给你一个整数数组 nums &#xff0c;找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列&#xff0c;删除&#xff08;或不删除&#xff09;数组中的元素而不改变其余元素的顺序。例如&#xff0c;[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的 …

进程控制-fork函数

一个进程&#xff0c;包括代码、数据和分配给进程的资源。 fork &#xff08;&#xff09;函数通过系统调用创建一个与原来进程几乎完全相同的进程&#xff0c;也就是两个进程可以做完全相同的事&#xff0c;但如果初始参数或者传入的变量不同&#xff0c;两个进程也可以做不同…

第十六章 Qt的文件处理操作详解

目录 一、基本文件操作 二、二进制文件读写 三、文本文件读写 四、操作例子 1、QTextStream的流操作符 一、基本文件操作 文件操作是应用程序必不可少的部分。Qt 作为一个通用开发库,提供了跨平台的文件操作能力。在所有的 I/O 设备中,文件 I/O 是最重要的部分之…

InfluxDB时序数据库基本使用介绍

1、概要介绍 1.1、时序数据库使用场景 所谓时序数据库就是按照一定规则的时间序列进行数据读写操作的数据库。它们常被用于以下业务场景&#xff1a; 物联网IOT场景&#xff1a;可用于IOT设备的指标、状态监控数据存取。IT建设场景&#xff1a;可用于服务器、虚拟机、容器的…

linux下的网络编程

网络编程 1. 网络基础编程知识1.1网络字节序问题1.2 常用socket编程接口1.2.1 sockaddr1.2.2 ip地址转换函数1.2.4 socket()1.2.3 bind()1.2.4 listen()1.2.5 accept()1.2.6 connect() 1.3 以udp为基础的客户端连接服务器的demo1.4 以udp为基础的的服务器聊天室功能demo1.5 基于…

解决vscode配置C++编译带有中文名称报错问题

在新电脑上安装vscode运行带有中文路径和中文名称的C代码时遇到报错 根据别人的教程将laugh.json文件中"program": "${fileDirname}\\${fileBasenameNoExtension}.exe",改成了"program": "${fileDirname}\\output\\test.exe",&#x…

聊天广场(Vue+WebSocket+SpringBoot)

由于心血来潮想要做个聊天室项目 &#xff0c;但是仔细找了一下相关教程&#xff0c;却发现这么多的WebSocket教程里面&#xff0c;很多都没有介绍详细&#xff0c;代码都有所残缺&#xff0c;所以这次带来一个比较完整得使用WebSocket的项目。 目录 一、效果展示 二、准备工…

大数据中的常见数据问题:独断脏

想象你刚刚入职一家声称正在进行"数字化转型"的大型企业,担任大数据开发工程师。在入职的第一周,你满怀热情,迫不及待地想要大展拳脚,用你的技能来推动公司的数据驱动决策。 目录 大数据中的常见数据问题1. 独 - 数据孤岛2. 断 - 数据价值链断层3. 缺 - 标准、治理…

并口、串口和GPIO口区别

并口 并行接口,简称并口。并口采用的是25针D形接头。所谓“并行”,是指8位数据同时通过并行线进行传送,这样数据传送速度大大提高,但并行传送的线路长度受到限制,因为长度增加,干扰就会增加,数据也就容易出错,目前,并行接口主要作为打印机端口等。 并口的工作模式 …

ctfshow web 36d 练手赛

不知所措.jpg 没啥用然后测试了网站可以使用php伪达到目的 ?filephp://filter/convert.base64-encode/resourcetest/../index.<?php error_reporting(0); $file$_GET[file]; $file$file.php; echo $file."<br />"; if(preg_match(/test/is,$file)){inclu…

安全测试之使用Docker搭建SQL注入安全测试平台sqli-labs

1 搜索镜像 docker search sqli-labs 2 拉取镜像 docker pull acgpiano/sqli-labs 3 创建docker容器 docker run -d --name sqli-labs -p 10012:80 acgpiano/sqli-labs 4 访问测试平台网站 若直接使用虚拟机&#xff0c;则直接通过ip端口号访问若通过配置域名&#xff0…

【论文笔记】UniST:通用预训练城市时空预测模型

目录 写在前面1. 通用时空模型的挑战与能力特性2. 构建通用时空模型UniST2.1 大规模时空预训练2.2 时空知识规则引导提示学习 3. UniST的实验与分析3.1 模型预测效果3.2其他实验分析 写在前面 文章标题&#xff1a;UniST: A Prompt-Empowered Universal Model for Urban Spati…

基于SpringBoot+Vue的招生管理系统(带1w+文档)

基于SpringBootVue的招生管理系统(带1w文档&#xff09; 通过招生管理系统的研究可以更好地理解系统开发的意义&#xff0c;而且也有利于发展更多的智能系统&#xff0c;解决了人才的供给和需求的平衡问题&#xff0c;招生管理系统的开发建设&#xff0c;由于其开发周期短&…

linux centos 安装niginx并且添加ssl(https)模块

文章目录 前言一、nginx安装教程1.流程步骤 总结 前言 一、nginx安装教程 1.流程步骤 代码如下&#xff08;示例&#xff09;&#xff1a; 1.先下载linux安装包 2.解压安装命令 sudo tar -zxvf nginx-1.20.1.tar.gz3.进入解压后的目录 sudo cd nginx-1.20.14.安装 sudo y…