文章目录
- 0. 前言
- 1. 一般情况
- 2. 循环引用
- 3. 总结
- 代码附录
- 1. DefaultSingletonBeanRegistry#getSingleton()
- 2. DefaultSingletonBeanRegistry#beforeSingletonCreation()
- 3. AbstractAutowireCapableBeanFactory#createBeanInstance
- 4. DefaultSingletonBeanRegistry#addSingletonFactory
- 5. AbstractAutowireCapableBeanFactory#doCreateBean
- 6. AbstractAutowireCapableBeanFactory#initializeBean()
- 7. DefaultSingletonBeanRegistry#addSingleton
0. 前言
0.1说明
以下的东西,都是我看源码总结出来的,并不是道听途说。大部分都是经过我验证的。如果有错误,那就是我源码没有看明白。有错误的地方,欢迎指出,谢谢。。。。。
0.2 为什么要学习spring 源码?
spring 源码我也看了一段时间了。那为什么要看spring 源码呢?
现在spring 发展的多厉害我不用多说吧,到处都离不开。
都知道:Spring的源代码设计精妙、结构清晰、匠心独用,处处体现着大师对Java设计模式灵活运用以及对Java技术的高深造诣。。它的源代码无意是Java技术的最佳实践的范例。
那spring 的源码体系极其庞大,各种继承实现体系更不用说了。那家伙,能看懂就不错了。还想学习人家是怎么设计的,怎么运用各种设计模式的?我感觉,大部分人,看都费劲,分析人家的结构,还是算了。(除非水平特别的高)。
那我感觉,大部分人,看spring 源码之后,对spring 的流程与设计思想,都有了很深的认识。那我看spring 源码的目的,就是为了对spring更加的了解,更加的精通spring 。并不是说,看了人家的源码,自己写代码的水平就提高了。那也不太可能吧…… 所以说,我看spring源码就是为了对spring 更加的了解。了解其spring 的扩展点等地方,那么有朝一日,你开发了自己的组件,自己的框架,可以很顺利的整合到spring 中。现在,如果一个框架,连spring 都无法整合?你感觉这个框架可以被很多地方运用么?其他的不用多说了吧……
1. 一般情况
那么在了解 “spring如何解决循环引用情况下创建bean的?” 的问题之前呢,我感觉有必要先阐述一下:spring在一般情况下创建bean的大概流程。注意是大概流程!
1.1 图解
1.2 文字
为了方便进行代码的跳转,和方便阅读,我把图转换成文字。
- 进入
doGetBean()
方法,我们都知道,获取bean是从此方法开始的。
- 根据
beanName
首先去singletonObjects
容器(Map)中去拿,如果没有就返回null。
-----因为此bean并没有处于创建状态(没有在singletonsCurrentlyInCreation
中)所以并不会去二级缓存,三级缓存中去拿。 代码详见附录 1
- (拿不到的情况)将beanName 放入
singletonsCurrentlyInCreation
;
----singletonsCurrentlyInCreation
是一个list 里面存放着正在创建的bean的beanName
,当bean创建完成,即将放入singletonObjects
中之前,会从中移除。 代码详见 附录2
- 选择合适的构造方法对bean进行创建。详见代码附录3
- 放入中
singletonFactories(三级缓存)
----并不是所有的都放入三级缓存
,是有条件的;条件是:单例的,允许循环引用的,正在创建状态中的 放入缓存的作用是什么呢?(为了解决循环引用的问题。) 详见代码附录4
- 对其内的属性进行填充
----- 进行属性注入的时候,如果要注入的对象,还会调用getBean()
进行获取(有,则拿到,没有则创建),其流程与这个相同。 如果要注入的对象,其内还依赖了一个其他的对象,则原理相同。详见代码附录 5
- 对bean应用相应的
BeanPostProcessor
,调用相应的Aware 的方法,调用相应的生命周期中的初始化方法等
-----BeanPostProcessor
这种类型,不用我说,相信都知道,毕竟这东西很重要。
Aware 看代码:
生命周期中的初始化的方法比如:你通过xml
or注解
,声明一个 init-method ,或者是afterPropertiesSet()
方法等。
详见代码附录6
- 将
beanName
从singletonsCurrentlyInCreation
中删除将bean放入singletonObjects
, 同时根据beanName将bean从二级缓存
,三级缓存
中删除。
详见代码附录7
2. 循环引用
2.1 什么是循环引用?
很简单,就是一个对象1
依赖对象2
, 但是对象2
呢,又依赖对象1
。如图所示
2.2 前置代码准备
准备两个类,让其循环引用,其代码如下:
BeanOne
@Component
public class BeanOne {
@Autowired
private BeanTwo beanTwo;
}
BeanTwo
@Component
public class BeanTwo {
@Autowired
private BeanOne beanOne;
}
2.3 流程图解
其代码步骤,在附录都有。希望在看完这个图之后,能理解流程。注意,请跟着箭头的方向看。
2.4 构造方法注入在循环依赖时产生的问题。
2.4.1 前置代码准备
BeanOne
@Component
public class BeanOne {
private final BeanTwo beanTwo;
/**
* 构造方法注入
*/
@Autowired
public BeanOne(BeanTwo beanTwo) {
this.beanTwo = beanTwo;
}
}
BeanTwo
@Component
public class BeanTwo {
private final BeanOne beanOne;
/**
* 构造方法注入
*/
@Autowired
public BeanTwo(BeanOne beanOne) {
this.beanOne = beanOne;
}
}
- 测试代码(好像给这个没啥用,哈哈哈)
@Test
public void test3() {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(AppConfig.class);
}
2.4.2 结果
那结果必定是创建失败的。
2.4.3 为什么?
为什么会失败呢?那我们查看源码,得知的流程为:如果你这边就一个有参的构造方法,他必然会使用你这个有参的构造方法进行对象的创建。
那么流程来了:
1. 准备创建 beanOne 对象:首先将 beanOne的beanName 放入 标识正在创建的list 变量中(singletonsCurrentlyInCreation) 详情看代码附录 2
2. 获得beanOne的构造方法,因为他就一个有参的,用此构造方法进行对象的创建(我已经通过源码验证)
3. 解析beanOne 的唯一一个有参的构造方法, 发现参数中有一个BeanTwo 类型的变量`,则调用getBean()获取BeanTwo类型的变量注入
4. 到了getBean()方法来获取beanTwo 对象了,首先看singletenObjects 等容器中有没有,肯定是没有,因为还没创建
5. 准备创建beanTwo对象: 首先将beanTwo的beanName 放入 标识正在创建的list 变量中(singletonsCurrentlyInCreation) 详情看代码附录 2
6. 获得beanTwo 的构造方法,因为他就一个有参的,用此构造方法进行对象的创建(我已经通过源码验证)
7. 解析 beanTwo 的唯一一个有参的构造方法,发现参数中有一个BeanOne 类型的变量,则调用getBean()获取BeanOne类型的变量注入
8. 到了getBean() 方法来获取beanOne 对象了,首先看singletenObjects 等容器中有没有,肯定是没有,因为还没创建,我们也放不了。
9. 又准备创建beanOne对象:那么重点来了,首先将 beanOne的beanName 放入 标识正在创建的list 变量中(singletonsCurrentlyInCreation) 详情看代码附录 2
然后,你看代码附录2中的代码可以察觉到,我们this.singletonsCurrentlyInCreation.add(beanName) 这句,肯定放不进去,因为有相同的了,所以此语句返回了false, 然后!取反,就是true。然后与另一个语句进行结合。这条语句的判断,就为true ,直接if语句体,然后直接抛了一个异常,然后就没然后了。创建失败。!!结束。
这就是根本原因。
2.5 在循环依赖状态下 使用 构造方法注入与setter(or @Autowired ) 混合的方式产生的问题
那我想说的是,就别抽那个风了。这个跟两个对象实例化的顺序有关联,比如 Bean1
是使用的是setter
or 字段 @Autowired
方式注入的。且使用构造方法,能创建出来对象的,那么一创建完成(属性注入之前) 就放到了三级缓存中。
那么剩下的bean2
无论是使用构造方法注入
,还是setter方式注入
,都不会影响了,因为在bean2
需要bean1
的时候去调用getBean()
方法。然后在getBean()
方法的一开始,就会去缓存拿,那么由于bean1
已经通过构造方法创建了,放入了缓存中,那么必定可以拿到。拿到之后就直接返回了,然后把返回的bean1
直接注入到bean2
中。(虽然现在的bean1中属性还没有注入完整。)
那么显然再拿bean1
的时候,根本就直接返回了,不会执行后面复杂的逻辑,也就不会执行到 代码附录2
中的那个方法,也就不会抛出异常了(其他的地方检测抛出的,没在论述范围之内)。
那么反过来,那就出问题了。
bean1
放入 标识正在创建的list 变量中(singletonsCurrentlyInCreation
) 详情看代码附录 2bean1
使用的是 构造方法注入,然后调用bean1
构造方法的时候,发现需要获得bean2
. 那么就调用getBean()
来创建bean2
。bean2
放入标识正在创建的list 变量中(singletonsCurrentlyInCreation
) 详情看代码附录 2bean2
,调用构造方法创建出来了bean2
类的实例,然后再调用populateBean()
方法进行bean1
的注入,那么再获取bean1
的时候,就调用getBean()
方法getBean()
方法获取bean1
, 首先去缓存中找,发现没有。因为步骤1的bean1
根本正在调用构造方法中,根本连实例都没产生出来。所以肯定没有放入缓存,那么去缓存拿,肯定拿不到。之后又进行Bean1
的创建bean1
又要放入标识正在创建的list 变量中(singletonsCurrentlyInCreation
) 详情看代码附录 2- 因为在步骤1 里面已经放了,现在再放,肯定会进入 代码附录2 中的if 体里面,自然会抛出异常。
- 没了。
2.5 循环依赖中关于aop代理对象的问题
那么有人就问了:这关aop啥事?
比如这么一个情况,我有两个bean
产生了循环依赖。这两个bean
都是配置了aop
的类,那么问题来了。
那bean1
只是调用构造方法实例化了一下,就扔到了三级缓存
中了?
还是在这个期间,调用aop
的BeanPostProcessor
给我们产生一个代理对象
,然后把这个代理对象
扔到了三级缓存
?
那么通过观察源码发现,在此期间并没有应用aop
。我也不能在这说啥就是啥吧?那么来通过调试证实一下,证明我不是道听途说的(扔了个lambda
(ObjectFactory
的实现)可以看到bean
现在还没有被代理):
那么排除了上面的,那么就一种可能,就是在从三级缓存
中获取的时候,就会调用这个lambda
,然后应用对应的BeanPostProceesor
产生aop代理对象
。验证:
3. 总结
那么上面一堆,问:spring
是如何解决循环依赖的问题的呢?
自己根据理解瞎聊就行。(下面总结一下大概)
通过
三级缓存
解决了循环依赖。当
Bean1
与Bean2
发生循环依赖
的时候呢,比如首先实例化的是Bean1
,那么首先是去缓存
里面取,那么肯定拿不到,然后调用Bean1
的相应的构造方法
进行Bean1
的实例化,然后再把Bean1
的对象放入三级缓存
,然后接下来就是调用populateBean()
方法注入Bean1
对应的属性,注入属性的时候,发现依赖了Bean2
则调用getBean()
方法获取Bean2
的对象。
那么就进入了创建
Bean2
的流程,首先去缓存
中拿Bean2
, 肯定拿不到。然后调用Bean2
对应的构造方法
,创建了Bean2
的实例,然后放入三级缓存
,之后调用populateBean()
方法注入Bean2
对应的属性,然后发现依赖了Bean1
。那么就该调用getBean()
方法获取Bean1
了。
那么又进入了创建了
Bean1
的流程,那么这次还是从缓存
中拿,发现有了!!!直接返回这个Bean1
,然后
Bean2
初始化完成,然后返回,(虽然引用的Bean1
里面的属性还没有注入完成。)
Bean2
返回给Bean1
了,所以Bean1
也注入了Bean2
那么此时
Bean2
中引用的Bean1
也完整了,
解释一下一个基础的问题😂
因为这个是引用啊,就是Bean2 拿的是Bean1 的地址啊。
那Bean1 的属性更新了, 那就都更新了。(这么基础的问题还是解释一下吧)
3.1 注入方式不同对循环依赖的影响
注入方式 | 结果 |
---|---|
两个都是setter / 字段@Autowired | 成功 |
两个都是构造方法 注入 | 失败 |
构造方法与 setter /字段@Autowired 混合 | 先创建的采用setter/字段@Autowired 则可以创建成功,反之不能。 |
其他的细节就不总结了。
代码附录
1. DefaultSingletonBeanRegistry#getSingleton()
调用位置:
- doGetBean()->
- Object sharedInstance = getSingleton(beanName);
代码:
- Object sharedInstance = getSingleton(beanName);
/**
* Return the (raw) singleton object registered under the given name.
* <p>Checks already instantiated singletons and also allows for an early
* reference to a currently created singleton (resolving a circular reference).
* @param beanName the name of the bean to look for
* @param allowEarlyReference whether early references should be created or not
* @return the registered singleton object, or {@code null} if none found
*/
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
/*isSingletonCurrentlyInCreation(): 根据beanName 判断此对象是否正在创建(是否在创建的list中)*/
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
2. DefaultSingletonBeanRegistry#beforeSingletonCreation()
调用位置:
-
doGetBean ->
-
sharedInstance = getSingleton(beanName, () -> {
-
/*将beanName 存入 singletonsCurrentlyInCreation(当前正在创建的bean的名称) list 中*/ beforeSingletonCreation(beanName);
-
-
代码:
/**
* Callback before singleton creation.
* <p>The default implementation register the singleton as currently in creation.
* @param beanName the name of the singleton about to be created
* @see #isSingletonCurrentlyInCreation
*/
protected void beforeSingletonCreation(String beanName) {
/*
inCreationCheckExclusions: 存储的是 在当前创建检查中 被排除的bean的名称
singletonsCurrentlyInCreation: 存储的是 当前正在创建的bean的名称
*/
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
3. AbstractAutowireCapableBeanFactory#createBeanInstance
调用位置:
-
AbstractAutowireCapableBeanFactory#doCreateBean
-
instanceWrapper = createBeanInstance(beanName, mbd, args);
-
代码:
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// Make sure bean class is actually resolved at this point.
Class<?> beanClass = resolveBeanClass(mbd, beanName);
// ..............上面省略
/*
需要去确定一个或者多个构造方法。然后利用这些构造方法返回的构造方法 进行bean的创建。
此方法点进去可以查看他是如何选定合适的构造方法的。
*/
// Need to determine the constructor...
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null ||
mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
/*使用自动注入方式的构造方法*/
return autowireConstructor(beanName, mbd, ctors, args);
}
/*使用无参的构造方法直接构造返回*/
// No special handling: simply use no-arg constructor.
return instantiateBean(beanName, mbd);
}
4. DefaultSingletonBeanRegistry#addSingletonFactory
调用位置:
-
AbstractAutowireCapableBeanFactory#doCreateBean->
-
/*放入singletonFactories中(三级缓存)*/ addSingletonFactory(beanName, () -> 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);
}
}
}
5. AbstractAutowireCapableBeanFactory#doCreateBean
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
//.............
// Initialize the bean instance.
Object exposedObject = bean;
try {
/* 进行属性值的注入*/
populateBean(beanName, mbd, instanceWrapper);
/*
进行bean的初始化:
应用 BeanPostProcessor(当然也包括aop) , 调用实现了InitializingBean或定义了一个自定义init方法afterPropertiesSet 方法@PostConstruct注解的方法等(生命周期中的方法)
invokeAwareMethods的调用
*/
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
//.............
}
6. AbstractAutowireCapableBeanFactory#initializeBean()
调用位置 :
- doCreateBean()->
- exposedObject = initializeBean(beanName, exposedObject, mbd);
代码:
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
}
else {
/*
调用Aware 的方法
例如如果你实现了其特定的Aware 接口的话,会在此调用接口中的方法给其传值
比如你实现了BeanFactoryAware 接口,他在这会调用其中的setBeanFactory() 方法,
把方法需要的参数:bean工厂传给你。你就可以用了。
里面的代码非常的简单,点进去看看,就知道了。
*/
invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
/* 应用 BeanPostProcessor 的 postProcessorsBeforeInitialization 方法,
注意@PostConstruct 注解的方法,也是其中一个BeanPostProcessor 处理的*/
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
/*如调用afterPropertiesSet(), 自定义的init 方法,*/
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
/*
应用 BeanPostProcessor 的 processorsAfterInitialization 方法
如:aop代理对象的生成; 事件发布等;
*/
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
7. DefaultSingletonBeanRegistry#addSingleton
调用位置:
-
doGetBean()
-
sharedInstance = getSingleton(beanName, () -> {
-
/* 放入singletonObjects 里面(狭义的ioc容器。)*/ addSingleton(beanName, singletonObject);
-
-
代码:
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}