百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 文章教程 > 正文

SpringIOC分析(Xml配置)02

xsobi 2024-11-24 23:34 1 浏览

3.开始启动

SpringlOC容器对Bean配置资源的载入是从refresh()函数开始的,refresh()是一个模板方法,规定了IOC容器的启动流程,有些逻辑要交给其子类去实现。它对Bean配置资源进行载入ClassPathXmlApplicationContext 通过调用其父类AbstractApplicationContext的refresh()函数启动整个IOC容器对Bean定义的载入过程,现在我们来详细看看refresh()中的逻辑处理:

Bash
// 启动整个IOC容器对Bean定义的载入过程
@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.告诉子类启动 refreshBeanFactory() 方法,Bean定义资源文件的载入从子类的 refreshBeanFactory() 方法启动
		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.为容器的某些子类指定特殊的BeanPost事件处理器
			postProcessBeanFactory(beanFactory);

			// Invoke factory processors registered as beans in the context.
			// 5.调用所有注册的 BeanFactoryPostProcessors 的Bean
			invokeBeanFactoryPostProcessors(beanFactory);

			// Register bean processors that intercept bean creation.
			// 6.为beanFactory 注册 BeanPost事件处理器
			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.
			// 13.销毁已创建的Bean
			destroyBeans();

			// Reset 'active' flag.
			// 14.起效refresh操作,重置容器的同步标识
			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...
			// 15.重设公共缓存
			resetCommonCaches();
		}
	}
}

refresh()方法主要为IOC容器Bean的生命周期管理提供条件,Spring IOC 容器载入Bean配置信息从其子类容器的refreshBeanFactory()方法启动,所以整个refresh()中 "ConfigurablelListableBeanFactory beanFactory = obtainFreshBeanfactory();"这句以后代码的都是注册容器的信息源和生命周期事件,我们前面说的载入就是从这句代码开始启动。

refresh()方法的主要作用是:在创建IOC容器前,如果已经有容器存在,则需要把已有的容器销毁和关闭,以保证在refresh之后使用的是新建立起来的IOC容器。它类似于对IOC容器的重启,在新建立好的容器中对容器进行初始化,对Bean配置资源进行载入。

4.创建容器

obtainFreshBeanfactory()方法调用子类容器的 refreshBeanFactory()方法,启动容器载入Bean配置信息的过程,代码如下:

Bash
/**
 * Tell the subclass to refresh the internal bean factory.
 * @return the fresh BeanFactory instance
 * @see #refreshBeanFactory()
 * @see #getBeanFactory()
 */
// 注册容器信息源和生命周期事件
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
	// 这里使用了委派模式,父类定义抽象 refreshBeanFactory() 方法,具体实现调用子类容器
	// 的 refreshBeanFactory() 方法
	refreshBeanFactory();
	ConfigurableListableBeanFactory beanFactory = getBeanFactory();
	if (logger.isDebugEnabled()) {
		logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
	}
	return beanFactory;
}

AbstractApplicationContext 类中只抽象定义了refreshBeanFactory()方法,容器真正调用的是 其子类AbstractRefreshableApplicationContext实现的refreshBeanFactory()方法,方法的源 码如下:

/**
 * This implementation performs an actual refresh of this context's underlying
 * bean factory, shutting down the previous bean factory (if any) and
 * initializing a fresh bean factory for the next phase of the context's lifecycle.
 */
@Override
protected final void refreshBeanFactory() throws BeansException {
	// 如果已经有容器,销毁容器中的bean,关闭容器
	if (hasBeanFactory()) {
		destroyBeans();
		closeBeanFactory();
	}
	try {
		// 创建IOC容器
		DefaultListableBeanFactory beanFactory = createBeanFactory();
		beanFactory.setSerializationId(getId());
		// 对IOC容器进行定制化,如设置启动参数,开启注解的自动装配等
		customizeBeanFactory(beanFactory);
		// 调用载入bean 定义的方法,主要这里又使用了委派模式,在当前类中定义了抽象
		// 的 loadBeanDefinitions 方法,具体的实现调用子类容器
		loadBeanDefinitions(beanFactory);
		this.beanFactory = beanFactory;
	}
	catch (IOException ex) {
		throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
	}
}

在这个方法中,先判断BeanFactory是否存在,如果存在则先销毁 beans并关闭beanFactory,接着 创建DefaultListableBeanFactory,并调用loadBeanDefinitions(beanFactory)装载 bean定义。

5.载入配置路径

AbstractRefreshableApplicationContext中只定义了抽象的loadBeanDefinitions 方法,容器真正调 用的是其子类AbstractXmlApplicationContext对该方法的实现,AbstractXmlApplicationContext 的主要源码如下:

loadBeanDefinitions0方法同样是抽象方法,是由其子类实现的,也即在 AbstractXmlApplicationContext中。

/**
 * Loads the bean definitions via an XmlBeanDefinitionReader.
 * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
 * @see #initBeanDefinitionReader
 * @see #loadBeanDefinitions
 */
// 实现父类抽象的载入Bean方法
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
	// 创建XmlBeanDefinitionReader,即创建Bean读取器,并通过回调设置到容器中去,容器使用该读取器读取Bean配置资源
	// Create a new XmlBeanDefinitionReader for the given BeanFactory.
	XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

	// Configure the bean definition reader with this context's
	// resource loading environment.
	//
	beanDefinitionReader.setEnvironment(this.getEnvironment());
	beanDefinitionReader.setResourceLoader(this);
	// 为Bean读取器设置SAX xml解析器
	beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

	// Allow a subclass to provide custom initialization of the reader,
	// then proceed with actually loading the bean definitions.
	// 当bean读取器读取bean定义的xml资源文件时,启用xml的校验机制
	initBeanDefinitionReader(beanDefinitionReader);
	// bean读取器真正实现加载的方法
	loadBeanDefinitions(beanDefinitionReader);
}
/**
 * Initialize the bean definition reader used for loading the bean
 * definitions of this context. Default implementation is empty.
 * <p>Can be overridden in subclasses, e.g. for turning off XML validation
 * or using a different XmlBeanDefinitionParser implementation.
 * @param reader the bean definition reader used by this context
 * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader#setDocumentReaderClass
 */
protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader) {
	reader.setValidating(this.validating);
}

/**
 * Load the bean definitions with the given XmlBeanDefinitionReader.
 * <p>The lifecycle of the bean factory is handled by the {@link #refreshBeanFactory}
 * method; hence this method is just supposed to load and/or register bean definitions.
 * @param reader the XmlBeanDefinitionReader to use
 * @throws BeansException in case of bean registration errors
 * @throws IOException if the required XML document isn't found
 * @see #refreshBeanFactory
 * @see #getConfigLocations
 * @see #getResources
 * @see #getResourcePatternResolver
 */
// xml bean读取器加载bean配置资源
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
	// 获取bean配置资源的定位
	Resource[] configResources = getConfigResources();
	if (configResources != null) {
		// xml bean读取器调用其父类AbstractBeanDefinitionReader读取定位的bean配置资源
		reader.loadBeanDefinitions(configResources);
	}
	// 如果子类中获取的bean配置资源定位为空,则获取ClassPathXmlApplicationContext
	// 构造方法中setConfigLocations方法设置的资源
	String[] configLocations = getConfigLocations();
	if (configLocations != null) {
		// XML bean读取器调用其父类AbstractBenaDefinitionReader读取定位的Bean配置资源
		reader.loadBeanDefinitions(configLocations);
	}
}

/**
 * Return an array of Resource objects, referring to the XML bean definition
 * files that this context should be built with.
 * <p>The default implementation returns {@code null}. Subclasses can override
 * this to provide pre-built Resource objects rather than location Strings.
 * @return an array of Resource objects, or {@code null} if none
 * @see #getConfigLocations()
 */
// 这里又使用委派模式,调用子类获取bean配置资源定位的方法
// 该方法在ClassPathXmlApplicationContext中进行实现
@Nullable
protected Resource[] getConfigResources() {
	return null;
}

以XmlBean读取器的其中一种策略XmlBeanDefinitionReader为例。XmlBeanDefinitionReader调 用其父类AbstractBeanDefinitionReader的reader.loadBeanDefinitions()方法读取Bean配置资源。 由于我们使用ClassPathXmlApplicationContext作为例子分析,因此getConfigResources的返回值 为null,因此程序执行 reader.loadBeanDefinitions(configLocations)分支。

6.分配路径处理策略

在XmlBeanDefinitionReader的抽象父类AbstractBeanDefinitionReader中定义了载入过程。

AbstractBeanDefinitionReader的 loadBeanDefinitions0方法源码如下:

@Override
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
	return loadBeanDefinitions(location, null);
}

/**
 * Load bean definitions from the specified resource location.
 * <p>The location can also be a location pattern, provided that the
 * ResourceLoader of this bean definition reader is a ResourcePatternResolver.
 * @param location the resource location, to be loaded with the ResourceLoader
 * (or ResourcePatternResolver) of this bean definition reader
 * @param actualResources a Set to be filled with the actual Resource objects
 * that have been resolved during the loading process. May be {@code null}
 * to indicate that the caller is not interested in those Resource objects.
 * @return the number of bean definitions found
 * @throws BeanDefinitionStoreException in case of loading or parsing errors
 * @see #getResourceLoader()
 * @see #loadBeanDefinitions(org.springframework.core.io.Resource)
 * @see #loadBeanDefinitions(org.springframework.core.io.Resource[])
 */
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
	// 获取在IOC容器初始化过程中设置的资源加载器
	ResourceLoader resourceLoader = getResourceLoader();
	if (resourceLoader == null) {
		throw new BeanDefinitionStoreException(
				"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
	}

	if (resourceLoader instanceof ResourcePatternResolver) {
		// Resource pattern matching available.
		try {
			// 将指定位置的bean配置信息解析为SpirngIOC容器封装的资源
			// 加载多个指定位置的bean配置信息
			Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
			// 委派调用其子类XmlBeanDefinitionReader的方法,实现加载功能
			int loadCount = loadBeanDefinitions(resources);
			if (actualResources != null) {
				for (Resource resource : resources) {
					actualResources.add(resource);
				}
			}
			if (logger.isDebugEnabled()) {
				logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
			}
			return loadCount;
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException(
					"Could not resolve bean definition resource pattern [" + location + "]", ex);
		}
	}
	else {
		// Can only load single resources by absolute URL.
		// 将制定位置的Bean配置信息解析为SpringIOC容器封装的资源
		// 加载单个指定位置的bean配置信息
		Resource resource = resourceLoader.getResource(location);
		// 委派调用其子类XmlBeanDefinitionReader的方法,实现加载功能
		int loadCount = loadBeanDefinitions(resource);
		if (actualResources != null) {
			actualResources.add(resource);
		}
		if (logger.isDebugEnabled()) {
			logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
		}
		return loadCount;
	}
}

// 重载方法,调用loadBeanDefinitions
@Override
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
	Assert.notNull(locations, "Location array must not be null");
	int counter = 0;
	for (String location : locations) {
		counter += loadBeanDefinitions(location);
	}
	return counter;
}

AbstractRefreshableConfigApplicationContext的 loadBeanDefinitions(Resource... resources)方 法实际上是调用AbstractBeanDefinitionReader的loadBeanDefinitions0方法。

从对AbstractBeanDefinitionReader的loadBeanDefinitions()方法源码分析可以看出该方法就做了 两件事:

首先,调用资源加载器的获取资源方法resourceLoadergetResource(location)获取到要加载的资源。 其次,真正执行加载功能是其子类XmlBeanDefinitionReader的loadBeanDefinitions()方法。在 loadBeanDefinitions()方法中调用了AbstractApplicationContext的getResources()方法,跟进去之 后发现getResources()方法其实定义在ResourcePatternResolver中,此时,我们有必要来看一下 ResourcePatternResolver的全类图:

从上面可以看到ResourceLoader与ApplicationContext的继承关系,可以看出其实际调用的是 DefaultResourceloader 中的getSource()方法定位Resource,因为 ClassPathXmlApplicationContext 本身就是DefaultResourceLoader的实现类,所以此时又回到了 ClassPathXmlApplicationContext中来。

相关推荐

bootstrap入门

bootstrap是一个前端ui框架,它把我们网页开发常用的功能都写好了,我们使用它可以像搭积木一样的轻松的开发网站,不过现在都流行前后端分析了,而且layui也比较好用,个人觉得无论哪个ui框架,我...

BootStrap简介及应用要点

BootStrap简介BootStrap是基于HTML、CSS和JavaScript的框架,使你只需要写简单的代码就可以很快的搭建一个还不错的前端框架,他是后端程序员的福音,使它们只需要专注业务逻辑,...

如何在Bootstrap Studio中使用图标字体?

BootstrapStudioforMac是一款网站设计制作工具,图标字体类似于普通的Web字体,但是它们包含矢量形状,而不是字母和数字。那么如何在BootstrapStudio中使用图标字体...

Bootstrap5.0-全球流行的前端开源UI工具包迎来了大版本更新

Bootstrap5.0正式发布了,带来了很多亮点,还学得动吗?Bootstrap介绍...

BootstrapBlazor 模板适配移动设备使用笔记

项目模板BootstrapBlazorApp模板为了方便大家利用这套组件快速搭建项目,作者制作了项目模板(ProjectTemplates),使用dotnetnew命令行模式,使用步骤...

bootstrap的tab标签页的使用

标签tab页在,当前的web中应用十分广泛,君不见,在博客的右侧出现的最新文章和随机文章中有它的身影,在大型门户网站中也有它的身影,可以说其无处不在的刷着存在感。既然其如此嚣张的存在,我们没有理由不应...

使用 Bootstrap 的最简单方法 - 让你的 HTML 看起来赏心悦目

什么是BootstrapBootstrap是一个免费的开源CSS框架,使得前端Web开发变得更加简单。...

Bootstrap-table 使用总结

一、什么是Bootstrap-table?在业务系统开发中,对表格记录的查询、分页、排序等处理是非常常见的,在Web开发中,可以采用很多功能强大的插件来满足要求,且能极大的提高开发效率,本随笔介绍这...

建议收藏:哪些电子发票有XML格式?

报销时,财务要求提供发票XML格式。但不是所有的电子发票都有哦,目前只有数电发票(全面数字化的电子发票)有XML格式!目前的数电发票有下面这几大类:1、电子发票(增值税专用发票):适用于增值税一般纳税...

Mybatis中mapper的xml解析详解

上一篇文章分析了mapper注解关键类MapperAnnotationBuilder,今天来看mapper的项目了解析关键类XMLMapperBuilder。基础介绍回顾下之前是在分析configur...

word修改文中任意一处文字,其他地方相同的内容自动修改

我们工作写方案处理word的时候,经常会遇到这么一种情况,即文中存在多处相同的文字内容,可能是词语,也可能是段落。当我们修改了其中一处后,其他地方还得手动修改,十分不便。今天给大家分享一下,在word...

第9天 | 鸿蒙App开发实战,XML创建布局,共性很重要

XML声明布局的方式更加简便直观,是开发App的核心内容之一,咱们完全有必要搞清楚。每一个Component和ComponentContainer对象大部分属性都支持在XML中进行设置,它们有各自的X...

可扩展标记语言格式XML

1,XML(eXtensibleMarkupLanguage):指可扩展标记语言,一种数据表示格式,被设计用来传输和存储数据,不用于表现和展示数据。2,XML和基于XML的语言的整个结构是...

比较一下JSON与XML两种数据格式?

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

PROFINET工业以太网教程(16)-GSDML文件详解

前面的文章(PROFINET工业以太网教程(10)——GSD文件)我们介绍过GSD文件,它的全称是“GeneralStationDescription”,中文翻译为“通用站描述文件”。GSD文件的...