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

上一篇我们通过如下一段基础代码作为切入点,最终找到核心的处理是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方法

	@Overridepublic 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、初始化BeanFactoryConfigurableListableBeanFactory 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、实例化:非兰加载BeanfinishBeanFactoryInitialization(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);}@Overrideprotected 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进行调用的,在进入方法看下:

@Overridepublic 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);}@Overrideprotected 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.mzph.cn/web/37867.shtml

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

相关文章

鸿蒙本地签名不匹配问题

连接鸿蒙手机运行项目报如下错误 这是由于本地签名和鸿蒙设备签名不匹配导致的&#xff0c;需要注释掉如下代码&#xff0c;选择file project 自动签名 勾选auto选项&#xff0c;会在build-profile.json5中生成一个签名&#xff0c;然后运行就ok了~

创建一个Django用户认证系统

目录 1、Django2、Django用户认证系统User 模型&#xff1a;Authentication 视图&#xff1a;认证后端 (Authentication Backends)&#xff1a;Form 类&#xff1a;中间件 (Middleware)&#xff1a;权限和组 (Permissions and Groups)&#xff1a; 3、创建一个django用户认证系…

MNIST手写字体识别(算法基础)

快教程 10分钟入门神经网络 PyTorch 手写数字识别 慢教程 【深度学习Pytorch入门】 简单回归问题-1 梯度下降算法 梯度下降算法 l o s s x 2 ∗ s i n ( x ) loss x^2 * sin(x) lossx2∗sin(x) 求导得&#xff1a; f ‘ ( x ) 2 x s i n x x 2 c o s x f^(x)2xsinx x^…

Cesium大屏-vue3注册全局组件

1.需求 说明&#xff1a;产品经理要求开发人员在地图大屏上面随意放置组件&#xff0c;并且需要通过数据库更改其组件大小&#xff0c;位置等&#xff1b;适用于大屏组件中场站视角、任意位置标题等。 2.实现 2.1GlobalComponents.vue 说明&#xff1a;containerList可以通…

python基础语法 004-2流程控制- for遍历

1 遍历 1.1 什么是遍历&#xff1f; 可以遍历的元素&#xff1a;字符串、列表、元组、字典、集合字符串是可以进行for 循环。&#xff08;容器对象&#xff0c;序列&#xff09;可迭代对象iterable 例子&#xff1a; 1 &#xff09;、for遍历字符串&#xff1a; name xiao…

RK3568驱动指南|第十五篇 I2C-第167章 I2C上拉电阻

瑞芯微RK3568芯片是一款定位中高端的通用型SOC&#xff0c;采用22nm制程工艺&#xff0c;搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码&#xff0c;支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU&#xff0c;可用于轻量级人工…

SpringBoot实现图片添加水印

提示&#xff1a;今日完成图片添加水印功能 后续可能还会继续完善这个功能 文章目录 目录 文章目录 前端部分 后端 Xml Controller层 Sercive层 Service实现层 Config配置层 application.properties 文件后缀名获取 常量定义 前端部分 <!DOCTYPE html> <htm…

WIN11,如何同时连接有线网络与WLAN无线网络

之前写了两篇文章&#xff0c;一篇是双网卡多网卡时win11如何设置网卡优先级_多网卡设置网卡优先级-CSDN博客 另一篇是win11 以太网和WLAN冲突 连接网线时导致WiFi掉线 解决_win11 以太网和wifi不能同时生效-CSDN博客 这篇是对上面两篇的补充&#xff1a;主要解决电脑重启后&…

语音芯片TD5580,USB小音响芯片—拓达半导体

有时候电脑的声卡会出现损坏的问题&#xff0c;给我们的生活带来了很多麻烦。这时候&#xff0c;我们就需要一款方便易用的产品来解决声卡问题。USB声卡小音响就是为了解决这个问题而设计的一款便捷的产品。它不仅可以作为一个小音响&#xff0c;让您在工作和娱乐的时候享受高品…

docker-compose搭建minio对象存储服务器

docker-compose搭建minio对象存储服务器 最近想使用oss对象存储进行用户图片上传的管理&#xff0c;了解了一下例如aliyun或者腾讯云的oss对象存储服务&#xff0c;但是呢涉及到对象存储以及经费有限的缘故&#xff0c;决定自己手动搭建一个oss对象存储服务器&#xff1b; 首先…

创建XCOM窗体和跳转连接

Xcom 窗体&#xff1a; (groupBox组合框&#xff0c;comboBox下拉框) xcom代码&#xff1a; namespace _01_作业 {// 1kb 1024B 1200B// 1MB public partial class Form1 : Form{public List<string> botelv new List<string> { "600","1200&…

如何使用 SPM 插件从 Pkl 配置文件生成 Swift 接口

文章目录 前言示例展示 Pkl 配置生成 Swift 绑定手动安装和使用 pkl-gen-swift创建 SPM 命令插件加载 Pkl 配置总结前言 Pkl(全称为 Pickle)是苹果推出的一种全新的专用于配置的编程语言。它允许开发人员通过类型和内置验证安全、直观地设计数据模型。 作为苹果语言,Pkl 有…

Python容器 之 列表--下标和切片

列表的切片 得到是 新的列表字符串的切片 得到是 新的字符串 如果下标 不存在会报错 list1 [1, 3.14, "hello", False] print(list1)# 获取 列表中 第一个数据 print(list1[0]) # 1# 获取列表中的最后一个数据 print(list1[-1]) # [False]# 获取中间两个数 即 3.1…

3.2ui功能讲解之graph页面

本节重点介绍 : graph页面target页面flags页面status页面tsdb-status页面 访问地址 $ip:9090 graph页面 autocomplete 可以补全metrics tag信息或者 内置的关键字 &#xff0c;如sum聚合函数table查询 instante查询&#xff0c; 一个点的查询graph查询调整分辨率 resolutio…

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

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

Django 一对多关系

1&#xff0c;创建 Django 应用 Test/app9 django-admin startapp app9 2&#xff0c;注册应用 Test/Test/settings.py 3&#xff0c;添加应用路由 Test/Test/urls.py from django.contrib import admin from django.urls import path, includeurlpatterns [path(admin/,…

数据资产安全策略的定制化之道:深入了解各企业独特需求,量身打造个性化的数据资产保护方案,确保数据安全无虞,助力企业稳健发展

目录 一、引言 二、企业数据资产安全现状分析 &#xff08;一&#xff09;数据安全风险多样化 &#xff08;二&#xff09;传统安全措施难以满足需求 &#xff08;三&#xff09;企业数据资产安全意识亟待提高 三、定制化数据资产安全策略的重要性 &#xff08;一&#…

natvicat为什么连不上linux上的mysql?

老规矩&#xff0c;废话不多说&#xff0c;直接上教程。 号外&#xff0c;数据库管理工具领域的知名品牌Navicat&#xff0c;推出其免费版本——Navicat Premium Lite&#xff0c;用户可从Navicat官网下载体验这款软件。 https://www.navicat.com.cn/download/navicat-premium-…

【HALCON】如何实现hw窗口自适应相机拍照成像的大小

前言 在开发一个喷码检测软件的时候碰到相机成像和hw窗体的大小不一致&#xff0c;hw太小显示不完全成像的图片&#xff0c;这使得成像不均匀&#xff0c;现场辨别起来比较不直观&#xff0c;因此需要对其进行一个调整。 解决 省略掉读取图片的环节&#xff0c;我们只需要将…

ruoyi—cloud 新建模块+生成代码

1.复制一个模块——修改名字 2.打开模块下的yml文件&#xff0c;修改端口号和名字 &#xff08;1&#xff09;修改一个名字 &#xff08;2&#xff09;打开yml文件 &#xff08;3&#xff09;修改端口号&#xff0c;不要重复 &#xff08;4&#xff09;改名字和模块一致 3.…