前言:在【spring源码分析】IOC容器初始化(三)中已经分析了BeanDefinition注册之前的一些准备工作,下面将进入BeanDefinition注册的核心流程。


 //DefaultBeanDefinitionDocumentReader
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 进行bean标签解析
// 如果解析成功,则返回BeanDefinitionHolder,BeanDefinitionHolder为name和alias的BeanDefinition对象
// 如果解析失败,则返回null
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 进行标签处理,主要对bean标签的相关属性进行处理 如: p:name="测试用例"
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 注册BeanDefinition
// Register the final decorated instance.
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
} catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// 发出响应时间,通知监听器,已完成该bean标签的解析
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}

前面分析了如何解析bean标签的默认属性,在进行BeanDefinition注册之前,还需对bean标签的相关属性进行处理,第9行代码处。

BeanDefinitionParserDelegate#decorateBeanDefinitionIfRequired

     public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder definitionHolder) {
return decorateBeanDefinitionIfRequired(ele, definitionHolder, null);
} public BeanDefinitionHolder decorateBeanDefinitionIfRequired(
Element ele, BeanDefinitionHolder definitionHolder, @Nullable BeanDefinition containingBd) { // 解析完成后的返回值,封装了其自定义属性的BeandefinitionHolder
BeanDefinitionHolder finalDefinition = definitionHolder; // #1.遍历属性,查看是否有适用于装饰的属性
// Decorate based on custom attributes first.
NamedNodeMap attributes = ele.getAttributes();
for (int i = 0; i < attributes.getLength(); i++) {
Node node = attributes.item(i);
finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
} // #2.遍历子节点,查看是否有适用于装饰的子节点
// Decorate based on custom nested elements.
NodeList children = ele.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node node = children.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
}
}
return finalDefinition;
}

分析:

这里代码逻辑比较简单,就是遍历节点的属性或子节点,检查是否需要装饰的节点,如直接在bean标签里对属性赋值:p:name="XXX"。其核心点在第16行处与第25行处。

BeanDefinitionParserDelegate#decorateIfRequired

 public BeanDefinitionHolder decorateIfRequired(
Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) { // 首先获取自定义标签的命名空间
String namespaceUri = getNamespaceURI(node);
// 过滤掉默认的命名空间,因为这里是自定义空间的解析,默认命名空间上面已经进行了解析
if (namespaceUri != null && !isDefaultNamespace(namespaceUri)) {
// 通过命名空间获取对应的空间处理器
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler != null) {
// 进行装饰处理 在SimplePropertyNamespaceHandler处理器中
BeanDefinitionHolder decorated =
handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));
if (decorated != null) {
return decorated;
}
} else if (namespaceUri.startsWith("http://www.springframework.org/")) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", node);
} else {
// A custom namespace, not to be handled by Spring - maybe "xml:...".
if (logger.isDebugEnabled()) {
logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]");
}
}
}
return originalDef;
}

分析:

  • 首先得到节点命名空间uri,并判断namespaceUri不为"http://www.springframework.org/schema/beans"。
  • 然后通过namespaceUri解析出命名空间解析器,这里会调用DefaultNamespaceHandlerResolver#resolve函数,该函数在【spring源码分析】IOC容器初始化(三)中已经分析过。
  • 最后通过SimplePropertyNamespaceHandler#decorate进行装饰处理。

SimplePropertyNamespaceHandler#decorate

 public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) {
// 如果当前节点是属性节点
if (node instanceof Attr) {
Attr attr = (Attr) node;
// 获取name、value属性
String propertyName = parserContext.getDelegate().getLocalName(attr);
String propertyValue = attr.getValue();
// 获取bean的propertyValues集合
MutablePropertyValues pvs = definition.getBeanDefinition().getPropertyValues();
// 如果已经存在属性了,则报错
if (pvs.contains(propertyName)) {
parserContext.getReaderContext().error("Property '" + propertyName + "' is already defined using " +
"both <property> and inline syntax. Only one approach may be used per property.", attr);
}
// 如果属性name以-ref结尾,则需要进行解析,否则直接加入MutablePropertyValues集合中
if (propertyName.endsWith(REF_SUFFIX)) {
propertyName = propertyName.substring(0, propertyName.length() - REF_SUFFIX.length());
pvs.add(Conventions.attributeNameToPropertyName(propertyName), new RuntimeBeanReference(propertyValue));
}
else {
pvs.add(Conventions.attributeNameToPropertyName(propertyName), propertyValue);
}
}
// 返回已经封装了property属性的BeanDefinitionHolder
return definition;
}

分析:

其实这段代码的逻辑也比较简单,就是获取解析节点的name、value属性,然后放入MutablePropertyValues集合中。

BeanDefinitionReaderUtils#registerBeanDefinition

 public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException { // 注册beanName
// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
// 调用DefaultListableBeanFactory#registerBeanDefinition方法进行bean注册
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // 注册aliases
// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
// 这里调用的是SimpleAliasRegistry#registerAlias
registry.registerAlias(beanName, alias);
}
}
}

分析:

BeanDefinition注册分两步:

  • beanName注册(重点),这里会委托DefaultListableBeanFactory#registerBeanDefinition进行注册。
  • aliases注册,同样这里会委托DefaultListableBeanFactory#registerAlias进行注册。

DefaultListableBeanFactory#registerBeanDefinition

 public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
// 校验beanName与beanDefinition非空
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null"); // 校验BeanDefinition
// 这是注册前的最后一次校验,主要是对属性methodOverrides进行校验
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
} catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
// 从缓存中获取指定beanName的BeanDefinition
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
// 如果缓存中存在
if (existingDefinition != null) {
// 如果存在但是不允许覆盖,则抛出异常
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
// 覆盖BeanDefinition的ROLE大于被覆盖的ROLE,打印info日志
else if (existingDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (logger.isInfoEnabled()) {
logger.info("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
existingDefinition + "] with [" + beanDefinition + "]");
}
}
// 如果覆盖BeanDefinition与被覆盖的BeanDefinition不相同,打印debug日志
else if (!beanDefinition.equals(existingDefinition)) {
if (logger.isDebugEnabled()) {
logger.debug("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
// 其他,打印debug日志
else {
if (logger.isTraceEnabled()) {
logger.trace("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
// 允许覆盖,直接覆盖原来的BeanDefinition
this.beanDefinitionMap.put(beanName, beanDefinition);
}
// 如果缓存中不存在
else {
// 检测创建Bean阶段是否已经开启,如果开启,需要对beanDefinitionMap做并发控制
if (hasBeanCreationStarted()) {
// beanDefinitionMap为全局变量,避免并发情况
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
// 添加BeanDefinition到beanDefinitionMap中
this.beanDefinitionMap.put(beanName, beanDefinition);
// 更新beanName集合beanDefinitionNames
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
// 从manualSingletonNames中移除beanName
if (this.manualSingletonNames.contains(beanName)) {
Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
}
// 如果bean创建阶段未开启
else {
// Still in startup registration phase
// 添加BeanDefinition到beanDefinitionMap中
this.beanDefinitionMap.put(beanName, beanDefinition);
// 添加beanName到beanDefinitionNames集合中
this.beanDefinitionNames.add(beanName);
// 从manualSingletonNames中移除beanName
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;
}
// 如果缓存存在,则更新beanName对应的缓存
if (existingDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}

分析:

整段函数理解起来还是比较顺畅的,这里谨记我们的最终落脚点beanDefinitionMap。

  • 在BeanDefinition注册前会对其进行最后一次校验,判断方法覆盖是否与工厂方法并存,如果并存,则会抛出异常。
  • 从缓存中查找是否存在BeanDefinition,如果存在并且不允许覆盖,则抛出异常,否则这几覆盖原来的BeanDefinition。
  • 如果缓存中不存在BeanDefinition,则进行注册。

至此,BeanDefinition基于beanName和alias的维度,都已经注入到缓存中,接下来就是初始化然后使用bean了,接下来会继续进行分析,这里先看加载BeanDefinition的整个过程:

注:图片来源芋道源码

总结

过多的总结显得苍白无力,最后一张图片足以说明问题。


by Shawn Chen,2018.12.10日,晚。

【spring源码分析】IOC容器初始化(四)的更多相关文章

  1. SPRING源码分析:IOC容器

    在Spring中,最基本的IOC容器接口是BeanFactory - 这个接口为具体的IOC容器的实现作了最基本的功能规定 - 不管怎么着,作为IOC容器,这些接口你必须要满足应用程序的最基本要求: ...

  2. Spring源码解析-ioc容器的设计

    Spring源码解析-ioc容器的设计 1 IoC容器系列的设计:BeanFactory和ApplicatioContext 在Spring容器中,主要分为两个主要的容器系列,一个是实现BeanFac ...

  3. spring源码分析---IOC(1)

    我们都知道spring有2个最重要的概念,IOC(控制反转)和AOP(依赖注入).今天我就分享一下spring源码的IOC. IOC的定义:直观的来说,就是由spring来负责控制对象的生命周期和对象 ...

  4. Spring源码分析(二十四)初始化非延迟加载单例

    摘要: 本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 完成BeanFactory的初始化工作,其中包括ConversionS ...

  5. spring 源码之 ioc 容器的初始化和注入简图

    IoC最核心就是两个过程:IoC容器初始化和IoC依赖注入,下面通过简单的图示来表述其中的关键过程:

  6. Spring源码阅读-IoC容器解析

    目录 Spring IoC容器 ApplicationContext设计解析 BeanFactory ListableBeanFactory HierarchicalBeanFactory Messa ...

  7. Spring 源码剖析IOC容器(一)概览

    目录 一.容器概述 二.核心类源码解读 三.模拟容器获取Bean ======================= 一.容器概述 spring IOC控制反转,又称为DI依赖注入:大体是先初始化bean ...

  8. Spring源码解析-IOC容器的实现

    1.IOC容器是什么? IOC(Inversion of Control)控制反转:本来是由应用程序管理的对象之间的依赖关系,现在交给了容器管理,这就叫控制反转,即交给了IOC容器,Spring的IO ...

  9. Spring源码解析-IOC容器的实现-ApplicationContext

    上面我们已经知道了IOC的建立的基本步骤了,我们就可以用编码的方式和IOC容器进行建立过程了.其实Spring已经为我们提供了很多实现,想必上面的简单扩展,如XMLBeanFacroty等.我们一般是 ...

  10. Spring源码之IOC容器创建、BeanDefinition加载和注册和IOC容器依赖注入

    总结 在SpringApplication#createApplicationContext()执行时创建IOC容器,默认DefaultListableBeanFactory 在AbstractApp ...

随机推荐

  1. 【原】Oracle EBS 11无法打开Form及Form显示乱码的解决

    问题:Oracle EBS 11无法打开Form及Form显示乱码 解决: 1.尝试使用jre1.5或1.6安装目录下jre/bin/server目录里的jvm.dll替换JInitiator安装目录 ...

  2. IO_ADDRESS()的实现【转】

    上面我们说了如何去在系统中自己实现一个设置系统寄存器的一个方法,上面归根到底要进行物理地址到虚拟地址的映射 现在我们就说说IO_ADDRESS()的实现 #define __REG32ALI(addr ...

  3. ASP.NET Core 身份验证(一)

    前言 这篇文章我想带领大家了解一下 ASP.NET Core 中如何进行的身份验证,在开始之前强烈建议还没看过我写的 Identity 系列文章的同学先看一下. Identity 入门系列文章: Id ...

  4. [AI开发]将深度学习技术应用到实际项目

    本文介绍如何将基于深度学习的目标检测算法应用到具体的项目开发中,体现深度学习技术在实际生产中的价值,算是AI算法的一个落地实现.本文算法部分可以参见前面几篇博客: [AI开发]Python+Tenso ...

  5. Java中Map和Object的互相转换方式

    一.使用Apache中的BeanUtils类,导入commons-beanutils包. Jar包Maven下载地址:https://mvnrepository.com/artifact/common ...

  6. redis 初识

    架构 sharding redis 集群是主从式架构,数据分片是根据hash slot(哈希槽来分布) 总共有16384个哈希槽,所以理论上来说,集群的最大节点(master) 数量是16384个.一 ...

  7. 距离度量以及python实现(一)

    1. 欧氏距离(Euclidean Distance)        欧氏距离是最易于理解的一种距离计算方法,源自欧氏空间中两点间的距离公式. (1)二维平面上两点a(x1,y1)与b(x2,y2)间 ...

  8. Spring Boot系列(一) Spring Boot准备知识

    本文是学习 Spring Boot 的一些准备知识. Spring Web MVC Spring Web MVC 的两个Context 如下图所示, 基于 Servlet 的 Spring Web M ...

  9. 图像检索(5):基于OpenCV实现小型的图像数据库检索

    本文对前面的几篇文章进行个总结,实现一个小型的图像检索应用. 一个小型的图像检索应用可以分为两部分: train,构建图像集的特征数据库. retrieval,检索,给定图像,从图像库中返回最类似的图 ...

  10. MVC中使用Ninject依赖注入

    在.NET MVC中使用Ninject注入,主要分为以下几步: 使用NuGet包添加Ninject引用,我添加的是目前最新版本3.34 在App_Start文件夹下,创建NinjectControll ...