Spring源码十四:Spring生命周期

news2024/11/16 12:40:57

上一篇我们在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.coloradmin.cn/o/1901017.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

医疗器械企业CRM系统推荐清单(2024版)

近年来&#xff0c;我国医疗器械行业在国家政策支持、医改深入、人口老龄化和消费能力提升等因素推动下&#xff0c;得到了快速发展&#xff0c;正日益成为创新能力增强、市场需求旺盛的朝阳产业。然而&#xff0c;行业也面临价格压力、市场份额重新分配、合规风险以及产品和服…

【C语言】register 关键字

在C语言中&#xff0c;register关键字用于提示编译器将变量尽量存储在CPU的寄存器中&#xff0c;而不是在内存中。这是为了提高访问速度&#xff0c;因为寄存器的访问速度比内存快得多。使用register关键字的变量通常是频繁使用的局部变量。 基本用法 void example() {regist…

使用ChatGPT写学术论文的技巧和最佳实践指南

大家好&#xff0c;感谢关注。我是七哥&#xff0c;一个在高校里不务正业&#xff0c;折腾学术科研AI实操的学术人。关于使用ChatGPT等AI学术科研的相关问题可以和作者七哥&#xff08;yida985&#xff09;交流&#xff0c;多多交流&#xff0c;相互成就&#xff0c;共同进步&a…

ActiveMq工具之管理页面说明

文章目录 安装ActiveMQ一: 访问管理页面二: 进入管理页面&#xff0c;主页三: Queues页说明四: Topics页说明五: Subscribers页说明 安装ActiveMQ wget https://archive.apache.org/dist//activemq/5.13.3/apache-activemq-5.13.3-bin.tar.gz wget https://mirrors.huaweiclou…

ubuntu 系统中 使用docker 制作 Windows 系统,从此告别 vmware虚拟机

我的系统是 ubuntu 24 前期准备工作&#xff1a; 安装dockerdocker pull 或者 手动制作镜像 docker build 的话 必须要 科学上网&#xff0c; 好像阿里镜像都下不下来。需要 知道 docker 和docker compose 命令的使用方式 我是给docker 挂了 http代理 如果你能pull下来镜像 …

Mysql-常见DML-DQL-语句语法用法总结

1、常见DML语句 1.1 INSERT语句 说明&#xff1a;将数据插入到数据库表中。 INSERT INTO table_name (column1, column2, ...) VALUES (value1, value2, ...); 实例&#xff1a;添加C罗信息到数据库表中 insert into employee (ID, name, gender, entrydate, age) values …

MinIO - 从 环境搭建 -> SpringBoot实战 -> 演示,掌握 Bucket 和 Object 操作

目录 开始 Docker 部署 MinIO 中的基本概念 SpringBoot 集成 MinIO 依赖 配置 MinIO 时间差问题报错 The difference between the request time and the servers time is too large MinIO 中对 Bucket&#xff08;文件夹&#xff09; 的操作 是否存在 / 创建 查询所有…

Android 四大组件

1. Activity 应用程序中&#xff0c;一个Activity通常是一个单独的屏幕&#xff0c;它上面可以显示一些控件&#xff0c;也可以监听并对用户的事件做出响应。 Activity之间通过Intent进行通信&#xff0c;在Intent 的描述结构中&#xff0c;有两个最重要的部分&#xff1a;动…

嵌入式Linux系统编程 — 7.2 进程的环境变量

目录 1 什么是进程的环境变量 2 环境变量的作用 3 应用程序中获取环境变量 3.1 environ全局变量 3.2 获取指定环境变量 getenv 4 添加/删除/修改环境变量 4.1 putenv()函数添加环境变量 4.2 setenv()函数 4.3 unsetenv()函数 1 什么是进程的环境变量 每一个进程都有一…

Node.js 生成vue组件

在项目根目录下创建 create.js /*** 脚本生成vue组件* 主要是利用node自带的fs模块操作文件的写入* ===========================================* 准备步骤:* 1.输入作者名* 2.输入文件名* 3.输入菜单名* 4.输入文件地址* ============================================* 操…

【公益案例展】厦门大学附属成功医院——国产数据库在综合三甲医院核心系统的应用...

‍ 达梦数据公益案例 本项目案例由达梦数据投递并参与数据猿与上海大数据联盟联合推出的 #榜样的力量# 《2024中国数据智能产业最具社会责任感企业》榜单/奖项”评选。 大数据产业创新服务媒体 ——聚焦数据 改变商业 厦门大学附属成功医院是一所集医疗、教学、科研、保健、疗…

C++基础(八):类和对象 (下)

经过前面的学习&#xff0c;我们已经翻过了两座大山&#xff0c;类和对象入门知识就剩下这一讲了&#xff0c;加油吧&#xff0c;少年&#xff01; 目录 一、再谈构造函数 1.1 构造函数体赋值 1.2 初始化列表&#xff08;理解&#xff09; 1.3 explicit关键字&#xff08;C…

代码随想录算法训练营第13天|二叉树的递归遍历、二叉树的迭代遍历、二叉树的统一迭代法、102.二叉树的层序遍历

打卡Day13 1.理论基础2.二叉树的递归遍历3.二叉树的迭代遍历3.二叉树的统一迭代法4.102.二叉树的层序遍历扩展107. 二叉树的层序遍历 II199.二叉树的右视图637.二叉树的层平均值429.N叉树的层序遍历515.在每个树行中找最大值116.填充每个节点的下一个右侧节点指针117. 填充每个…

ctfshow web入门 web338--web344

web338 原型链污染 comman.js module.exports {copy:copy };function copy(object1, object2){for (let key in object2) {if (key in object2 && key in object1) {copy(object1[key], object2[key])} else {object1[key] object2[key]}}}login.js var express …

Python + 在线 + 文生音,音转文(中文文本转为英文语音,语音转为中文文本)

开源模型 平台&#xff1a;https://huggingface.co/ars-语言转文本: pipeline("automatic-speech-recognition", model"openai/whisper-large-v3", device0 ) hf: https://huggingface.co/openai/whisper-large-v3 github: https://github.com/openai/wh…

使用自动化测试确保接口正确性的详细指南!

引言&#xff1a; 随着软件开发的迅速发展&#xff0c;接口的正确性成为了确保应用程序质量的关键要素之一。自动化测试是一种强大的工具&#xff0c;可以帮助开发人员和测试人员减少错误&#xff0c;提高测试覆盖率&#xff0c;并加快测试过程。本文将详细介绍从零开始如何使…

探索LlamaIndex:如何用Django打造高效知识库检索

简介 LlamaIndex&#xff08;前身为 GPT Index&#xff09;是一个数据框架&#xff0c;为了帮助我们去建基于大型语言模型&#xff08;LLM&#xff09;的应用程序。 主要用于处理、构建和查询自定义知识库。 它支持多种数据源格式 excel&#xff0c;txt&#xff0c;pdf&…

ELK优化之Filebeat部署

目录 1.安装配置Nginx 2.安装 Filebeat 3.设置 filebeat 的主配置文件 4.修改Logstash配置 5.启动配置 6.kibana验证 主机名ip地址主要软件es01192.168.9.114ElasticSearches02192.168.9.115ElasticSearches03192.168.9.116ElasticSearch、Kibananginx01192.168.9.113ng…

测试人员如何管理项目与风险预警

在平时工作过程中&#xff0c;你有没有因项目延时&#xff0c;需求频繁变更&#xff0c;开发提测质量不高&#xff0c;以及漏测的情况下&#xff0c;背了不少锅的情况呢&#xff1f; 作为测试人员&#xff0c;我们应该如何发挥积极主动性&#xff0c;进行项目管理&#xff0c;有…

VuePress 的更多配置

现在&#xff0c;读者应该对 VuePress、主题和插件等有了基本的认识&#xff0c;除了插件&#xff0c;VuePress 自身也有很多有用的配置&#xff0c;这里简单说明下。 ‍ ‍ VuePress 的介绍 在介绍了 VuePress 的基本使用、主题和插件的概念之后&#xff0c;我们再来看看官…