Spring5.0源码学习系列之浅谈BeanFactory创建
Spring5.0源码学习系列之浅谈BeanFactory创建过程
系列文章目录
@
博客前言介绍
提示:在上一章的学习中,我们简单了解了Spring IoC容器启动初始化的主流程,不过并没有详细解释,因为代码比较复杂,没有做长篇大论,所以本文接着学习BenFactory的创建过程,学习源码建议带着疑问去学,一点点跟,时间积累之后就可以串起来
提示:以下是本篇文章正文内容,下面案例可供参考
一、获取BeanFactory主流程
在前面的学习中,#refresh是IoC容器创建很关键的主线,在代码里,可以找到obtainFreshBeanFactory这个比较关键的方法,这个方法就是BeanFactory的创建过程方法,本文ClassPathXmlApplicationContext为准,进行源码的学习,上篇博客是以AnnotationApplicationContext为准进行简单的分析,本文继续比较详细的讲述
实验环境:
- SpringFramework版本
- Springframework5.0.x
 
- 开发环境
- JAR管理:gradle 4.9/ Maven3.+
- 开发IDE:IntelliJ IDEA 2018.2.5
- JDK:jdk1.8.0_31
- Git Server:Git fro window 2.8.3
- Git Client:SmartGit18.1.5(可选)
 
maven项目,需要加上pom配置:
 <properties>
   <springframework.version>5.0.9.RELEASE</springframework.version>
 </properties>
 <dependencies>
     <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-context</artifactId>
         <version>${springframework.version}</version>
     </dependency>
 </dependencies>
如果是gradle环境,可以直接在Spring框架project里新增一个module,具体可以参考教程:加上对应的配置

SpringBean.java:
package com.example.bean;
import org.springframework.beans.factory.InitializingBean;
/**
 * <pre>
 *      SpringBean
 * </pre>
 *
 * <pre>
 * @author mazq
 * 修改记录
 *    修改后版本:     修改人:  修改日期: 2020/11/05 10:50  修改内容:
 * </pre>
 */
public class SpringBean implements InitializingBean {
    public SpringBean(){
        System.out.println("SpringBean构造函数");
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("SpringBean afterPropertiesSet");
    }
}
applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="springBean" class="com.example.bean.SpringBean" ></bean>
</beans>
测试类,使用ClassPathXmlApplicationContext
package com.example;
import com.example.bean.SpringBean;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
 * <pre>
 *      TestController
 * </pre>
 *
 * <pre>
 * @author mazq
 * 修改记录
 *    修改后版本:     修改人:  修改日期: 2020/11/05 10:22  修改内容:
 * </pre>
 */
public class TestApplication {
    public static void testClassPathXmlApplicationContext() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        SpringBean  springBean = context.getBean(SpringBean.class);
        System.out.println(springBean);
    }
    public static void main(String[] args) {
        // 测试ClassPathXmlApplicationContext
        testClassPathXmlApplicationContext();
    }
}
在SpringBean 打断点,debug进行调试:找到#refresh方法

obtainFreshBeanFactory示例:
/**
 * Tell the subclass to refresh the internal bean factory.
 * @return the fresh BeanFactory instance
 * @see #refreshBeanFactory()
 * @see #getBeanFactory()
 */
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
	// 创建BeanFactory
	refreshBeanFactory();
	// 返回refreshBeanFactory方法创建好的BeanFactory
	ConfigurableListableBeanFactory beanFactory = getBeanFactory();
	if (logger.isDebugEnabled()) {
		logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
	}
	return beanFactory;
}
然后refreshBeanFactory是由哪个基类实现的?我们找来一张uml类图:

从图可以知道ClassPathXmlApplicationContext通过AbstractRefreshableApplicationContext实现AbstractApplicationContext,所以refreshBeanFactory是AbstractRefreshableApplicationContext#refreshBeanFactory
ok,所以主体的路线,就可以熟悉,用uml时序图表示:

二、refreshBeanFactory创建过程
refreshBeanFactory方法是BeanFactory的创建过程,接着跟
org.springframework.context.support.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 {
	// 先判断是否有BeanFactory
	if (hasBeanFactory()) {
		// destroy Beans
		destroyBeans();
		// 关闭BeanFactory
		closeBeanFactory();
	}
	try {
		// 实例化DefaultListableBeanFactory
		DefaultListableBeanFactory beanFactory = createBeanFactory();
		// 设置序列化id
		beanFactory.setSerializationId(getId());
		// 定义BeanFactory的一些属性(是否允许Bean覆盖,是否允许循环依赖)
		customizeBeanFactory(beanFactory);
		// 加载应用中的BeanDefinition
		loadBeanDefinitions(beanFactory);
		this.beanFactory = beanFactory;
	}
	catch (IOException ex) {
		throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
	}
}
{@link org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions}
/**
 * Loads the bean definitions via an XmlBeanDefinitionReader.
 * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
 * @see #initBeanDefinitionReader
 * @see #loadBeanDefinitions
 */
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
	// Create a new XmlBeanDefinitionReader for the given BeanFactory.
	// 给BeanFactory实例化一个XmlBeanDefinitionReader实例
	XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
	// Configure the bean definition reader with this context's
	// resource loading environment.
	beanDefinitionReader.setEnvironment(this.getEnvironment());
	beanDefinitionReader.setResourceLoader(this);
	beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
	// Allow a subclass to provide custom initialization of the reader,
	// then proceed with actually loading the bean definitions.
	// 给子类提供初始化XmlBeanDefinitionReader的模板方法
	initBeanDefinitionReader(beanDefinitionReader);
	// 重点在这,继续跟
	loadBeanDefinitions(beanDefinitionReader);
}
// org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory)
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
	Resource[] configResources = getConfigResources();
	if (configResources != null) {
		// 继续跟
		reader.loadBeanDefinitions(configResources);
	}
	String[] configLocations = getConfigLocations();
	if (configLocations != null) {
		reader.loadBeanDefinitions(configLocations);
	}
}
{@link org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions}
@Override
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
	Assert.notNull(resources, "Resource array must not be null");
	int counter = 0;
	// 变量资源对象,加载BeanDefinition
	for (Resource resource : resources) {
		// 在这里,经过debug,调到XmlBeanDefinitionReader.loadBeanDefinitions(resource)
		counter += loadBeanDefinitions(resource);
	}
	// 返回统计数量,表示总共有多少个BeanDefinition
	return counter;
}
XmlBeanDefinitionReader类
{@link org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions}
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
	return loadBeanDefinitions(new EncodedResource(resource));
}
// 省略...
/**
 * Load bean definitions from the specified XML file.
 * @param encodedResource the resource descriptor for the XML file,
 * allowing to specify an encoding to use for parsing the file
 * @return the number of bean definitions found
 * @throws BeanDefinitionStoreException in case of loading or parsing errors
 */
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
	Assert.notNull(encodedResource, "EncodedResource must not be null");
	if (logger.isInfoEnabled()) {
		logger.info("Loading XML bean definitions from " + encodedResource);
	}
	// 用ThreadLocal存放资源对象,目的是保证线程安全
	Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
	if (currentResources == null) {
		currentResources = new HashSet<>(4);
		this.resourcesCurrentlyBeingLoaded.set(currentResources);
	}
	if (!currentResources.add(encodedResource)) {
		throw new BeanDefinitionStoreException(
				"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
	}
	try {
		InputStream inputStream = encodedResource.getResource().getInputStream();
		try {
			InputSource inputSource = new InputSource(inputStream);
			if (encodedResource.getEncoding() != null) {
				inputSource.setEncoding(encodedResource.getEncoding());
			}
			// 核心,往下跟
			return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
		}
		finally {
			inputStream.close();
		}
	}
	catch (IOException ex) {
		throw new BeanDefinitionStoreException(
				"IOException parsing XML document from " + encodedResource.getResource(), ex);
	}
	finally {
		currentResources.remove(encodedResource);
		if (currentResources.isEmpty()) {
			this.resourcesCurrentlyBeingLoaded.remove();
		}
	}
}
/**
 * Actually load bean definitions from the specified XML file.
 * @param inputSource the SAX InputSource to read from
 * @param resource the resource descriptor for the XML file
 * @return the number of bean definitions found
 * @throws BeanDefinitionStoreException in case of loading or parsing errors
 * @see #doLoadDocument
 * @see #registerBeanDefinitions
 */
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
		throws BeanDefinitionStoreException {
	try {
		// 读取xml信息,将xml信息保存在Document对象
		Document doc = doLoadDocument(inputSource, resource);
		// 封装BeanDefinitions并注册,继续解析Document对象,往下跟
		return registerBeanDefinitions(doc, resource);
	}
	catch (BeanDefinitionStoreException ex) {
		throw ex;
	}
	catch (SAXParseException ex) {
		throw new XmlBeanDefinitionStoreException(resource.getDescription(),
				"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
	}
	catch (SAXException ex) {
		throw new XmlBeanDefinitionStoreException(resource.getDescription(),
				"XML document from " + resource + " is invalid", ex);
	}
	catch (ParserConfigurationException ex) {
		throw new BeanDefinitionStoreException(resource.getDescription(),
				"Parser configuration exception parsing XML from " + resource, ex);
	}
	catch (IOException ex) {
		throw new BeanDefinitionStoreException(resource.getDescription(),
				"IOException parsing XML document from " + resource, ex);
	}
	catch (Throwable ex) {
		throw new BeanDefinitionStoreException(resource.getDescription(),
				"Unexpected exception parsing XML document from " + resource, ex);
	}
}
/**
 * Register the bean definitions contained in the given DOM document.
 * Called by {@code loadBeanDefinitions}.
 * <p>Creates a new instance of the parser class and invokes
 * {@code registerBeanDefinitions} on it.
 * @param doc the DOM document
 * @param resource the resource descriptor (for context information)
 * @return the number of bean definitions found
 * @throws BeanDefinitionStoreException in case of parsing errors
 * @see #loadBeanDefinitions
 * @see #setDocumentReaderClass
 * @see BeanDefinitionDocumentReader#registerBeanDefinitions
 */
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
	BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
	// 获取已有的BeanDefinition数量
	int countBefore = getRegistry().getBeanDefinitionCount();
	// 注册BeanDefinition,关注点:registerBeanDefinitions、createReaderContext
	documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
	// 返回新注册的BeanDefinition统计数量
	return getRegistry().getBeanDefinitionCount() - countBefore;
}
{@link org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#registerBeanDefinitions}
/**
 * This implementation parses bean definitions according to the "spring-beans" XSD
 * (or DTD, historically).
 * <p>Opens a DOM Document; then initializes the default settings
 * specified at the {@code <beans/>} level; then parses the contained bean definitions.
 */
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
	this.readerContext = readerContext;
	logger.debug("Loading bean definitions");
	Element root = doc.getDocumentElement();
	// 往下跟,从xml根节点开始解析文件
	doRegisterBeanDefinitions(root);
}
/**
 * Register each bean definition within the given root {@code <beans/>} element.
 */
protected void doRegisterBeanDefinitions(Element root) {
	// Any nested <beans> elements will cause recursion in this method. In
	// order to propagate and preserve <beans> default-* attributes correctly,
	// keep track of the current (parent) delegate, which may be null. Create
	// the new (child) delegate with a reference to the parent for fallback purposes,
	// then ultimately reset this.delegate back to its original (parent) reference.
	// this behavior emulates a stack of delegates without actually necessitating one.
	BeanDefinitionParserDelegate parent = this.delegate;
	this.delegate = createDelegate(getReaderContext(), root, parent);
	if (this.delegate.isDefaultNamespace(root)) {
		// profile(环境校验)
		String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
		if (StringUtils.hasText(profileSpec)) {
			String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
					profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
			// 不是当前profile的,跳过
			if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
				if (logger.isInfoEnabled()) {
					logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
							"] not matching: " + getReaderContext().getResource());
				}
				return;
			}
		}
	}
	// preProcessXml模板方法
	preProcessXml(root);
	// 重点是parseBeanDefinitions
	parseBeanDefinitions(root, this.delegate);
	// postProcessXml 同样是模板方法 过
	postProcessXml(root);
	this.delegate = parent;
}
/**
 * Parse the elements at the root level in the document:
 * "import", "alias", "bean".
 * @param root the DOM root element of the document
 */
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
	if (delegate.isDefaultNamespace(root)) {
		NodeList nl = root.getChildNodes();
		for (int i = 0; i < nl.getLength(); i++) {
			Node node = nl.item(i);
			if (node instanceof Element) {
				Element ele = (Element) node;
				if (delegate.isDefaultNamespace(ele)) {
					// 解析default namespace下面的元素
					parseDefaultElement(ele, delegate);
				}
				else {
					// 解析自定义标签元素
					delegate.parseCustomElement(ele);
				}
			}
		}
	}
	else {
		delegate.parseCustomElement(root);
	}
}
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
	// import元素处理
	if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
		importBeanDefinitionResource(ele);
	}
	// alias元素处理
	else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
		processAliasRegistration(ele);
	}
	// bean元素处理
	else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
		processBeanDefinition(ele, delegate);
	}
	// 嵌套beans处理
	else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
		// recurse
		doRegisterBeanDefinitions(ele);
	}
}
bean元素处理,本文挑beana元素的处理进行讲述:
/**
 * Process the given bean element, parsing the bean definition
 * and registering it with the registry.
 */
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
	//解析Bean元素为BeanDefinition,再通过BeanDefinitionHolder封装成BeanDefinitionHolder对象
	BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
	if (bdHolder != null) {
		// 有自定义属性会进行相应的解析
		bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
		try {
			// Register the final decorated instance.
			// BeanDefinition的注册过程
			BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
		}
		catch (BeanDefinitionStoreException ex) {
			getReaderContext().error("Failed to register bean definition with name '" +
					bdHolder.getBeanName() + "'", ele, ex);
		}
		// Send registration event.
		getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
	}
}
知识点归纳
提示:这里对文章进行归纳:

Spring5.0源码学习系列之浅谈BeanFactory创建的更多相关文章
- Spring5.0源码学习系列之浅谈循环依赖问题
		前言介绍 附录:Spring源码学习专栏 在上一章的学习中,我们对Bean的创建有了一个粗略的了解,接着本文浅谈Spring循环依赖问题,这是一个面试比较常见的问题 1.什么是循环依赖? 所谓的循环依 ... 
- Spring5.0源码学习系列之浅谈懒加载机制原理
		前言介绍 附录:Spring源码学习专栏 在上一章的学习中,我们对Bean的创建有了一个粗略的了解,接着本文挑一个比较重要的知识点Bean的懒加载进行学习 1.什么是懒加载? 懒加载(Lazy-ini ... 
- Spring5.0源码学习系列之事务管理概述
		Spring5.0源码学习系列之事务管理概述(十一),在学习事务管理的源码之前,需要对事务的基本理论比较熟悉,所以本章节会对事务管理的基本理论进行描述 1.什么是事务? 事务就是一组原子性的SQL操作 ... 
- Spring5.0源码学习系列之Spring AOP简述
		前言介绍 附录:Spring源码学习专栏 在前面章节的学习中,我们对Spring框架的IOC实现源码有了一定的了解,接着本文继续学习Springframework一个核心的技术点AOP技术. 在学习S ... 
- JDK源码学习系列05----LinkedList
		JDK源码学习系列05----LinkedList 1.LinkedList简介 LinkedList是基于双向链表实 ... 
- JDK源码学习系列04----ArrayList
		JDK源码学习系列04----ArrayList 1. ... 
- JDK源码学习系列03----StringBuffer+StringBuilder
		JDK源码学习系列03----StringBuffer+StringBuilder 由于前面学习了StringBuffer和StringBuilder的父类A ... 
- JDK源码学习系列02----AbstractStringBuilder
		JDK源码学习系列02----AbstractStringBuilder 因为看StringBuffer 和 StringBuilder 的源码时发现两者都继承了AbstractStringBuil ... 
- JDK源码学习系列01----String
		JDK源码学习系列01----String 写在最前面: 这是我JDK源码学习系列的第一篇博文,我知道 ... 
随机推荐
- kubernetes-集群架构与组件
			1. kubernetes集群架构 2. kubernetes组件 1) master组件 kube-apiserver Kubernetes API,集群的统一入口,各组件协调者,以RESTful ... 
- volatile型变量语义讲解一 :对所有线程的可见性
			volatile型变量语义讲解一 :对所有线程的可见性 一.volatile变量语义一的概念 当一个变量被定义成volatile之后,具备两个特性: 特性一:保证此变量对所有线程的可见性.这里的&qu ... 
- day07 Pyhton学习
			一.昨日内容回顾 小数据池,常量池 id()内存地址 is == 的区别 is 判断的是内存地址 == 判断的是值 存在的意义: 快速的创建字符串,整数,布尔值的对象 帮你节省内存 解码和编码 enc ... 
- BUUCTF-misc九连环 详解
			这个没什么玄学,我们解压出来一张照片,放到hxd中搂一眼,最后结尾的那几行中看到了zip压缩包的结尾标识符,难道这里面还有压缩包,于是我们就formostlrb 果然有图片有压缩包 我们打开压缩包看到 ... 
- rabbitmq--通配符模式Topics
			topic模式也称为主题模式,其实他相对于routing模式最大的好处就是他多了一种匹配模式的路由,怎么理解匹配呢,其实就相当于我们之前正则的.*这种,不过他的匹配机制可能不是这种(其实除了匹配规则外 ... 
- openresty使用redis作本地缓存
			一,为什么要使用redis作本地缓存? 1,使用缓存通常会有三层 当使用openresty作为web服务器时,我们更看重是的它可以通过lua编程的扩展能力,就openresty而言,它可以实现的功能非 ... 
- Drone 安装教程
			Drone 安装教程 一. CentOS设置 1. 更换阿里源 curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/ ... 
- django—模板相关
			关于在html文件中使用模板语言时,一些符号的含义 {{}}:显示变量相关,在模板渲染的时候替换成值 {%%}:处理逻辑相关 django模板语言中,对于列表字典等容器类型,不能够通过[ 索引/键值 ... 
- git删除缓存区中文件
			删除缓冲区中的文件 git rm --cached "文件路径",不删除物理文件,仅将该文件从缓存中删除: git rm --f "文件路径",不仅将该文件从缓 ... 
- Flutter - 自定义Dialog弹窗
			------------恢复内容开始------------ Flutter - 自定义Dialog弹窗 应用场景:app系统版本升级弹窗,系统退出登录弹窗,首页广告弹窗,消息中心弹窗,删除文件弹窗等 ... 
