专题五:Spring源码之初始化容器上下文

news2024/12/23 10:53:03

上一篇我们通过如下一段基础代码作为切入点,最终找到核心的处理是refresh方法,从今天开始正式进入refresh方法的解读。

public class Main {
	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
		JmUser jmUser = (JmUser)context.getBean("jmUser");
		System.out.println(jmUser.getName());
		System.out.println(jmUser.getAge());

	}
}

初始化容器上下文

首先还是整体看下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();
			}
		}
	}

首先将目标聚焦在第一个方法prepareRefresh方法上,根据方法名称和注释,我们大概可以猜测到该方法是在容器初始化前做些准备工作。

有了这个想法我来具体看下这个方法到底干了什么?

/**
	 * Prepare this context for refreshing, setting its startup date and
	 * active flag as well as performing any initialization of property sources.
	 * 一些初始化设置如:设置容器开始事件、容器状态active设置激活】初始化配置源等。
	 * 1.1、其中关注初始化配置源:这个也是留给子类自己实现,扩展点加一
	 * 1.2、容器初始化的时候,校验必须的配置是否为空,当我们自己对原框架修改的时候,可以通过这个属性加上必要的配置判断
	 *
	 */
	protected void prepareRefresh() {
		// Switch to active.
		this.startupDate = System.currentTimeMillis();
		this.closed.set(false);
		this.active.set(true);

		if (logger.isDebugEnabled()) {
			if (logger.isTraceEnabled()) {
				logger.trace("Refreshing " + this);
			}
			else {
				logger.debug("Refreshing " + getDisplayName());
			}
		}

		// 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<>();
	}

可以看到,该方法大部分时间只是做了初始化的设置如开始时间、容器状态初始化等,聚焦下

initPropertySources方法
	/**
	 * <p>Replace any stub property sources with actual instances.
	 * @see org.springframework.core.env.PropertySource.StubPropertySource
	 * @see org.springframework.web.context.support.WebApplicationContextUtils#initServletPropertySources
	 */
	protected void initPropertySources() {
		// For subclasses: do nothing by default.
	}

Spring最经典的设计之一,空实现方法方法的权限级别为protected。给子类自己实现,扩展点加一。这里单独提出来和大家看看,因为后面我们能看到很多类似的代码。这也是Spring是一个易扩展框架的原因之一。

说完这个方法的设计,下面再来看看这个方法具体干了什么。看注释说是为了替换占位符。既然这样我们自己来重写这个方法试试看就知道啦。重写代码如下:

public class MyselfClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {
	/**
	 * Create a new ClassPathXmlApplicationContext, loading the definitions
	 * from the given XML file and automatically refreshing the context.
	 * @param configLocations resource location
	 * @throws BeansException if context creation failed
	 */
	public MyselfClassPathXmlApplicationContext(String... configLocations) throws BeansException {
		super(configLocations);
	}

	@Override
	protected void initPropertySources() {
		System.out.println("自定义 initPropertySources");
		getEnvironment().getSystemProperties().put("systemOS", "mac");
	}


public class Main {
	public static void main(String[] args) {
		ApplicationContext context = new MyselfClassPathXmlApplicationContext("applicationContext.xml");
		JmUser jmUser = (JmUser)context.getBean("jmUser");
		System.out.println(jmUser.getName());
		System.out.println(jmUser.getAge());

	}
}

执行完initPropertySources方法以后,发现环境变量多了我们设置的代码systemOS,后续在需要的地方可以替换成我们所需要的值。

接着我们来看prepareRefresh下一个方法:

getEnvironment().validateRequiredProperties();

老样子根据注释和方法名称简答猜测一下,应该是用来校验是否需要检验某个必须的属性。猜测后进入代码验证一波。

看代码是自己的成员属性propertyResolver进行调用的,在进入方法看下:


	@Override
	public void validateRequiredProperties() {
		MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();
		for (String key : this.requiredProperties) {
			if (this.getProperty(key) == null) {
				ex.addMissingRequiredProperty(key);
			}
		}
		if (!ex.getMissingRequiredProperties().isEmpty()) {
			throw ex;
		}
	}

上述代码主要是遍历requirePrpperties属性,将不存在的key存入ex中,待循环结束以后抛出异常。目前我们的代码属性为空。我们再改写下上述代码看看。

package org.springframework;

import org.springframework.beans.BeansException;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author Jeremy
 * @version 1.0
 * @description: 自定义容器
 * @date 2024/7/1 20:17
 */
public class MyselfClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {
	/**
	 * Create a new ClassPathXmlApplicationContext, loading the definitions
	 * from the given XML file and automatically refreshing the context.
	 * @param configLocations resource location
	 * @throws BeansException if context creation failed
	 */
	public MyselfClassPathXmlApplicationContext(String... configLocations) throws BeansException {
		super(configLocations);
	}

	@Override
	protected void initPropertySources() {
		System.out.println("自定义 initPropertySources");
//		getEnvironment().getSystemProperties().put("systemOS", "mac");
		getEnvironment().setRequiredProperties("systemOS");
	}
}

堆栈日志如下

自定义 initPropertySources
Disconnected from the target VM, address: 'localhost:50403', transport: 'socket'
Exception in thread "main" org.springframework.core.env.MissingRequiredPropertiesException: The following properties were declared as required but could not be resolved: [systemOS]
    at org.springframework.core.env.AbstractPropertyResolver.validateRequiredProperties(AbstractPropertyResolver.java:145)
    at org.springframework.core.env.AbstractEnvironment.validateRequiredProperties(AbstractEnvironment.java:519)
    at org.springframework.context.support.AbstractApplicationContext.prepareRefresh(AbstractApplicationContext.java:602)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:522)
    at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:148)
    at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:95)
    at org.springframework.MyselfClassPathXmlApplicationContext.<init>(MyselfClassPathXmlApplicationContext.java:20)
    at org.springframework.Main.main(Main.java:15)

放开上面注释:正常运行。通过上述两个简单的实例我们可以通过重写上述代码为我们Spring容器提供基础的校验和设置对应的值。方便后续开发。到这里我们初始化容器上下文prepareRefresh方法告一段落。

总结

目前我们代码进程如下图所示:

下一节我们正式进入初始化容器,看看众所周知的Bean Factory到底怎么来的。

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

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

相关文章

Study--Oracle-05-Oracler体系结构

一、oracle 体系概览 Oracle数据库的体系结构通常包括以下主要组件&#xff1a; 1、实例&#xff08;Instance&#xff09;&#xff1a;运行数据库的软件环境&#xff0c;包括内存结构&#xff08;SGA&#xff09;和进程结构&#xff08;Background Processes and User Proces…

Mysql面试合集

概念 是一个开源的关系型数据库。 数据库事务及其特性 事务&#xff1a;是一系列的数据库操作&#xff0c;是数据库应用的基本逻辑单位。 事务特性&#xff1a; &#xff08;1&#xff09;原子性&#xff1a;即不可分割性&#xff0c;事务要么全部被执行&#xff0c;要么就…

Spring Boot 实现 AOP 动态热插拔功能并附DEMO源码

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…

3D Gaussian Splatting代码中的Gaussian_Module和Cameras两个类的代码解读

Gaussian_model 讨论Gaussian_model这个类&#xff0c;是因为里面包含了三维高斯分布的基本信息&#xff0c;里面定义了各种参量的构建方式、用于优化学习的激活函数、学习率设置方法和高斯点优化过程中的增加与删除方式及对应优化器的处理方法。这个类定义在scene文件夹中的g…

探索工业AI智能摄像机的高端科技

在当今快速发展的工业智能化领域&#xff0c;工业AI智能摄像机系列以其卓越的性能和多功能性在国内外备受关注&#xff08;文末有国外工程师的评测链接&#xff09;。搭载Raspberry Pi CM4支持的旨在广泛应用&#xff0c;涵盖从简单的条形码扫描到基于人工智能的工业环境中的缺…

数学知识——欧拉函数

数学知识&#xff08;二&#xff09; 20240628 求和N互质的个数公式 先分解N&#xff0c;再求个数fai n欧拉函数的证明&#xff1a;用容斥原理 不考 求质因子 p1, … , pk 1-N中与N互质的个数&#xff0c; 去掉质因子倍数 是pi的倍数的有N/pi个&#xff0c;但是会有既是p1也是…

【UE5.1】Chaos物理系统基础——01 创建可被破坏的物体

目录 步骤 一、通过笔刷创建静态网格体 二、破裂静态网格体 三、“统一” 多层级破裂 四、“簇” 群集化的破裂 五、几何体集的材质 六、防止几何体集自动破碎 步骤 一、通过笔刷创建静态网格体 1. 可以在Quixel Bridge中下载两个纹理&#xff0c;用于表示石块的内外纹…

微信小程序毕业设计-英语互助系统项目开发实战(附源码+论文)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;微信小程序毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计…

微信小程序-插槽slot

一.插槽slot 在页面使用自定义组件的时候&#xff0c;如果在自定义组件里面写子组件&#xff0c;子组件的内容无法显示。 <custom01> <text slotslot-top>你好&#xff0c;上方组件</text> 你好&#xff0c;组件 <text slotslot-bottom>你好&#xf…

小型语言模型的兴起

过去几年&#xff0c;我们看到人工智能能力呈爆炸式增长&#xff0c;其中很大一部分是由大型语言模型 (LLM) 的进步推动的。GPT-3 等模型包含 1750 亿个参数&#xff0c;已经展示了生成类似人类的文本、回答问题、总结文档等能力。然而&#xff0c;虽然 LLM 的能力令人印象深刻…

文件操作~

目录 1.为什么使用文件&#xff1f; 2.什么是文件&#xff1f; 2.1 程序文件 2.2 数据文件 2.3 文件名 3.⼆进制文件和文本文件&#xff1f; 4.文件的打开和关闭 4.1 流和标准流 4.1.1 流 4.1.2 标准流 4.2 文件指针 4.3 ⽂件的打开和关闭 5.文件的顺序读写 5.1 …

志愿者管理系统带讲解,保运行

技术栈 后端: SpringBoot Mysql MybatisPlus 前端: Vue Element 分为 管理员端 用户端 功能描述 用户端 管理员端 观看地址&#xff1a; B站 &#xff1a; 【毕设者】志愿者管理系统(安装讲解源码)

C++算法学习心得八.动态规划算法(6)

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

超详细的 C++中的封装继承和多态的知识总结<2.多态>

引言 小伙伴们我们都知道了&#xff0c;什么是封装和继承&#xff0c;在有了这个的基础上我们接着来看什么是多态。多态从字面上意思我们就可以知道&#xff0c;大概就是一个函数的不同形态&#xff0c;而且&#xff0c;前边我们在学习函数重载的时候我们已经简单的了解了如何用…

如何优化前端性能:提高网页加载速度的实用技巧

我们在前端开发中&#xff0c;性能优化是提高用户体验的关键因素。网页加载速度直接影响用户的满意度和留存率。本文将介绍几种优化前端性能的实用方法&#xff0c;帮助你提高网页加载速度。 问题描述 &#xff1a; 首先前端性能优化涉及多个方面&#xff0c;包括减少HTTP请…

【单片机与嵌入式】stm32串口通信入门

一、串口通信/协议 &#xff08;一&#xff09;串口通信简介 串口通信是一种通过串行传输方式在电子设备之间进行数据交换的通信方式。它通常涉及两条线&#xff08;一条用于发送数据&#xff0c;一条用于接收数据&#xff09;&#xff0c;适用于各种设备&#xff0c;从微控制…

万字长文|下一代系统内存数据加速接口SDXI解读

本文内容分为5章节&#xff0c;总计10535字&#xff0c;内容较多&#xff0c;建议先收藏&#xff01; 1.SDXI技术产生的背景 2.SDXI相比DMA的优势 3.SDXI实现原理与架构 3.1 描述符环原理解读 3.2 上下文管理介绍 3.3 AKey与RKey解读 3.4 错误日志和状态管理 3.5 跨Function访…

javascript 常见设计模式

什么是设计模式? 在软件开发中&#xff0c;设计模式是解决特定问题的经验总结和可复用的解决方案。设计模式可以提高代码的复用性、可维护性和可读性&#xff0c;是提高开发效率的重要手段。 单例模式 1.概念 单例模式 &#xff08;Singleton Pattern&#xff09;&#xf…

c++ 智能指针实战分析

一.智能指针的设计思路 智能指针是类模板&#xff0c;再栈上创建智能指针对象。把普通指针交给智能指针对象。智能指针对象过期时&#xff0c;调用析构函数释放普通指针的内存。 智能指针的类型 auto_ptr是C98的标准&#xff0c;c17已经弃用。unique_ptr、shared_ptr和weak_…

动手学深度学习(Pytorch版)代码实践 -计算机视觉-41目标检测数据集

41目标检测数据集 import os import pandas as pd import torch import torchvision import matplotlib.pylab as plt from d2l import torch as d2l# 数据集下载链接 # http://d2l-data.s3-accelerate.amazonaws.com/banana-detection.zip# 读取数据集 #save def read_data_b…