Spring是如何加载BeanDefinition的

前言

在Spring加载Bean的过程中是无法感知每个Bean的,也无法知道每一个bean具体的特征,比如某个bean是单例还是原型,是否懒加载等等。所以Spring需要beandefinition来对每个不同的bean进行描述,并不是直接把Bean直接存入容器而是读取成beanDefinition,再根据BeanDefenition对bean的描述进行实例化。那么Spring是如何读取这些BeanDefinition的呢?

准备工作

我们从源码出发一步步Debug来看BeanDefinition读取的过程,再次之前我已经下载编译了Spring的源码,还需要创建xml文件和对于的bean对象。

  • 创建类Bean
public class Bean{
	private String username = "LYX";
}
  • 创建一个spring.xml并注册类Bean
<?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 https://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
	<bean class="com.lyx.learn.entity.Teacher" name="teacher">
		<property name="name" value="lyx"></property>
	</bean>

	<bean id="bean" class="com.lyx.learn.entity.Bean">
	</bean>
</beans>

读取Xml文件

就以我们常见的ClassPathXmlApplicationContext(“文件路径”)来进行debug,在new ClassPathXmlApplicationContext对象时就将xml文件路径作为参数传入。其内部调用了另一个构造方法,如下图所示
在这里插入图片描述
在这个构造方法中会通过setConfigLocations设置资源路径,但只是标记路径并没有进行读取操作。
在这里插入图片描述
设置完路径之后就会调用refresh方法,这个方法基本上包含了spring初始化过程的整个周期,具体可以了解我的另一篇文章链接: Spring源码:refresh方法解析。在做完准备工作之后就开始设置容器以及读取beanDefinition了,在obtainFreshBeanFactory方法中我们可以看到具体的读取过程,所以本篇文章只关注obtainFreshBeanFactory方法。
在这里插入图片描述
在obtainFreshBeanFactory只关注两件事,就是创建beanFactory容器和读取beanDefinition。这些都是在refreshBeanFactory方法中实现的。它会先判断是否已经存在BeanFactory,如果已经存在就销毁,然后重新创建。
在这里插入图片描述
在这里插入图片描述
可以关注一下createBeanFactory方法,这个方法就是创建容器的方法。在里面会直接new一个DefaultListableBeanFactory,所以我们常说的容器和beanFactory实际上就是DefaultListableBeanFactory,这里不过多阐述,有兴趣的可以自行去了解一下这个容器。我们重点要说的是loadBeanDefinitions。
在这里插入图片描述
在loadBeanDefinitions中会创建一个XmlBeanDefinitionReader对象然后调用XmlBeanDefinitionReader的loadBeanDefinitions方法将开始传入的资源路径开始读取。(除了XmlBeanDefinitionReader还其他BeanDefinitionReader,目的时通过不同方式读取BeanDefinition,它们都实现自接口BeanDefinitionReader)
在这里插入图片描述
精华都在这个loadBeanDefinitions中了,在启动spring发现日志打印Loading XML bean definitions from XXX就可以知道初始化进行到这一步了。
在这里插入图片描述
我们都知道在Spring中干活的往往都是do开头的(doGetBean,doCreateBean)方法,所以我们得关注doLoadBeanDefinitions方法,入参是xml文件的输入流对象。在doLoadBeanDefinitions中就做了两件事,解析xml文件,把一个个的bean标签解析成document对象后封装成beanDefinition。
在这里插入图片描述
doLoadDocument是由实现类DefaultDocumentLoader执行的,其内部会创建一个DocumentBuilder对象,再通过这个builder来解析。解析过程就不说了,就是像剥洋葱一样一层层解析各个子标签。
在这里插入图片描述
解析完成后拿到顶层的Document对象,调用registerBeanDefinitions方法,在方法中会交给BeanDefinitionDocumentReader的registerBeanDefinitions来执行。
在这里插入图片描述
BeanDefinitionDocumentReader拿到Document对象后直接拿根目录交给doRegisterBeanDefinitions,也像剥洋葱一样层层解析各个标签。
在这里插入图片描述

我们先看spring对doRegisterBeanDefinitions的说明,在遇到beans标签是会递归执行,在这里使用了委派模式。但不是我们关注的重点。
在这里插入图片描述
解析过程是在parseBeanDefinitions中执行的,preProcessXml和postProcessXml是解析是的前后置方法,默认实现是空的,可以理解为留给开发人员自定义拓展的。
在这里插入图片描述
在parseBeanDefinitions中将当前element元素交给parseDefaultElement方法进行解析。
在这里插入图片描述

parseDefaultElement

在parseDefaultElement中我们可以看到常用的bean,import,alias等标签,不同的标签交给不同的处理器。我们就关注processBeanDefinition标签看看是如何解析bean标签的。
在这里插入图片描述
由下图我们看到spring在拿到bean标签后会通过
委派者的parseBeanDefinitionElement创建一个BeanDefinitionHolder对象,这个BeanDefinitionHolder实际上以内部变量的形式持有了BeanDefinition对象。
在这里插入图片描述
在parseBeanDefinitionElement会根据标签的beanName,class等属性创建一个beanDefinition对象,但此时只是封装了class等属性,beanName和beanDefinition对象在这个holder中是作为两个属性存在的。解析过程就不细说了,大致就是拿到各标签的属性值设置到beanDefinition中。直接看返回结果可以发现解析完成后的结果如下图所示
在这里插入图片描述
在这里插入图片描述
beanDefinitionHolder结构如下图所示
在这里插入图片描述
拿到这个beanDefinitionHolder后就准备把beandefinition注册到beanDefinitionMap了(存放beanDefinitions的容器),注册的过程会在registerBeanDefinition方法中实现。这个时候就会调用我们刚才提到的spring默认的容器DefaultListableBeanFactory的实现了。在方法中会先尝试根据beanName获取对应的beanDefinition,如果已经存在就会抛异常。然后就判断是否已经开始创建bean了就锁住beanDefinitionMap更新beanDefinition,否则就直接存入beanDefinitionMap。代码如下

	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {

		Assert.hasText(beanName, "Bean name must not be empty");
		Assert.notNull(beanDefinition, "BeanDefinition must not be null");

		if (beanDefinition instanceof AbstractBeanDefinition) {
			try {
				((AbstractBeanDefinition) beanDefinition).validate();
			}
			catch (BeanDefinitionValidationException ex) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Validation of bean definition failed", ex);
			}
		}
        // 尝试根据beanName从beanDefinitionMap获取BeanDefinition
		BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
		// 如果已经存在
		if (existingDefinition != null) {
			if (!isAllowBeanDefinitionOverriding()) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
						"': There is already [" + existingDefinition + "] bound.");
			}
			// 下面的省略
			// ...............
			this.beanDefinitionMap.put(beanName, beanDefinition);
		}
		else {
			// 如果bean已经开始创建
			if (hasBeanCreationStarted()) {
				// Cannot modify startup-time collection elements anymore (for stable iteration)
				// 容器上锁
				synchronized (this.beanDefinitionMap) {
					// 更新beanDefinition
					this.beanDefinitionMap.put(beanName, beanDefinition);
					// 加入updatedDefinitions列表
					List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
					updatedDefinitions.addAll(this.beanDefinitionNames);
					updatedDefinitions.add(beanName);
					this.beanDefinitionNames = updatedDefinitions;
					// 更新完成
					if (this.manualSingletonNames.contains(beanName)) {
						Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
						updatedSingletons.remove(beanName);
						this.manualSingletonNames = updatedSingletons;
					}
				}
			}
			else {
				// 直接存入容器
				// Still in startup registration phase
				this.beanDefinitionMap.put(beanName, beanDefinition);
				this.beanDefinitionNames.add(beanName);
				this.manualSingletonNames.remove(beanName);
			}
			this.frozenBeanDefinitionNames = null;
		}

		if (existingDefinition != null || containsSingleton(beanName)) {
			resetBeanDefinition(beanName);
		}
		else if (isConfigurationFrozen()) {
			clearByTypeCache();
		}
	}

hasBeanCreationStarted是干嘛的

hasBeanCreationStarted顾名思义就是判断是否已经开始创建bean了。大家都知道spring留的拓展点无处不在,我们随时可以通过容器的api随时往里面注册beanDefinition,虽然beanDefinitionMap默认是ConcurrentHashMap容器,但更新beanDefinition修改的不只是beanDefinitionMap,在修改的时候不能保障其他的业务的线程安全。所以需要加锁。当然我们这次演示是正常流程线性走完的,默认是直接this.beanDefinitionMap.put(beanName, beanDefinition)的,到这里一个BeanDefinition的注册流程已经走完了。

总结

本篇文章说的是如何从xml文件中加载beanDefinition的,在spring中有许多加载bean的方式。每种方式的具体实现不同,但基本上都是把信息封装成beanDefinition最后调用registerBeanDefinition进行注册。注解扫描的加载方式是通过beanFactoryPostProcessor拓展点完成的,感兴趣的可以看看beanFactoryPostProcessor后置处理器。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值