专题四:Spring源码初始化环境与BeanFactory

上文我们通过new ClassPathXmlApplicationContext("applicationContext.xml");这段代码看了下Spring是如何将Xml里面内容注入到Java对象中,并通过context.getBean("jmUser");方式获得了一个对象实例,而避开使用new 来耦合。今天我们就来看看Spring是如何做到的呢?

上图是AbstractApplicationContext的类图,dedug跟踪这段代码

复制代码
 new ClassPathXmlApplicationContext("applicationContext.xml");

ClassPathXmlApplicationContext构造方法

java 复制代码
/**
	 * 根据xml路径,创建一个ClassPathXmlApplicationContext实例
	 * Create a new ClassPathXmlApplicationContext with the given parent,
	 * loading the definitions from the given XML files.
	 * @param configLocations array of resource locations
	 * @param refresh whether to automatically refresh the context,
	 * loading all bean definitions and creating all singletons.
	 * Alternatively, call refresh manually after further configuring the context.
	 * @param parent the parent context
	 * @throws BeansException if context creation failed
	 * @see #refresh()
	 */
	public ClassPathXmlApplicationContext(
			String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
			throws BeansException {
		// 调用父类构造方法,主要是初始化环境变量相关信息,核心代码在 AbstractApplicationContext 类中
		super(parent);
		// 设置配置文件的位置 ApplicationContext的实现可能会使用默认的配置文件位置。
		setConfigLocations(configLocations);
		// 默认构造true
		if (refresh) {
			// 进入核心方法
			refresh();
		}
	}

1、初始化环境变量相关信息

发现最终进入

java 复制代码
	/**
	 * Create a new AbstractApplicationContext with no parent.
	 * 创建一个没有父容器的AbstractApplicationContext的实例
	 */
	public AbstractApplicationContext() {
		//  返回一个 PathMatchingResourcePatternResolver(this)实例
		this.resourcePatternResolver = getResourcePatternResolver();
	}

	/**
	 * Create a new AbstractApplicationContext with the given parent context.
	 * @param parent the parent context
	 */
	public AbstractApplicationContext(@Nullable ApplicationContext parent) {
		this();
		// 将父容器复制给成员变量 parent,如果parent不为空且属于ConfigurableEnvironment类型则合并环境
		setParent(parent);
	}

2、设置配置文件的位置

java 复制代码
/**
	 * Set the config locations for this application context.
	 * <p>If not set, the implementation may use a default as appropriate.
	 * 配置了配置文件的位置
	 * 如果未设置配置位置,某些ApplicationContext的实现可能会使用默认的配置文件位置。
	 * 例如,ClassPathXmlApplicationContext如果没有指定配置位置,会尝试查找类路径下的applicationContext.xml文件。
	 */
	public void setConfigLocations(@Nullable String... locations) {
		if (locations != null) {
			Assert.noNullElements(locations, "Config locations must not be null");
			this.configLocations = new String[locations.length];
			for (int i = 0; i < locations.length; i++) {
//				getEnvironment().resolveRequiredPlaceholders(path),从路径中解析展位福${}参数
				this.configLocations[i] = resolvePath(locations[i]).trim();
			}
		}
		else {
			this.configLocations = null;
		}
	}


	/**
	 * Resolve the given path, replacing placeholders with corresponding
	 * environment property values if necessary. Applied to config locations.
	 * @param path the original file path
	 * @return the resolved file path
	 * @see org.springframework.core.env.Environment#resolveRequiredPlaceholders(String)
	 */
	protected String resolvePath(String path) {
		return getEnvironment().resolveRequiredPlaceholders(path);
	}

	public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
		return this.propertyResolver.resolveRequiredPlaceholders(text);
	}

3、进入refresh方法

java 复制代码
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);
			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();
			}
		}
	}

整个流程可以分为以下几个主要步骤:

  1. 初始化上下文环境信息。
  2. 解析XML配置文件,创建并初始化BeanFactory
  3. 设置BeanFactory的类加载器,注册一些默认的环境Bean。
  4. 提供扩展点用于对BeanFactory进行进一步的定制。
  5. 调用BeanFactory的后置处理器。
  6. 注册Bean的后置处理器。
  7. 初始化消息源,用于国际化支持。
  8. 初始化事件广播器,用于发布事件。
  9. 提供扩展点用于在刷新上下文时做一些特殊的初始化。
  10. 检查并注册监听器Bean。
  11. 实例化所有剩余的单例Bean(非懒加载)。
  12. 发布相应的事件通知,表示上下文刷新完成。

步骤概览

1. prepareRefresh()
prepareRefresh();

prepareRefresh()方法用于初始化上下文环境信息,包括设置启动时间、活动状态等基本属性。它确保在刷新过程中,应用上下文处于正确的状态。

2. obtainFreshBeanFactory()
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

该方法解析XML配置文件,创建并初始化BeanFactory,并载入Bean定义。它是Spring应用上下文初始化的关键步骤之一。

3. prepareBeanFactory(beanFactory)
prepareBeanFactory(beanFactory);

此步骤设置BeanFactory的类加载器,并注册一些默认的环境Bean,如类加载器、表达式解析器等。它确保BeanFactory在上下文中能够正确工作。

4. postProcessBeanFactory(beanFactory)
postProcessBeanFactory(beanFactory);

这是一个扩展点,通常为空实现。开发者可以在子类中重写该方法,对BeanFactory进行进一步的定制,如添加额外的Bean定义或修改现有的Bean定义。

5. invokeBeanFactoryPostProcessors(beanFactory)
invokeBeanFactoryPostProcessors(beanFactory);

该方法调用BeanFactory的后置处理器,这些处理器可以在Bean实例化之前对Bean定义进行修改或增加新的Bean定义。

6. registerBeanPostProcessors(beanFactory)
registerBeanPostProcessors(beanFactory);

在此步骤中,Spring注册Bean的后置处理器,这些处理器会在Bean的创建过程中拦截并进行处理,如执行依赖注入和初始化回调等。

7. initMessageSource()
initMessageSource();

该方法初始化消息源,用于支持国际化(i18n)。它允许应用程序根据用户的语言环境显示不同的消息。

8. initApplicationEventMulticaster()
initApplicationEventMulticaster();

Spring使用事件广播器来发布和监听应用程序事件。该方法初始化事件广播器,确保事件能够在应用上下文中正确传播。

9. onRefresh()
onRefresh();

这是另一个扩展点,通常为空实现。开发者可以在子类中重写该方法,在刷新上下文时执行一些特殊的初始化操作。

10. registerListeners()
registerListeners();

该方法检查并注册监听器Bean,用于监听和处理应用程序事件。

11. finishBeanFactoryInitialization(beanFactory)
finishBeanFactoryInitialization(beanFactory);

此步骤实例化所有剩余的单例Bean(非懒加载)。它确保所有Bean都已准备就绪,可以在应用程序中使用。

12. finishRefresh()
finishRefresh();

最后,Spring发布相应的事件通知,表示上下文刷新已完成。此步骤通常包括发布ContextRefreshedEvent事件,通知所有监听器上下文已刷新。

总结

本章主要通过断点源码,一步步跟踪到refresh方法,然后整体跟踪了refresh的具体步骤,每一步大概干了什么内容,后面我们将一个一个方法深入讨论,具体做了什么?预留了什么扩展点。我们在开发中怎么去使用这些扩展点。以及后面SpringBoot和SpringCloud是怎样利用这些扩展点给我么提供开箱即用的功能的。

相关推荐
FF在路上15 分钟前
Knife4j调试实体类传参扁平化模式修改:default-flat-param-object: true
java·开发语言
真的很上进22 分钟前
如何借助 Babel+TS+ESLint 构建现代 JS 工程环境?
java·前端·javascript·css·react.js·vue·html
众拾达人1 小时前
Android自动化测试实战 Java篇 主流工具 框架 脚本
android·java·开发语言
皓木.1 小时前
Mybatis-Plus
java·开发语言
不良人天码星1 小时前
lombok插件不生效
java·开发语言·intellij-idea
守护者1701 小时前
JAVA学习-练习试用Java实现“使用Arrays.toString方法将数组转换为字符串并打印出来”
java·学习
源码哥_博纳软云1 小时前
JAVA同城服务场馆门店预约系统支持H5小程序APP源码
java·开发语言·微信小程序·小程序·微信公众平台
禾高网络1 小时前
租赁小程序成品|租赁系统搭建核心功能
java·人工智能·小程序
学会沉淀。1 小时前
Docker学习
java·开发语言·学习
如若1231 小时前
对文件内的文件名生成目录,方便查阅
java·前端·python