目录
调用getSingleton(beanName, singletonFactory)
注意:本文参考
Spring循环依赖
什么是循环依赖?
从字面上来理解就是A依赖B的同时B也依赖了A,就像下面这样
体现到代码层次就是这个样子
@Component
public class A {
// A中注入了B
@Autowired
private B b;
}
@Component
public class B {
// B中也注入了A
@Autowired
private A a;
}
当然,这是最常见的一种循环依赖,比较特殊的还有
// 自己依赖自己
@Component
public class A {
// A中注入了A
@Autowired
private A a;
}
虽然体现形式不一样,但是实际上都是同一个问题----->循环依赖
什么情况下循环依赖可以被处理?
在回答这个问题之前首先要明确一点,Spring解决循环依赖是有前置条件的
1 出现循环依赖的Bean必须要是单例
2 依赖注入的方式不能全是构造器注入的方式(很多博客上说,只能解决setter方法的循环依赖,这是错误的)
其中第一点应该很好理解,第二点:不能全是构造器注入是什么意思呢?我们还是用代码说话
@Component
public class A {
// @Autowired
// private B b;
public A(B b) {
}
}
@Component
public class B {
// @Autowired
// private A a;
public B(A a){
}
}
在上面的例子中,A中注入B的方式是通过构造器,B中注入A的方式也是通过构造器,这个时候循环依赖是无法被解决,如果你的项目中有两个这样相互依赖的Bean,在启动时就会报出以下错误:
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
为了测试循环依赖的解决情况跟注入方式的关系,我们做如下四种情况的测试
依赖情况 | 依赖注入方式 | 循环依赖是否被解决 |
---|---|---|
AB相互依赖(循环依赖) | 均采用setter方法注入 | 是 |
AB相互依赖(循环依赖) | 均采用构造器注入 | 否 |
AB相互依赖(循环依赖) | A中注入B的方式为setter方法,B中注入A的方式为构造器 | 是 |
AB相互依赖(循环依赖) | B中注入A的方式为setter方法,A中注入B的方式为构造器 | 否 |
第四种参考
这种情况,Spring会根据自然排序创建bean,A会先于B进行创建
不是只有在setter方法注入的情况下循环依赖才能被解决,即使存在构造器注入的场景下,循环依赖依然被可以被正常处理掉。
那么到底是为什么呢?Spring到底是怎么处理的循环依赖呢?不要急,我们接着往下看
基础介绍与循环依赖的本质
首先,Spring内部维护了三个Map,也就是我们通常说的三级缓存。
翻阅Spring文档倒是没有找到三级缓存的概念,可能也是本土为了方便理解的词汇。
在Spring的DefaultSingletonBeanRegistry
类中,你会赫然发现类上方挂着这三个Map:
singletonObjects 它是我们最熟悉的朋友,俗称“单例池”“容器”,缓存创建完成单例Bean的地方。
singletonFactories 映射创建Bean的原始工厂
earlySingletonObjects 映射Bean的早期引用,也就是说在这个Map里的Bean不是完整的,甚至还不能称之为“Bean”,只是一个Instance.
后两个Map其实是“垫脚石”级别的,只是创建Bean的时候,用来借助了一下,创建完成就清掉了。
所以前文对“三级缓存”这个词有些迷惑,可能是因为注释都是以Cache of开头吧。
为什么成为后两个Map为垫脚石,假设最终放在singletonObjects的Bean是你想要的一杯“凉白开”。
那么Spring准备了两个杯子,即singletonFactories和earlySingletonObjects来回“倒腾”几番,把热水晾成“凉白开”放到singletonObjects中。
闲话不说,都浓缩在图里。
了解完Spring如何处理循环依赖之后,让我们跳出“阅读源码”的思维,假设让你实现一个有以下特点的功能,你会怎么做?
将指定的一些类实例为单例
类中的字段也都实例为单例
支持循环依赖
举个例子,假设有类A:
public class A {
private B b;
}
类B:
public class B {
private A a;
}
说白了让你模仿Spring:假装A和B是被@Component修饰,
并且类中的字段假装是@Autowired修饰的,处理完放到Map中。
其实非常简单,写了一份粗糙的代码,可供参考:
/**
* 放置创建好的bean Map
*/
private static Map<String, Object> cacheMap = new HashMap<>(2);
public static void main(String[] args) {
// 假装扫描出来的对象
Class[] classes = {A.class, B.class};
// 假装项目初始化实例化所有bean
for (Class aClass : classes) {
getBean(aClass);
}
// check
System.out.println(getBean(B.class).getA() == getBean(A.class));
System.out.println(getBean(A.class).getB() == getBean(B.class));
}
@SneakyThrows
private static <T> T getBean(Class<T> beanClass) {
// 本文用类名小写 简单代替bean的命名规则
String beanName = beanClass.getSimpleName().toLowerCase();
// 如果已经是一个bean,则直接返回
if (cacheMap.containsKey(beanName)) {
return (T) cacheMap.get(beanName);
}
// 将对象本身实例化
Object object = beanClass.getDeclaredConstructor().newInstance();
// 放入缓存
cacheMap.put(beanName, object);
// 把所有字段当成需要注入的bean,创建并注入到当前bean中
Field[] fields = object.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
// 获取需要注入字段的class
Class<?> fieldClass = field.getType();
String fieldBeanName = fieldClass.getSimpleName().toLowerCase();
// 如果需要注入的bean,已经在缓存Map中,那么把缓存Map中的值注入到该field即可
// 如果缓存没有 继续创建
field.set(object, cacheMap.containsKey(fieldBeanName)
? cacheMap.get(fieldBeanName) : getBean(fieldClass));
}
// 属性填充完成,返回
return (T) object;
}
这段代码的效果,其实就是处理了循环依赖,并且处理完成后,cacheMap中放的就是完整的“Bean”了
这就是“循环依赖”的本质,而不是“Spring如何解决循环依赖”。
之所以要举这个例子,是发现一小部分盆友陷入了“阅读源码的泥潭”,而忘记了问题的本质。
为了看源码而看源码,结果一直看不懂,却忘了本质是什么。
如果真看不懂,不如先写出基础版本,逆推Spring为什么要这么实现,可能效果会更好。
what?问题的本质居然是two sum!
看完刚才的代码有没有似曾相识?没错,和two sum的解题是类似的。
不知道two sum是什么梗的,和你介绍一下:
two sum是刷题网站leetcode序号为1的题,也就是大多人的算法入门的第一题。
常常被人调侃,有算法面的公司,被面试官钦定了,合的来。那就来一道two sum走走过场。
问题内容是:给定一个数组,给定一个数字。返回数组中可以相加得到指定数字的两个索引。
比如:给定nums = [2, 7, 11, 15], target = 9
那么要返回 [0, 1]
,因为2 + 7 = 9
这道题的优解是,一次遍历+HashMap:
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];
if (map.containsKey(complement)) {
return new int[] { map.get(complement), i };
}
map.put(nums[i], i);
}
throw new IllegalArgumentException("No two sum solution");
}
}
先去Map中找需要的数字,没有就将当前的数字保存在Map中,如果找到需要的数字,则一起返回。
和上面的代码是不是一样?
先去缓存里找Bean,没有则实例化当前的Bean放到Map,如果有需要依赖当前Bean的,就能从Map取到。
如何到getSingleton方法
SpringIOC中的有个关键方法refresh(),这里面包含了13个核心的子方法,不了解的同学可以去复习一下前面讲的SpringIOC启动过程。
在13个子方法中有一个finishBeanFactoryInitialization(beanFactory) ;初始化剩下的单实例(非懒加载的)方法。这个就是开始入口了
public void refresh() throws BeansException, IllegalStateException {
// 添加一个synchronized 防止出现refresh还没有完成出现其他的操作(启动,或者销毁)
synchronized (this.startupShutdownMonitor) {
// 1.准备工作
// 记录下容器的启动时间、
// 标记“已启动”状态,关闭状态为false、
// 加载当前系统属性到环境对象中
// 准备一系列监听器以及事件集合对象
prepareRefresh();
// 2. 创建容器对象:DefaultListableBeanFactory,加载XML配置文件的属性到当前的工厂中(默认用命名空间来解析),就是上面说的BeanDefinition(bean的定义信息)这里还没有初始化,只是配置信息都提取出来了,(包含里面的value值其实都只是占位符)
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 3. BeanFactory的准备工作,设置BeanFactory的类加载器,添加几个BeanPostProcessor,手动注册几个特殊的bean等
prepareBeanFactory(beanFactory);
try {
// 4.子类的覆盖方法做额外的处理,就是我们刚开始说的 BeanFactoryPostProcessor ,具体的子类可以在这步的时候添加一些特殊的BeanFactoryPostProcessor完成对beanFactory修改或者扩展。
// 到这里的时候,所有的Bean都加载、注册完成了,但是都还没有初始化
postProcessBeanFactory(beanFactory);
// 5.调用 BeanFactoryPostProcessor 各个实现类的 postProcessBeanFactory(factory) 方法
invokeBeanFactoryPostProcessors(beanFactory);
// 6.注册 BeanPostProcessor 处理器 这里只是注册功能,真正的调用的是getBean方法
registerBeanPostProcessors(beanFactory);
// 7.初始化当前 ApplicationContext 的 MessageSource,即国际化处理
initMessageSource();
// 8.初始化当前 ApplicationContext 的事件广播器,
initApplicationEventMulticaster();
// 9.从方法名就可以知道,典型的模板方法(钩子方法),感兴趣的同学还可以再去复习一下之前写的设计模式中的-模版方法模式
// 具体的子类可以在这里初始化一些特殊的Bean(在初始化 singleton beans 之前)
onRefresh();
// 10.注册事件监听器,监听器需要实现 ApplicationListener 接口。这也不是我们的重点,过
registerListeners();
// 11.初始化所有的 singleton beans(lazy-init 的除外),重点关注
finishBeanFactoryInitialization(beanFactory);
// 12.广播事件,ApplicationContext 初始化完成
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// 13.销毁已经初始化的 singleton 的 Beans,以免有些 bean 会一直占用资源
destroyBeans();
cancelRefresh(ex);
// 把异常往外抛
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
1.因为IOC作为Spring的容器,且默认的都是单例的,所以在我们创建bean之前都会去getBean一把,判断当前是否有,当没有时才会去创建。
所以进入finishBeanFactoryInitialization方法中找到 beanFactory.preInstantiateSingletons();
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// 省略其他干扰代码(判断逻辑)。。。
// Instantiate all remaining (non-lazy-init) singletons.
// 实例化剩下的所有的单例对象(非懒加载的)
beanFactory.preInstantiateSingletons();
}
进入到 preInstantiateSingletons 方法中,可以看到通过beanDefinitionNames(bean的定义信息)来判断当前需要创建的bean信息,所以开始通过beanName循环开始走创建流程。
因为是我们创建的普通的bean实例,所以肯定会走到最下面的getBean(beanName);方法中,如下代码所示
@Override
public void preInstantiateSingletons() throws BeansException {
if (logger.isTraceEnabled()) {
logger.trace("Pre-instantiating singletons in " + this);
}
// Iterate over a copy to allow for init methods which in turn register new bean definitions.
// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// Trigger initialization of all non-lazy singleton beans...
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
// 判断是否是工厂bean
if (isFactoryBean(beanName)) {
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
final FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
((SmartFactoryBean<?>) factory)::isEagerInit,
getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
}
else {
// 如果当前beanName对应的bean不是工厂bean,则通过beanName来获取bean的实例
getBean(beanName);
}
}
}
}
进入到这个getBean(beanName);方法中有一个doGetBean方法,在Spring源码中真正开始干活做事情的都一定会打上do的前缀方法。
@Override
public Object getBean(String name) throws BeansException {
// 实际获取bean的方法,触发依赖注入方法
return doGetBean(name, null, null, false);
}
所以在进到doGetBean的方法中,还是会默认先去获取一把,没有则开始创建进入createBean(beanName, mbd, args)方法
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
final String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
// 确认一下容器中是否已经有了当前bean实例
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isTraceEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
}
}
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
// 省略其他逻辑代码。。。
// Create bean instance.
// 创建Bean的实例对象
if (mbd.isSingleton()) {
// 返回以beanName的单例对象,如果没有注册,则使用singletonFactory创建并且注册一个。
sharedInstance = getSingleton(beanName, () -> {
try {
// 为给定的BeanDefinition(和参数)创建一个Bean的实例 重点
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// 省略其他逻辑代码。。。
return (T) bean;
}
在上面没有获取到bean时候则开始创建bean了,所以直接进到createBean的方法中,因为是容器初始化启动所以肯定是没有的,顾一定会进入createBean的方法中,所以再进入createBean的方法中。
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// 省略其他相关代码。。。。。
try {
// 实际创建Bean的调用 重点
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
// A previously detected exception with proper bean creation context already,
// or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(
mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
}
}
看到doCreateBean方法那说明要开始真正的创建Bean了。
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
// 判断如果是单例对象,则从factoryBean实例缓存汇总移除当前Bean的定义信息
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// 根据执行的bean使用的对应的策略创建新的实例。也可以理解实例化对象,在内存总开辟空间
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// 省略其他的相关代码。。。。。
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
// 判断当前bean是否需要提前曝光,单例&允许循环依赖&当前bean正在创建,检测循环依赖
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 在bean的初始化完成之前将创建的实例加入ObjectFactory(添加三级缓存),主要是为了防止后期的循环依赖。。。。重点
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
Object exposedObject = bean;
try {
// 填充bean属性,假设其中存在依赖于其他的bean的属性,则会递归初始化依赖的bean
populateBean(beanName, mbd, instanceWrapper);
//执行初始化逻辑
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
return exposedObject;
}
进入到doCreateBean中首先需要核心看的一个方法createBeanInstance,这个方法就是真正的创建bean实例例,也就是在内存中开辟空间(实例化),完事之后就开始看第二个重点添加缓存
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
这个方法点进去,其实就是能发现开始添加到第三级缓存中,value值就是一个函数方法getEarlyBeanReference,不熟悉的同学可以看下JDK1.8的新特性。同时也标注了当前bean正在注册中。
实例化完bean按照bean的生命周期流程那肯定就是开始初始化bean了,填充属性,接着向下看有一个populateBean(填充bean属性)
populateBean(beanName, mbd, instanceWrapper);
在populateBean这个过程中就有很大的逻辑在里面了,比如说获取属性名称,属性值等等一系列操作。但是核心的还是需要看applyPropertyValues方法属性赋值,如下所示:
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
// 省略一堆其他判断校验逻辑代码,直接看到最后。。。
if (pvs != null) {
// 应用给定的属性值,解决任何在这个bean工厂运行时其他的bean的调用(就是设置属性值)
applyPropertyValues(beanName, mbd, bw, pvs);
}
}
同样的进入applyPropertyValues方法。
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
// 省略其他的一些校验代码。。。。。
// Create a deep copy, resolving any references for values.
List<PropertyValue> deepCopy = new ArrayList<>(original.size());
boolean resolveNecessary = false;
// 便利属性,将属性转换为对应类的对应属性类型
for (PropertyValue pv : original) {
// 判断当前属性是否已经解析过
if (pv.isConverted()) {
deepCopy.add(pv);
}
else {
// 获取属性明层
String propertyName = pv.getName();
// 获取属性值
Object originalValue = pv.getValue();
// valueResolver处理pv解析出的originalValue封装的对象(是否必要开始去处理属性值了)重点
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
// 默认转换后的值等于解析出来的值
Object convertedValue = resolvedValue;
// 判断转换标记
boolean convertible = bw.isWritableProperty(propertyName) &&
!PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
if (convertible) {
convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
}
// 省略其他的代码逻辑。。。。
}
applyPropertyValues方法中需要注意的是valueResolver.resolveValueIfNecessary值处理器
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
这一步主要是判断属性值是否需要处理,因为之前这个value值是存方法接口方法
所以在执行valueResolver.resolveValueIfNecessary方法时,一定会去处理,那再看看里面又处理什么逻辑?
public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {
// We must check each value to see whether it requires a runtime reference
// to another bean to be resolved.
// 如果value是RuntimeBeanReference实例 则处理
if (value instanceof RuntimeBeanReference) {
RuntimeBeanReference ref = (RuntimeBeanReference) value;
// 解析出对应的ref 所封装的Bean元信息(Bean的名称,Bean的类型) 的对象
return resolveReference(argName, ref);
}
// 省略其他的逻辑代码
}
面的断点的截图已经可以明确的看到value值是RuntimeBeanReference实例,所以接下来就一定会去调用resolveReference方法解析ref所封装的bean信息,那就再接着进入resolveReference方法看看干了什么?
@Nullable
private Object resolveReference(Object argName, RuntimeBeanReference ref) {
try {
Object bean;
String refName = ref.getBeanName();
refName = String.valueOf(doEvaluate(refName));
if (ref.isToParent()) {
if (this.beanFactory.getParentBeanFactory() == null) {
throw new BeanCreationException(
this.beanDefinition.getResourceDescription(), this.beanName,
"Can't resolve reference to bean '" + refName +
"' in parent factory: no parent factory available");
}
bean = this.beanFactory.getParentBeanFactory().getBean(refName);
}
else {
// 获取resolvedName的Bean对象 重点
bean = this.beanFactory.getBean(refName);
// 注册beanName到dependentBeanName的依赖关系到Bean的工中
this.beanFactory.registerDependentBean(refName, this.beanName);
}
if (bean instanceof NullBean) {
bean = null;
}
// 返回解析出来的对用的ref所封装的Bean对象
return bean;
}
catch (BeansException ex) {
throw new BeanCreationException(
this.beanDefinition.getResourceDescription(), this.beanName,
"Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex);
}
}
上面已经进入到resolveReference来处理ref中所以引用的Bean对象,又因为SpringIOC默认都是单例Bean,所以肯定还是在beanFactory中去获取Bean
bean = this.beanFactory.getBean(refName);
至此又开始循环创建循环依赖的对象,假设还是一开始的A和B两个对象来说,那么开始是创建A对象时,在设置B属性的时候,没有B属性,那么现在刚好就是开始创建B属性了。同样的B对象又开始填充属性A。
细心的同学应发现问题了,这不就是无限循环了吗?还怎么处理循环啊?这不是扯淡吗?
其实不是的,其实创建B对象想的时候,去获取A的Bean信息时,因为A还是在创建中,所以在接下来中从新走流程中会有一个新的发现,进入缓存中获取对象,如下
bean = this.beanFactory.getBean(refName) -> doGetBean(name, null, null, false) -> sharedInstance = getSingleton(beanName) -> getSingleton(beanName, true)
// 具体点 getSingleton 方法的内部实现
// 进入getSingleton方法中 isSingletonCurrentlyInCreation 当前的Bean正在创建中
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 从一级缓存获取BeanName对应的单例对象
Object singletonObject = this.singletonObjects.get(beanName);
// 如果没有获取到,但是当前 BeanName对应的单例对象又处于创建中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 从二级缓存中获取当前BeanName对应的单例对象
singletonObject = this.earlySingletonObjects.get(beanName);
// 二级缓存中没有,但是allowEarlyReference为true,在doCreateBean方法中已经设置,所以这里为true
if (singletonObject == null && allowEarlyReference) {
// 从三级缓存中获取
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 这里就是三级缓存函数方法,同过Factory创建一个单例对象
singletonObject = singletonFactory.getObject();
// 添加到二级缓存中,半成品对象
this.earlySingletonObjects.put(beanName, singletonObject);
// 同时删除三级缓存
this.singletonFactories.remove(beanName);
}
}
}
}
// 返回当前半成品对象
return singletonObject;
}
现在整个流程中二级缓存已经存放了一个半成品A的对象,因此在创建B对象时,获取A属性填充值从容器缓存中已经可以获取到A对象的单例Bean,对B对象来说其实就是一个完整的单例Bean实例,因此再次getSingleton Bean时候会有一个判断,如果有一个新的完成的单例Bean则会添加到一级缓存中,源码如下:
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName,
"Singleton bean creation not allowed while singletons of this factory are in destruction " +
"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
}
if (logger.isDebugEnabled()) {
logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
}
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
// 省略其他的代码逻辑
//判断生成了新的单例对象
if (newSingleton) {
// 将添加BeanName和 singletonObject 添加到一级缓存中去
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
上面聊到当新的单例对象生成会再调用addSingleton方法
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
// 添加到一级缓存中
this.singletonObjects.put(beanName, singletonObject);
// 移除二级缓存中的内容
this.singletonFactories.remove(beanName);
// 移除三级缓存中的内容
this.earlySingletonObjects.remove(beanName);
// 将完成的BeanName添加到已经注册的单例集合中
this.registeredSingletons.add(beanName);
}
}
自此整个Spring的循环依赖过程就已经结束了。
还是用开始的A,B两个对象来总结一个流程吧
当开始创建A对象时,实例化后,添加一步三级缓存,针对属性赋值,因为此时还没有B对象的实例,所以在获取到A对象的B属性的值的ref引用对象B,触发创建B对象的创建,因此在B对象实例化后,在属性赋值时,获取到A属性的ref引用对象,而因为之前A对象已经完成实例化,并且添加到了三级缓存中,所以在B属性创建设置A属性时,因为此时A属性正在被创建,所以可以从第三级缓存中获取到值,同时把获取到的值添加到二级缓存中,同时删除第三级缓存的A对象。
在创建B对象中已经能获取到A属性值(半成品),所以B对象可以完成赋值状态,变成一个完整的B对象的实例。所以当新的单例对象生成会再调用addSingleton方法添加到一级缓存中,同时删除 二级 三级缓存的值,所以回过头来接着 A对象获取B属性值的时候已经能在一级缓存中获取到。所以也就可以完成属性赋值,自此循环依赖完全打开。
循环依赖问题已经跟大家聊完了,在看源码的过程中大家一定要注意以下的6个方法:
简单的循环依赖(没有AOP)
我们先来分析一个最简单的例子,就是上面提到的那个demo
@Component
public class A {
// A中注入了B
@Autowired
private B b;
}
@Component
public class B {
// B中也注入了A
@Autowired
private A a;
}
通过上文我们已经知道了这种情况下的循环依赖是能够被解决的,那么具体的流程是什么呢?我们一步步分析
首先,我们要知道Spring在创建Bean的时候默认是按照自然排序来进行创建的,所以第一步Spring会去创建A。
与此同时,我们应该知道,Spring在创建Bean的过程中分为三步
1 实例化,对应方法:AbstractAutowireCapableBeanFactory
中的createBeanInstance
方法
2 属性注入,对应方法:AbstractAutowireCapableBeanFactory
的populateBean
方法
3 初始化,对应方法:AbstractAutowireCapableBeanFactory
的initializeBean
这些方法在之前源码分析的文章中都做过详细的解读了,如果你之前没看过我的文章,那么你只需要知道
1 实例化,简单理解就是new了一个对象
2 属性注入,为实例化中new出来的对象填充属性
3 初始化,执行aware接口中的方法,初始化方法,完成AOP
代理
基于上面的知识,我们开始解读整个循环依赖处理的过程,整个流程应该是以A的创建为起点,前文也说了,第一步就是创建A嘛!
创建A的过程实际上就是调用getBean
方法,这个方法有两层含义
1 创建一个新的Bean
2 从缓存中获取到已经被创建的对象
我们现在分析的是第一层含义,因为这个时候缓存中还没有A嘛!
调用getSingleton(beanName)
首先调用getSingleton(a)
方法,这个方法又会调用getSingleton(beanName, true)
,在上图中我省略了这一步
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
getSingleton(beanName, true)
这个方法实际上就是到缓存中尝试去获取Bean,整个缓存分为三级
1 singletonObjects
,一级缓存,存储的是所有创建好了的单例Bean
2 earlySingletonObjects
,完成实例化,但是还未进行属性注入及初始化的对象
3 singletonFactories
,提前暴露的一个单例工厂,二级缓存中存储的就是从这个工厂中获取到的对象
因为A是第一次被创建,所以不管哪个缓存中必然都是没有的,因此会进入getSingleton
的另外一个重载方法getSingleton(beanName, singletonFactory)
。
调用getSingleton(beanName, singletonFactory)
这个方法就是用来创建Bean的,其源码如下:
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// ....
// 省略异常处理及日志
// ....
// 在单例对象创建前先做一个标记
// 将beanName放入到singletonsCurrentlyInCreation这个集合中
// 标志着这个单例Bean正在创建
// 如果同一个单例Bean多次被创建,这里会抛出异常
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
// 上游传入的lambda在这里会被执行,调用createBean方法创建一个Bean后返回
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
// ...
// 省略catch异常处理
// ...
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
// 创建完成后将对应的beanName从singletonsCurrentlyInCreation移除
afterSingletonCreation(beanName);
}
if (newSingleton) {
// 添加到一级缓存singletonObjects中
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
上面的代码我们主要抓住一点,通过createBean
方法返回的Bean最终被放到了一级缓存,也就是单例池中。
那么到这里我们可以得出一个结论:一级缓存中存储的是已经完全创建好了的单例Bean
调用addSingletonFactory方法
如下图所示:
在完成Bean的实例化后,属性注入之前Spring将Bean包装成一个工厂添加进了三级缓存中,对应源码如下:
// 这里传入的参数也是一个lambda表达式,() -> getEarlyBeanReference(beanName, mbd, bean)
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
// 添加到三级缓存中
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
这里只是添加了一个工厂,通过这个工厂(ObjectFactory
)的getObject
方法可以得到一个对象,而这个对象实际上就是通过getEarlyBeanReference
这个方法创建的。那么,什么时候会去调用这个工厂的getObject
方法呢?这个时候就要到创建B的流程了。
当A完成了实例化并添加进了三级缓存后,就要开始为A进行属性注入了,在注入时发现A依赖了B,那么这个时候Spring又会去getBean(b)
,然后反射调用setter方法完成属性注入。
因为B需要注入A,所以在创建B的时候,又会去调用getBean(a)
,这个时候就又回到之前的流程了,但是不同的是,之前的getBean
是为了创建Bean,而此时再调用getBean
不是为了创建了,而是要从缓存中获取,因为之前A在实例化后已经将其放入了三级缓存singletonFactories
中,所以此时getBean(a)
的流程就是这样子了
从这里我们可以看出,注入到B中的A是通过getEarlyBeanReference
方法提前暴露出去的一个对象,还不是一个完整的Bean,那么getEarlyBeanReference
到底干了啥了,我们看下它的源码
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
它实际上就是调用了后置处理器的getEarlyBeanReference
,而真正实现了这个方法的后置处理器只有一个,就是通过@EnableAspectJAutoProxy
注解导入的AnnotationAwareAspectJAutoProxyCreator
。也就是说如果在不考虑AOP
的情况下,上面的代码等价于:
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
return exposedObject;
}
也就是说这个工厂啥都没干,直接将实例化阶段创建的对象返回了!所以说在不考虑AOP
的情况下三级缓存有用嘛?讲道理,真的没什么用,我直接将这个对象放到二级缓存中不是一点问题都没有吗?如果你说它提高了效率,那你告诉我提高的效率在哪?
那么三级缓存到底有什么作用呢?不要急,我们先把整个流程走完,在下文结合AOP
分析循环依赖的时候你就能体会到三级缓存的作用!
到这里不知道小伙伴们会不会有疑问,B中提前注入了一个没有经过初始化的A类型对象不会有问题吗?
答:不会
这个时候我们需要将整个创建A这个Bean的流程走完,如下图:
从上图中我们可以看到,虽然在创建B时会提前给B注入了一个还未初始化的A对象,但是在创建A的流程中一直使用的是注入到B中的A对象的引用,之后会根据这个引用对A进行初始化,所以这是没有问题的。
结合了AOP的循环依赖
之前我们已经说过了,在普通的循环依赖的情况下,三级缓存没有任何作用。三级缓存实际上跟Spring中的AOP
相关,我们再来看一看getEarlyBeanReference
的代码:
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
如果在开启AOP
的情况下,那么就是调用到AnnotationAwareAspectJAutoProxyCreator
的getEarlyBeanReference
方法,对应的源码如下:
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.put(cacheKey, bean);
// 如果需要代理,返回一个代理对象,不需要代理,直接返回当前传入的这个bean对象
return wrapIfNecessary(bean, beanName, cacheKey);
}
回到上面的例子,我们对A进行了AOP
代理的话,那么此时getEarlyBeanReference
将返回一个代理后的对象,而不是实例化阶段创建的对象,这样就意味着B中注入的A将是一个代理对象而不是A的实例化阶段创建后的对象。
看到这个图你可能会产生下面这些疑问
1 在给B注入的时候为什么要注入一个代理对象?
答:当我们对A进行了AOP
代理时,说明我们希望从容器中获取到的就是A代理后的对象而不是A本身,因此把A当作依赖进行注入时也要注入它的代理对象
2 明明初始化的时候是A对象,那么Spring是在哪里将代理对象放入到容器中的呢?
在完成初始化后,Spring又调用了一次getSingleton
方法,这一次传入的参数又不一样了,false可以理解为禁用三级缓存,前面图中已经提到过了,在为B中注入A时已经将三级缓存中的工厂取出,并从工厂中获取到了一个对象放入到了二级缓存中,所以这里的这个getSingleton
方法做的时间就是从二级缓存中获取到这个代理后的A对象。exposedObject == bean
可以认为是必定成立的,除非你非要在初始化阶段的后置处理器中替换掉正常流程中的Bean,例如增加一个后置处理器:
@Component
public class MyPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("a")) {
return new A();
}
return bean;
}
}
不过,请不要做这种骚操作,徒增烦恼!
3 初始化的时候是对A对象本身进行初始化,而容器中以及注入到B中的都是代理对象,这样不会有问题吗?
答:不会,这是因为不管是cglib
代理还是jdk
动态代理生成的代理类,内部都持有一个目标类的引用,当调用代理对象的方法时,实际会去调用目标对象的方法,A完成初始化相当于代理对象自身也完成了初始化
4 三级缓存为什么要使用工厂而不是直接使用引用?换而言之,为什么需要这个三级缓存,直接通过二级缓存暴露一个引用不行吗?
答:这个工厂的目的在于延迟对实例化阶段生成的对象的代理,只有真正发生循环依赖的时候,才去提前生成代理对象,否则只会创建一个工厂并将其放入到三级缓存中,但是不会去通过这个工厂去真正创建对象
我们思考一种简单的情况,就以单独创建A为例,假设AB之间现在没有依赖关系,但是A被代理了,这个时候当A完成实例化后还是会进入下面这段代码:
// A是单例的,mbd.isSingleton()条件满足
// allowCircularReferences:这个变量代表是否允许循环依赖,默认是开启的,条件也满足
// isSingletonCurrentlyInCreation:正在在创建A,也满足
// 所以earlySingletonExposure=true
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
// 还是会进入到这段代码中
if (earlySingletonExposure) {
// 还是会通过三级缓存提前暴露一个工厂对象
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
看到了吧,即使没有循环依赖,也会将其添加到三级缓存中,而且是不得不添加到三级缓存中,因为到目前为止Spring也不能确定这个Bean有没有跟别的Bean出现循环依赖。
假设我们在这里直接使用二级缓存的话,那么意味着所有的Bean在这一步都要完成AOP
代理。这样做有必要吗?
不仅没有必要,而且违背了Spring在结合AOP
跟Bean的生命周期的设计!Spring结合AOP
跟Bean的生命周期本身就是通过AnnotationAwareAspectJAutoProxyCreator
这个后置处理器来完成的,在这个后置处理的postProcessAfterInitialization
方法中对初始化后的Bean完成AOP
代理。如果出现了循环依赖,那没有办法,只有给Bean先创建代理,但是没有出现循环依赖的情况下,设计之初就是让Bean在生命周期的最后一步完成代理而不是在实例化后就立马完成代理。
三级缓存真的提高了效率了吗?
现在我们已经知道了三级缓存的真正作用,但是这个答案可能还无法说服你,所以我们再最后总结分析一波,三级缓存真的提高了效率了吗?分为两点讨论:
1 没有进行AOP
的Bean间的循环依赖
从上文分析可以看出,这种情况下三级缓存根本没用!所以不会存在什么提高了效率的说法
2 进行了AOP
的Bean间的循环依赖
就以我们上的A、B为例,其中A被AOP
代理,我们先分析下使用了三级缓存的情况下,A、B的创建流程
假设不使用三级缓存,直接在二级缓存中
上面两个流程的唯一区别在于为A对象创建代理的时机不同,在使用了三级缓存的情况下为A创建代理的时机是在B中需要注入A的时候,而不使用三级缓存的话在A实例化后就需要马上为A创建代理然后放入到二级缓存中去。
对于整个A、B的创建过程而言,消耗的时间是一样的。
综上,不管是哪种情况,三级缓存提高了效率这种说法都是错误的!
答题模板
答:Spring通过三级缓存解决了循环依赖,其中一级缓存为单例池(singletonObjects
),二级缓存为早期曝光对象earlySingletonObjects
,三级缓存为早期曝光对象工厂(singletonFactories
)。
当A、B两个类发生循环引用时,在A完成实例化后,就使用实例化后的对象去创建一个对象工厂,并添加到三级缓存中,如果A被AOP代理,那么通过这个工厂获取到的就是A代理后的对象,如果A没有被AOP代理,那么这个工厂获取到的就是A实例化的对象。
当A进行属性注入时,会去创建B,同时B又依赖了A,所以创建B的同时又会去调用getBean(a)来获取需要的依赖,此时的getBean(a)会从缓存中获取:
第一步,先获取到三级缓存中的工厂;
第二步,调用对象工工厂的getObject方法来获取到对应的对象,得到这个对象后将其注入到B中。紧接着B会走完它的生命周期流程,包括初始化、后置处理器等。
当B创建完后,会将B再注入到A中,此时A再完成它的整个生命周期。至此,循环依赖结束!
面试官:”为什么要使用三级缓存呢?二级缓存能解决循环依赖吗?“
答:如果要使用二级缓存解决循环依赖,意味着所有Bean在实例化后就要完成AOP代理,这样违背了Spring设计的原则,Spring在设计之初就是通过AnnotationAwareAspectJAutoProxyCreator
这个后置处理器来在Bean生命周期的最后一步来完成AOP代理,而不是在实例化后就立马进行AOP代理。