Spring 获取单例流程(一)
读完这篇文章你将会收获到
- 在
getBean
方法中,Spring
处理别名以及factoryBean
的name
Spring
如何从多级缓存中根据beanName
获取bean
Spring
如何处理用户获取普通bean
和factoryBean
引言
从 Spring 容器的初始化 中,我们了解到 Spring
是如何将 XML
文件转换为 BeanDefinition
并注册到 BeanDefinitionRegstry
。
今天我们一起继续学习 Spring
的 bean
加载
public static void main(String[] args) {
Resource resource = new ClassPathResource("coderLi.xml");
DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(defaultListableBeanFactory);
xmlBeanDefinitionReader.loadBeanDefinitions(resource);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "https://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
<bean class="com.demo.data.Person">
<description>
微信搜一搜:CoderLi
</description>
</bean>
</beans>
熟悉的味道、熟悉的配方
源码分析
我们可以在上面的 Java 代码中加入以下的代码
System.out.println(defaultListableBeanFactory.getBean("com.demo.data.Person#0"));
我们根据默认的 beanName 从 Spring 容器中获取 Person 这个 bean 对象,当然我们可以使用默认的别名获取
System.out.println(defaultListableBeanFactory.getBean("com.demo.data.Person"));
对 Spring 别名不熟悉的朋友可以先看下我的这一篇文章 Spring-AliasRegistry
我们直接进入到 AbstractBeanFactory#getBean(String)
方法中, AbstractBeanFactory
为 DefaultListableBeanFactory
的父类
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
可以看到 do
开头的才是真正干活的老大 , AbstractBeanFactory#doGetBean
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// 找到这个参数的 bean name
final String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
// 检查缓冲中是否有这个bean、spring中只是保存单例的bean
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
// 这里被我删除了一些spring 的log
// 处理一下 factory bean 的情况、包括从 factory beans 的缓存中获取、或者重新调用 factory bean 的 get bean 方法 包括一些回调
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
..........
...........
因为这个方法实在太长了所以截取一部分、我们一步步来分析
transformedBeanName(name)
这个方法就是将我们的 name 转换为真正的 beanName,因为我们传进来的参数可能是一个 alias 或者可能是一个 factoryBean 的 beanName (前缀为&),而我们在 Spring 中存放的 factoryBean 的 beanName 是没有 & 前缀的,所以需要处理掉这个前缀
protected String transformedBeanName(String name) {
return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}
public static String transformedBeanName(String name) {
Assert.notNull(name, "'name' must not be null");
// 是否是一个 factory bean 、如果不是的话就直接返回
if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
return name;
}
// 如果是的话就将其前缀 & 去掉
return transformedBeanNameCache.computeIfAbsent(name, beanName -> {
do {
beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
}
while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
return beanName;
});
}
/**
* 找到这个别名的最终的 bean Name、如果没有的话(
* 也就是说参数中的name 就是人家的 bean name 那么就直接返回这个 参数就行了)
*
*/
public String canonicalName(String name) {
String canonicalName = name;
// Handle aliasing...
String resolvedName;
do {
resolvedName = this.aliasMap.get(canonicalName);
if (resolvedName != null) {
canonicalName = resolvedName;
}
}
while (resolvedName != null);
return canonicalName;
}
让我们再看看下一个方法 DefaultSingletonBeanRegistry#getSingleton(String)
public Object getSingleton(String beanName) {
// allowEarlyReference 允许早期依赖
return getSingleton(beanName, true);
}
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
// 这个bean 正处于 创建阶段
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 并发控制
synchronized (this.singletonObjects) {
// 单例缓存是否存在
singletonObject = this.earlySingletonObjects.get(beanName);
// 是否运行获取 bean factory 创建出的 bean
if (singletonObject == null && allowEarlyReference) {
// 获取缓存中的 ObjectFactory
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
// 将对象缓存到 earlySingletonObject中
this.earlySingletonObjects.put(beanName, singletonObject);
// 从工厂缓冲中移除
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
上面的代码就是 Spring 尝试从缓存中加载单例。单例在 Spring 的同一个容器中只会被创建一次,后续再获取 bean,就直接从缓存中取了。
在介绍这个方法之前、我们先认识下 DefaultSingletonBeanRegistry
这个类里面的成员变量吧
Map<String, Object> singletonObjects
这个很好理解、 key 就是 beanName ,value 就是 bean 实例Map<String, ObjectFactory<?>> singletonFactories
key 为 beanName,value 为创建 bean 的工厂Map<String, Object> earlySingletonObjects
key 为 beanName ,value 为 bean。但是和singletonObjects
不同的是,bean 被加入到earlySingletonObjects
的时候、这个 bean 还是处于一种创建中的状态,目的也很简单、Spring 用来解决某些场景下的循环依赖
我们再回到代码中、分析一下它的逻辑
- 先从 singletonObjects 中尝试获取 bean,这里存放的是已经创建好的 bean 了、如果在这里能知道、那当然是最好啦
- 如果在这里找不到的话、那么我们就要判断下这个 beanName 对应的 bean 是否正在创建中
- 如果是的话,那么我们再看看这个正在创建的 bean 是否已经曝光出来、如果没有的话、那么就要看看我们的参数是否允许依赖早期的 bean 了、
- 如果允许早期依赖、那么我们就尝试冲 ObjectFactory 中获取到对应的 bean、并将它放入到 earlySingletonObjects 中、并从 singletonFactories 中移除
类似多级缓存的设计
在上面的方法中我们看到 isSingletonCurrentlyInCreation(beanName)
这个方法、
public boolean isSingletonCurrentlyInCreation(String beanName) {
return this.singletonsCurrentlyInCreation.contains(beanName);
}
singletonsCurrentlyInCreation
这个 Set 中,当创建一个 bean 之前会将其 对应的 beanName 放置到这个 Set 中、后面的分析会涉及到、这里先提一嘴
我们第一次获取这个 bean 、返回为 null 是正常的
那假如我们在代码中 getBean 了两次
defaultListableBeanFactory.getBean("com.demo.data.Person#0")
defaultListableBeanFactory.getBean("com.demo.data.Person#0")
那么针对第二次的调用、返回的值就不是为 null 了
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
// 处理一下 factory bean 的情况、包括从 factory beans 的缓存中获取、或者重新调用 factory bean 的 get bean 方法 包括一些回调
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
我们先假设 sharedInstance 不为 null 也就是我们第二次调用 getBean
,我们进入到 getObjectForBeanInstance
方法中
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
// 我王大锤就是想要一个 factory bean
if (BeanFactoryUtils.isFactoryDereference(name)) {
// 如果这个是 NullBean 类型、表示这是一个 null 的 instance、直接返回
if (beanInstance instanceof NullBean) {
return beanInstance;
}
// 获取到的 beanInstance 不是一个 factory、但是你tm name 又带有这个 & 很迷惑啊兄弟
if (!(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
}
if (mbd != null) {
mbd.isFactoryBean = true;
}
return beanInstance;
}
// 王大锤不想要factory bean、并且spring 也帮他找到了一个普通的 bean、直接返回
if (!(beanInstance instanceof FactoryBean)) {
return beanInstance;
}
// 王大锤要的是一个普通的bean 、但是spring 给他找到了一个 factory的bean、那么spring 是不是要做一些额外的处理 给王大锤返回一个普通的bean
Object object = null;
if (mbd != null) {
mbd.isFactoryBean = true;
} else {
// 从缓存中 看看有没有
object = getCachedObjectForFactoryBean(beanName);
}
// 如果 bean factory 中还是没有
if (object == null) {
// Return bean instance from factory.
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
// 从 缓存中拿到 bean definition
if (mbd == null && containsBeanDefinition(beanName)) {
mbd = getMergedLocalBeanDefinition(beanName);
}
// 是否是用户定义的还是程序本身需要创建的bean
boolean synthetic = (mbd != null && mbd.isSynthetic());
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}
我们按步骤分析下上面的代码
- 我们调用
getBean(name)
中的 name 如果包含前缀 & ,表面我们是想要从 Spring 中获取一个 FactoryBean ,那么我们就要判断我们从缓存中获取的 beanInstance 是否是 一个 FactoryBean 、如果是的话就直接返回不是的话就要抛出异常了 - 我们想要的是一个非 factoryBean 并且 在 spring 容器中找到了非 factoryBean 的 bean、那么就直接返回
- 我们想要的是一个 非 factoryBean 但是在 spring 容器中找到了一个 factoryBean 的 bean、那么就要进入到
getObjectFromFactoryBean
方法中了
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
// 为单例模式且缓存中存在
if (factory.isSingleton() && containsSingleton(beanName)) {
synchronized (getSingletonMutex()) {
// 从缓存中获取指定的 bean(这个bean 是从 factory bean 创建出来的)
Object object = this.factoryBeanObjectCache.get(beanName);
if (object == null) {
// 为空则从 factory bean 中获取对象
object = doGetObjectFromFactoryBean(factory, beanName);
// 从缓存中获取
Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
if (alreadyThere != null) {
// 已经存放到 缓存中了、后续的操作就不需要了
object = alreadyThere;
} else {
// 需要做一些后置处理
if (shouldPostProcess) {
// 如果这个bean正在创建中、
if (isSingletonCurrentlyInCreation(beanName)) {
return object;
}
// 前置处理 主要是将这个bean 加入到正在创建中的队列 singletonsCurrentlyInCreation
beforeSingletonCreation(beanName);
try {
// 对 从 factoryBean 获取的对象进行后处理
// 生成对象将暴露给 bean 引用 并回调 beanPostProcessor
object = postProcessObjectFromFactoryBean(object, beanName);
} catch (Throwable ex) {
throw new BeanCreationException(beanName,
"Post-processing of FactoryBean's singleton object failed", ex);
} finally {
// 后置处理 将其从 singletonsCurrentlyInCreation 移除
afterSingletonCreation(beanName);
}
}
// 他的 factory bean 已经存在 缓存中了、那么这个 factory bean 产生的bean 应该也要缓存一下
if (containsSingleton(beanName)) {
this.factoryBeanObjectCache.put(beanName, object);
}
}
}
return object;
}
} else {
// 非单例
Object object = doGetObjectFromFactoryBean(factory, beanName);
if (shouldPostProcess) {
try {
//
object = postProcessObjectFromFactoryBean(object, beanName);
} catch (Throwable ex) {
throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
}
}
return object;
}
}
啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊、代码很长长长..........我们一点点来分析
第一步就是判断这个 factoryBean 是否是单例、如果不是的话,并且是用户自己定义的 bean、那么就需要调用
postProcessObjectFromFactoryBean
方法去做一个后续的处理- 这里面最终回调的就是我们常用的一个接口
BeanPostProcessor
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException { Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
- 这里面最终回调的就是我们常用的一个接口
如果这 beanFactory 是一个单例,那我们就看看 factoryBeanObjectCache ( key 是 beanName,value 是 beanFactory 产生出来的 object 也是我们正要获取的 bean ) 这个 Map 中是否存在这个 beanName 这个 bean
如果存在的话、就直接返回、如果不存在的话、那就
doGetObjectFromFactoryBean
,从这个方法中使用 FactoryBean#getObject 产生 bean其实下面这段代码确实让人看不懂哦
Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
if (alreadyThere != null) {
// 已经存放到 缓存中了、后续的操作就不需要了
object = alreadyThere;
}
然后我们看到
beforeSingletonCreation
这个方法、就是上面getSingleton
中isSingletonCurrentlyInCreation
判断一个 bean 是否处于正在创建中protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
public boolean isSingletonCurrentlyInCreation(String beanName) {
return this.singletonsCurrentlyInCreation.contains(beanName);
}
然后又调用到
postProcessObjectFromFactoryBean
方法、最终回调的就是我们常用的一个接口BeanPostProcessor
最好调用
afterSingletonCreation(beanName)
方法、将其从 正在创建中的 bean 的集合中移除、最后的最后、将其加入到factoryBeanObjectCache
集合中
今天我们就先分析到这里、后续的话我们在后面的文章继续探讨。今天我们大致分析了 getBean 里面的这三个方法
总结
- 根据参数中的 name 找出对应的 beanName、无论这个 name 是别名或者是一个 factoryBean 的 beanName
- 查看缓存中是否包含这个 beanName 对象
- 先从一级缓存
singletonObjects
中看看有没有 - 然后从二级缓存
earlySingletonObjects
- 都没有的话再从三级缓存
singletonFactories
中看看有没有
- 先从一级缓存
- 如果缓存中有 bean、那么我们还是需要处理一下这个 bean
- 如果 Spring 缓存中返回的 bean 是 factoryBean、而用户也想要的是一个 beanFactory (参数 name 中的前缀是
&
)、那么我们直接返回 - 如果 Spring 缓存中返回的 bean 是普通的 bean、而用户也想要的是一个普通的 bean 、那么就直接返回
- 如果 Spring 缓存中返回的 bean 是一个 factoryBean、而用户想要的是一个普通的 bean 、那么我们就要从 factoryBean 中获取这个 bean
- 而从 factoryBean 中获取这个 bean的过程中、需要调用到前置处理、后置处理和我们常用的接口回调
BeanPostProcessor
- 如果 Spring 缓存中返回的 bean 是 factoryBean、而用户也想要的是一个 beanFactory (参数 name 中的前缀是
上面的三个方法大致流程就是这样、希望对各位有帮助
有兴趣进入群聊、一起交流一起划水
Spring 获取单例流程(一)的更多相关文章
- Spring 获取单例流程(三)
读完这篇文章你将会收获到 Spring 何时将 bean 加入到第三级缓存和第一级缓存中 Spring 何时回调各种 Aware 接口.BeanPostProcessor .InitializingB ...
- Spring 获取单例流程(二)
读完这篇文章你将会收获到 Spring 中 prototype 类型的 bean 如何做循环依赖检测 Spring 中 singleton 类型的 bean 如何做循环依赖检测 前言 继上一篇文章 S ...
- Spring IOC 容器源码分析 - 获取单例 bean
1. 简介 为了写 Spring IOC 容器源码分析系列的文章,我特地写了一篇 Spring IOC 容器的导读文章.在导读一文中,我介绍了 Spring 的一些特性以及阅读 Spring 源码的一 ...
- Spring源码分析(十五)获取单例
本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 之前我们讲解了从缓存中获取单例的过程,那么,如果缓存中不存在已经加载的单例be ...
- 5.2:缓存中获取单例bean
5.2 缓存中获取单例bean 介绍过FactoryBean的用法后,我们就可以了解bean加载的过程了.前面已经提到过,单例在Spring的同一个容器内只会被创建一次,后续再获取bean直接从单例 ...
- Spring源码分析(十三)缓存中获取单例bean
摘要:本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 介绍过FactoryBean的用法后,我们就可以了解bean加载的过程了 ...
- 【转】Spring Bean单例与线程安全
一.Spring单例模式及线程安全 Spring框架中的Bean,或者说组件,获取实例的时候都是默认单例模式,这是在多线程开发的时候需要尤其注意的地方. 单例模式的意思是只有一个实例,例如在Sprin ...
- Spring Bean单例与线程安全
一.Spring单例模式及线程安全 Spring框架中的Bean,或者说组件,获取实例的时候都是默认单例模式,这是在多线程开发的时候需要尤其注意的地方. 单例模式的意思是只有一个实例,例如在Sprin ...
- Spring的单例实现原理-登记式单例
单例模式有饿汉模式.懒汉模式.静态内部类.枚举等方式实现,但由于以上模式的构造方法是私有的,不可继承,Spring为实现单例类可继承,使用的是单例注册表的方式(登记式单例). 什么是单例注册表呢, 登 ...
随机推荐
- Java实现 蓝桥杯VIP 算法提高 士兵排队问题
算法提高 士兵排队问题 时间限制:1.0s 内存限制:256.0MB 试题 有N个士兵(1≤N≤26),编号依次为A,B,C,-,队列训练时,指挥官要把一些士兵从高到矮一次排成一行,但现在指挥官不能直 ...
- 第六届蓝桥杯JavaC组国(决)赛真题
解题代码部分来自网友,如果有不对的地方,欢迎各位大佬评论 题目1.机器人数目 少年宫新近邮购了小机器人配件,共有3类,其中, A类含有:8个轮子,1个传感器 B类含有: 6个轮子,3个传感器 C类含有 ...
- Java实现 蓝桥杯 算法训练 Multithreading
问题描述 现有如下一个算法: repeat ni times yi := y y := yi+1 end repeat 令n[1]为你需要算加法的第一个数字,n[2]为第二个,-n[N]为第N个数字( ...
- Spring基本介绍
一:Spring是什么 Spring是分层的 Java SE/EE应用 full-stack 轻量级开源框架,它以IOC控制反转和AOP面向切面编程为核心,提供了展现层 Spring MVC 和持久层 ...
- STL常用序列容器
这里简要的记述一下STL常用容器的实现原理,要点等内容. vector vector是比较常用的stl容器,用法与数组是非类似,其内部实现是连续空间分配,与数组的不同之处在于可弹性增加空间,而arra ...
- Java基础(九)
一.总述 多线程程序在较低的层次上扩展了多任务的概念:一个程序同时执行多个任务.通常,每一个任务称为一个线程,它是线程控制的简称.可以同时运行一个以上线程的程序称为多线程程序. 多进程与多线程的区别: ...
- 曹工说JDK源码(3)--ConcurrentHashMap,Hash算法优化、位运算揭秘
hashcode,有点讲究 什么是好的hashcode,一般来说,一个hashcode,一般用int来表示,32位. 下面两个hashcode,大家觉得怎么样? 0111 1111 1111 1111 ...
- 【asp.net core 系列】8 实战之 利用 EF Core 完成数据操作层的实现
0. 前言 通过前两篇,我们创建了一个项目,并规定了一个基本的数据层访问接口.这一篇,我们将以EF Core为例演示一下数据层访问接口如何实现,以及实现中需要注意的地方. 1. 添加EF Core 先 ...
- php的ts和nts选择
TS(Thread-Safety)即线程安全,多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用.不会出现数据不一致或者数据 ...
- selenium获取图片验证码
# encoding:utf-8 from PIL import Image from selenium import webdriver url = '网站地址' driver = webdrive ...