Spring源码阅读 之 bean的注册
在前面我们已经学习了配置的加载,读取,解析。现在我们已经能够将一份配置转变成对应的一个个beandefinition了,我们知道Spring是一个IOC的容器,那么我们如何将这个一个个beandefinition放入我们的容器呢?换而言之,如何进行注册呢?相信看完本文后你会豁然开朗
文章目录
我们要分析的核心代码就是org.springframework.beans.factory.support.DefaultListableBeanFactory的registerBeanDefinition方法,代码如下:
@Override
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 {
// 这里会对MethodOverrides做检查
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
// 判断是否有同名的bean被注册过了
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
// 是否允许直接进行负载
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
else if (existingDefinition.getRole() < beanDefinition.getRole()) {
// ....省略部分代码,都是输出日志的
// beanName做key放入map中,完成注册
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
/**
*这段代码比较难理解
*我会在下面进行一一分析
*/
if (hasBeanCreationStarted()) {
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
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 {
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;
}
// 需要重置BeanDefinition,
// 当前注册的bean的定义已经在beanDefinitionMap缓存中存在,
// 或者其实例已经存在于单例bean的缓存中
if (existingDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}
beanDefinitionNames,manualSingletonNames,beanDefinitionMap,frozenBeanDefinitionNames是做什么的?
beanDefinitionNames:按照注册顺序的beanDefinition名称集合
beanDefinitionMap:beanDefinition的一个缓存,key是beanDefinitionName
manualSingletonNames:手动注册的单例类名称,按照注册顺序排序
frozenBeanDefinitionNames:在完成所以示例的初始化,Spring会将配置冻结起来,代表能缓存所有的bean的元数据,frozenBeanDefinitionNames保存的其实就是beanDefinitionNames,只不过是以数组形式保存的,用数组的原因是因为frozenBeanDefinitionNames在期望情况下不应该发生改变
hasBeanCreationStarted(),这是做什么的?为什么这种情况下需要加锁?
这里主要是判断是不是已经有bean被创建了,请注意,正常来说到目前为止,我们并有创建任何的bean,到现在我们做的仅仅是解析配置,并注册beanDefinition,如果有bean被创建意味着什么呢?大家可以思考下,当我们已经创建bean,代表着我们已经开始利用容器去进行我们的业务操作了,类似于我们执行了如下代码:
public static void main(String[] args) {
1. ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
2. Object d1 = context.getBean("d2");
3. /** 利用获取的d1进行业务操作*/
4. DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
factory.registerBeanDefinition("我要再注册一个bean",new GenericBeanDefinition());
}
在这种情况下,Spring容器无法保证使用者是在线程安全的情况下调用了,也就是无法保证下面的代码不会出现线程安全问题,所以需要加锁。
this.beanDefinitionMap.put(beanName, beanDefinition);
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;
}
另外,我们需要注意的是,虽然beanDefinitionMap底层使用的是ConcurrentHashMap,但是需要注意的是,ConcurrentHashMap只能保证自身操作的线程安全,并不能保证整体业务的线程安全,大家不要产生误解!
为什么要将this.beanDefinitionMap.put(beanName, beanDefinition)也放入同步代码块中?
我们之前已经知道了beanDefinitionNames是要按照注册顺序保存的,所以必须要加放入的动作也进行同步,不然可能出现顺序错误
为什么在加锁后给集合中添加元素还要进行一次类似于复制的操作(addAll)?
我们在业务操作过程中,很可能调用Spring的某些方法,这些方法需要遍历beanDefinitionNames,这些遍历方法通常都是使用迭代器的,我们知道迭代过程中如果我们又对集合进行了添加,移除的操作,会引发快速失败机制如果对快速失败机制不熟悉的请自行百度。为了避免这种情况所以选择新建一个集合然后进行复制
Spring源码阅读 之 bean的注册的更多相关文章
- Bean实例化(Spring源码阅读)-我们到底能走多远系列(33)
我们到底能走多远系列(33) 扯淡: 各位: 命运就算颠沛流离 命运就算曲折离奇 命运就算恐吓着你做人没趣味 别流泪 心酸 更不应舍弃 ... 主题: Spring源码阅读还在继 ...
- 初始化IoC容器(Spring源码阅读)
初始化IoC容器(Spring源码阅读) 我们到底能走多远系列(31) 扯淡: 有个问题一直想问:各位你们的工资剩下来会怎么处理?已婚的,我知道工资永远都是不够的.未婚的你们,你们是怎么分配工资的? ...
- Spring源码阅读-ApplicationContext体系结构分析
目录 继承层次图概览 ConfigurableApplicationContext分析 AbstractApplicationContext GenericApplicationContext Gen ...
- Sping学习笔记(一)----Spring源码阅读环境的搭建
idea搭建spring源码阅读环境 安装gradle Github下载Spring源码 新建学习spring源码的项目 idea搭建spring源码阅读环境 安装gradle 在官网中下载gradl ...
- Spring源码阅读 之 配置的读取,解析
在上文中我们已经知道了Spring如何从我们给定的位置加载到配置文件,并将文件包装成一个Resource对象.这篇文章我们将要探讨的就是,如何从这个Resouce对象中加载到我们的容器?加载到容器后又 ...
- [源码阅读] 阿里SOFA服务注册中心MetaServer(1)
[源码阅读] 阿里SOFA服务注册中心MetaServer(1) 目录 [源码阅读] 阿里SOFA服务注册中心MetaServer(1) 0x00 摘要 0x01 服务注册中心 1.1 服务注册中心简 ...
- [源码阅读] 阿里SOFA服务注册中心MetaServer(3)
[源码阅读] 阿里SOFA服务注册中心MetaServer(3) 目录 [源码阅读] 阿里SOFA服务注册中心MetaServer(3) 0x00 摘要 0x01 概念 1.1 分布式一致性 1.2 ...
- Spring 源码分析之 bean 依赖注入原理(注入属性)
最近在研究Spring bean 生命周期相关知识点以及源码,所以打算写一篇 Spring bean生命周期相关的文章,但是整理过程中发现涉及的点太多而且又很复杂,很难在一篇文章中把Spri ...
- Spring源码阅读笔记02:IOC基本概念
上篇文章中我们介绍了准备Spring源码阅读环境的两种姿势,接下来,我们就要开始探寻这个著名框架背后的原理.Spring提供的最基本最底层的功能是bean容器,这其实是对IoC思想的应用,在学习Spr ...
随机推荐
- 006-循环结构(下)-C语言笔记
006-循环结构(下)-C语言笔记 学习目标 1.[掌握]do-while循环结构 2.[掌握]for循环结构 3.[掌握]嵌套循环 一.do-while循环结构 do-while语法: 1 2 ...
- Apache SkyWalking
Apache SkyWalking 什么是 SkyWalking SkyWalking 是观察性分析平台和应用性能管理系统. 提供分布式追踪.服务网格遥测分析.度量聚合和可视化一体化解决方案. 支持J ...
- Python-气象-大气科学-可视化绘图系列(二)——利用basemap叠加地图,并添加白化效果(代码+示例)
本文为原创链接: https:////www.cnblogs.com/zhanling/p/12193031.html 白化单图代码: import numpy as np import xarray ...
- 用python从0到1制作动态条形图的过程
大家好,今天我们要讲的是如何使用Pyecharts制作动态排名变化图
- Daily Scrum 1/11/2016
Zhaoyang & Minlong: Took and edited the video which introduced our APP. Yandong: Summarized bugs ...
- 带权值的图 BFS
用bfs遍历最图求最短路径时通常借用优先队列即优先考虑最大的或者最小的权值 方法1 优先队列:(内置函数,优先考虑较小的权值) #include<iostream> #include< ...
- [YII2] 增删改查2
一.新增 使用model::save()操作进行新增数据 $user= new User; $user->username =$username; $user->password =$pa ...
- 详细的JavaScript知识梳理和经典的一百个例题,让你掌握JavaScript
这里先做一下JavaScript知识点的梳理,具体的可领取资料 JavaScript语法: js语法.png DOM操作: DOM操作.png 数据类型 面向对象 继承 闭包 插件 作用域 跨域 原型 ...
- JS - Promise使用详解
参考:https://www.cnblogs.com/developer-ios/p/10510564.html
- thinkphp5.0 url跳转
<a href="{:url('member/index/index',['id'=>5])}">跳转</a> define()自定义常量在thiin ...