前言:在【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. LeetCode算法题-Prime Number of Set Bits in Binary Representation(Java实现)

    这是悦乐书的第311次更新,第332篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第180题(顺位题号是762).给定两个正整数L和R,在[L,R]范围内,计算每个整数的 ...

  2. MYSQL 主从复制,读写分离(8)

    Mysql 数据库的主从复制方案,是其自带的功能,并且主从复制并不是复制磁盘上的数据文件,而是通过binlog日志复制到需要同步的服务器上. 一 主从复制的原理实现 原理图解说: 数据库更改 生成数据 ...

  3. scrapy-redis 分布式爬虫

    为什么要学? Scrapy_redis在scrapy的基础上实现了更多,更强大的功能. 有哪些功能体现? request去重.爬虫持久化.实现分布式爬虫.断点续爬(带爬取的request存在redis ...

  4. Storm入门(十四)Trident API Overview

    The core data model in Trident is the "Stream", processed as a series of batches. A stream ...

  5. 年末展望:Oracle 对 JDK收费和.NET Core 给我们的机遇

    2018年就结束了,马上就要迎来2019年,这一年很不平凡,中美贸易战还在继续,IT互联网发生急剧变化,大量互联网公司开始裁员,微软的市值在不断上升 ,在互联网公司的市值下跌过程中爬到了第一的位置,我 ...

  6. Kubernetes 笔记 08 Deployment 副本管理 重新招一个员工来填坑

    本文首发于我的公众号 Linux云计算网络(id: cloud_dev),专注于干货分享,号内有 10T 书籍和视频资源,后台回复「1024」即可领取,欢迎大家关注,二维码文末可以扫. Hi,大家好, ...

  7. 关于Python深浅拷贝

    拷贝: 说明:原则上就是把数据分离出来,复制其数据,并以后修改互不影响. 何来深浅拷贝的说法? 深浅拷贝的“深”和“浅”可以理解为从变量到硬盘上的物理存储介质之间的层次的多少. 下面用一个示例来解释浅 ...

  8. 带你精读你不知道的Javasript(上)(一)

    斌果在这几天看了下你不知道的js这本书,这本书讲的东西还是挺不错的,其中有很多平时我压根没接触到的概念和方法.借此也可以丰富一下我对js的了解. 第一部分 第一章 作用域是什么? 1.程序中一点源代码 ...

  9. 从零开始搭建etcd分布式存储系统+web管理界面

    什么是ETCD 随着CoreOS和Kubernetes等项目在开源社区日益火热,它们项目中都用到的etcd组件作为一个高可用.强一致性的服务发现存储仓库,渐渐为开发人员所关注. 在云计算时代,如何让服 ...

  10. Linux 软硬链接的区别及目录权限对软硬链接的影响

    目录权限的影响 首先,构建实验环境如下图: 有 A.B 两个目录,A 目录下建有 test 文件,且文件中的内容为 "Hello".B 目录下分别创建 test 文件的硬链接 te ...