专题五: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方法

	@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到底怎么来的。

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

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

相关文章

2.3章节Python中的数值类型

1.整型数值 2.浮点型数值 3.复数   Python中的数值类型清晰且丰富&#xff0c;主要分为以下几种类型&#xff0c;每种类型都有其特定的用途和特性。 一、整型数值 1.定义&#xff1a;整数类型用于表示整数值&#xff0c;如1、-5、100等。 2.特点&#xff1a; Python 3中的…

面试题-Spring家族与SpringIOC

1.spring家族的介绍 Spring简单图&#xff1a; 2.IOC原理 IOC就是原先代码里需要开发者实现对象的创建和关系依赖&#xff0c;反转交给SpringIOC容器管理对象的生命周期和对象之间的依赖关系。 依赖注入的方式&#xff1a; Setter&#xff1a;实现特定属性的public sette…

RedHat9 | podman容器-续集

一、管理容器存储和网络资源 使用容器来运行简单的进程&#xff0c;然后退出。可以配置容连续运行特定服务&#xff0c;如数据库服务。如果持续运行服务&#xff0c;需要向容器添加更多的资源&#xff0c;如持久存储或对其他网络的访问权限。 针对企业容器平台上的大型部署&a…

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

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

SuperMap GIS基础产品FAQ集锦(20240701)

一、SuperMap iDesktopX 问题1&#xff1a;对于数据提供方提供的osgb格式的数据&#xff0c;如何只让他生成一个s3mb文件呢&#xff1f;我用倾斜入库的方式会生成好多个s3mb缓存文件 11.1.1 【解决办法】不能控制入库后只生成一个s3mb文件&#xff1b;可以在倾斜入库的时候设…

永磁同步电机离线参数识别

引言 永磁同步电机&#xff08;PMSM&#xff09;因其结构简单、功率密度高、转矩惯量比大和效率高等优点&#xff0c;在工业生产、航空航天和新能源交通等领域得到了广泛应用。然而&#xff0c;传统的参数辨识方法依赖位置传感器&#xff0c;这不仅增加了硬件成本&#xff0c;…

如何借用物联网快速实现高标准农田信息化

如何借用物联网快速实现高标准农田信息化 高标准农田信息化&#xff0c;作为现代农业发展的重要基石&#xff0c;是指在建设高产、稳产、节水、环保的农田基础上&#xff0c;深度融合现代信息技术&#xff0c;实现农田管理的精准化、智能化和高效化。物联网&#xff08;Intern…

sql server启动、连接 与 navicat连接sql server

一、sql server 启动 1.搜索cmd->以管理员身份运行 2.输入以下命令 net start mssqlserver 3.服务器启动成功 二、sql server连接 1.打开ssms&#xff0c;输入&#xff0c;连接 2.右键&#xff0c;属性 3.连接&#xff0c;勾选允许远程连接到此服务器 三、navicat连接sq…

20人团队如何免费使用 Atlassian 云产品?

企业赚钱越来越难&#xff0c;尤其是初创团队或小型团队更倾向于使用免费工具支持业务。团队规模影响协作复杂度&#xff0c;Atlassian 考虑到小团队的需求&#xff0c;提供了多种选择。比如&#xff0c;Jira 和 Confluence 的云版本有免费版&#xff0c;包含基本的项目管理功能…

三坐标测量机:柔性生产制造中的高精度测量解决方案

柔性生产制造是制造业的核心竞争力之一。它强调生产线的灵活性和适应性&#xff0c;以满足市场对产品多样化和个性化的需求。在当今快速变化的工业环境中&#xff0c;随着消费者对产品个性化和定制化需求的增加&#xff0c;柔性生产制造和三坐标测量机的结合&#xff0c;为智能…

MSVCR120.DLL丢失的多种修复方法,助你快速解决dll问题

在日常生活和工作中&#xff0c;电脑已经成为我们不可或缺的工具。然而&#xff0c;在使用电脑的过程中&#xff0c;我们常常会遇到一些问题&#xff0c;其中之一就是电脑运行软件时提示找不到msvcr120.dll。如果该文件缺失或损坏&#xff0c;可能会导致依赖它的应用程序无法启…

大聪明教你学Java | 深入浅出聊 RocketMQ

前言 &#x1f34a;作者简介&#xff1a; 不肯过江东丶&#xff0c;一个来自二线城市的程序员&#xff0c;致力于用“猥琐”办法解决繁琐问题&#xff0c;让复杂的问题变得通俗易懂。 &#x1f34a;支持作者&#xff1a; 点赞&#x1f44d;、关注&#x1f496;、留言&#x1f4…

一、课程介绍,基础—环境安装、判断、循环语句等(爬虫及数据可视化)

一、课程介绍&#xff0c;基础—环境安装、判断、循环语句等&#xff08;爬虫及数据可视化&#xff09; 1. 课程介绍1.1 相关内容1.2 学习目标1.3 学习内容安排 2. python2.1 环境配置2.2 标识符和关键字2.3 运算符2.4 判断语句2.5 循环语句 1. 课程介绍 1.1 相关内容 10天的…

Node.js安装及配置

文章目录 1.安装Node.js2.创建目录3.配置环境变量4.配置全局安装路径和缓存路径(可选)配置Webstorm 1.安装Node.js https://registry.npmmirror.com/binary.html?pathnode 推荐安装18.x版本 2.创建目录 下载解压后进入目录&#xff0c;创建node_global和node_cache两个空文…

AI播客下载:Practical AI(人工智能最新进展)

Practical AI这是由 http://Changelog.com推出的节目。Changelog 本身做了许多跟软件开发的 podcast 节目 。比如《The Changelog》播客 &#xff0c;这是一个专注于软件领域的播客&#xff0c;每周一发布最新新闻摘要&#xff0c;周三进行深入技术访谈&#xff0c;周五则是访谈…

Vue-element 组件dialog右上角点击 X 清空表单校验信息

问题&#xff1a; 点击确定触发校验后&#xff0c;点击弹窗右上角的 X号关闭弹窗后再次打开弹窗&#xff0c;校验规则没有被清空 解决方法&#xff1a;

Linux——数据流和重定向,制作镜像

1. 数据流 标准输入&#xff08; standard input &#xff0c;简称 stdin &#xff09;&#xff1a;默认情况下&#xff0c;标准输入指从键盘获取的输入 标准输出&#xff08; standard output &#xff0c;简称 stdout &#xff09;&#xff1a;默认情况下&#xff0c;命令…

每日复盘-20240701

今日关注&#xff1a; 20240701 六日涨幅最大: ------1--------301182--------- 凯旺科技 五日涨幅最大: ------1--------300977--------- 深圳瑞捷 四日涨幅最大: ------1--------300977--------- 深圳瑞捷 三日涨幅最大: ------1--------300461--------- 田中精机 二日涨幅最…

迈阿密色主题学科 HTML5静态导航源码

源码介绍 迈阿密色主题学科 HTML5静态导航源码&#xff0c;源码直接上传可用&#xff0c;有技术的可以拿去写个后端搜索调用百度接口&#xff0c;也可用于做引导页下面加你网址添加一个A标签就行了&#xff0c;很简单&#xff0c;需要的朋友就拿去吧 界面预览 源码下载 迈阿…

nginx限制特定国家或地区的访问

一、查看 1. 先查看本地nginx是否有ngx_http_geoip2模块&#xff08;如果有的同学有该模块可以直接跳到下面直接配置nginx服务&#xff09; nginx -V 二、安装 1. 安装所需依赖 yum install -y libmaxminddb-devel pcre-devel zlib-devel gcc gcc-c make git 2. 下载ngx_h…