【spring源码分析】IOC容器初始化(四)
前言:在【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容器初始化(四)的更多相关文章
- SPRING源码分析:IOC容器
在Spring中,最基本的IOC容器接口是BeanFactory - 这个接口为具体的IOC容器的实现作了最基本的功能规定 - 不管怎么着,作为IOC容器,这些接口你必须要满足应用程序的最基本要求: ...
- Spring源码解析-ioc容器的设计
Spring源码解析-ioc容器的设计 1 IoC容器系列的设计:BeanFactory和ApplicatioContext 在Spring容器中,主要分为两个主要的容器系列,一个是实现BeanFac ...
- spring源码分析---IOC(1)
我们都知道spring有2个最重要的概念,IOC(控制反转)和AOP(依赖注入).今天我就分享一下spring源码的IOC. IOC的定义:直观的来说,就是由spring来负责控制对象的生命周期和对象 ...
- Spring源码分析(二十四)初始化非延迟加载单例
摘要: 本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 完成BeanFactory的初始化工作,其中包括ConversionS ...
- spring 源码之 ioc 容器的初始化和注入简图
IoC最核心就是两个过程:IoC容器初始化和IoC依赖注入,下面通过简单的图示来表述其中的关键过程:
- Spring源码阅读-IoC容器解析
目录 Spring IoC容器 ApplicationContext设计解析 BeanFactory ListableBeanFactory HierarchicalBeanFactory Messa ...
- Spring 源码剖析IOC容器(一)概览
目录 一.容器概述 二.核心类源码解读 三.模拟容器获取Bean ======================= 一.容器概述 spring IOC控制反转,又称为DI依赖注入:大体是先初始化bean ...
- Spring源码解析-IOC容器的实现
1.IOC容器是什么? IOC(Inversion of Control)控制反转:本来是由应用程序管理的对象之间的依赖关系,现在交给了容器管理,这就叫控制反转,即交给了IOC容器,Spring的IO ...
- Spring源码解析-IOC容器的实现-ApplicationContext
上面我们已经知道了IOC的建立的基本步骤了,我们就可以用编码的方式和IOC容器进行建立过程了.其实Spring已经为我们提供了很多实现,想必上面的简单扩展,如XMLBeanFacroty等.我们一般是 ...
- Spring源码之IOC容器创建、BeanDefinition加载和注册和IOC容器依赖注入
总结 在SpringApplication#createApplicationContext()执行时创建IOC容器,默认DefaultListableBeanFactory 在AbstractApp ...
随机推荐
- redis-dump实现redis库迁移
最近公司有切换redis库的需求,找了个简单的redis迁移方法,不过也有缺点.就是对于实时性要求很高的redis库迁移无法做到数据的实时同步.不过对于简单的redis库备份和迁移还是可以的,各位看官 ...
- winform 实现类似于TrackBar的自定义滑动条,功能更全
功能很全,随便列几个 1.可以设置滑块的大小,边框颜色.背景色.形状等等吧 2.可以设置轨道的方向.边框颜色.背景色.阴影等等 ... 效果图: 下载链接https://download.csdn.n ...
- ReactiveSwift源码解析(六) SignalProtocol的take(first)与collect()延展实现
上篇博客我们聊了observe().map().filter()延展函数的具体实现方式以及使用方式.我们在之前的博客中已经聊过,Signal的主要功能是位于SignalProtocol的协议延展中的, ...
- IM开发者的零基础通信技术入门(二):通信交换技术的百年发展史(下)
1.系列文章引言 1.1 适合谁来阅读? 本系列文章尽量使用最浅显易懂的文字.图片来组织内容,力求通信技术零基础的人群也能看懂.但个人建议,至少稍微了解过网络通信方面的知识后再看,会更有收获.如果您大 ...
- php批量修改文件名称
<?php//利用PHP目录和文件函数遍历用户给出目录的所有的文件和文件夹,修改文件名称function fRename($dirname){ if(!is_dir($dirname)){ ec ...
- python列表的交、并、差集
#!/usr/bin/env python3 l1 = ['] l2 = ['] # 交集 result1 = [i for i in l1 if i in l2] result2 = list(se ...
- Python-网络爬虫模块-requests模块之请求
Python原生库urllib库不太方便使用,本着"人生苦短, 我用Python"的珍惜生命精神, 基于urllib, 开发了一个对人类来说, 更好使用的一个库——requests ...
- 解决 React-Native mac 运行报错 error Failed to build iOS project. We ran "xcodebuild" command but it exited with error code 65. To debug build logs further, consider building your app with Xcode.app, by ope
React-Native 开发的项目,Android 方面没有任何问题,IOS 就是无法跑起来,报错信息如下: mac 10.14.4 xcode 10.2.1 error Failed to bui ...
- Java进阶篇设计模式之三 ----- 建造者模式和原型模式
前言 在上一篇中我们学习了工厂模式,介绍了简单工厂模式.工厂方法和抽象工厂模式.本篇则介绍设计模式中属于创建型模式的建造者模式和原型模式. 建造者模式 简介 建造者模式是属于创建型模式.建造者模式使用 ...
- SLAM+语音机器人DIY系列:(四)差分底盘设计——5.底盘PID控制参数整定
摘要 运动底盘是移动机器人的重要组成部分,不像激光雷达.IMU.麦克风.音响.摄像头这些通用部件可以直接买到,很难买到通用的底盘.一方面是因为底盘的尺寸结构和参数是要与具体机器人匹配的:另一方面是因为 ...