spring詳解-循環依賴的解決
Spring循環依賴
重點提示: 本文都快寫完了,發現“丈夫” 的英文是husband....... 在“②有AOP循環依賴” 改過來了,前面用到的位置太多了就沒改。我是說怎么idea的hansband英文下面怎么有波浪線。各位能夠理解意思就行,英文拼寫不要過于在意.
1.案例引入
在這篇文章中,"②容器刷新"這一小節,留下了如下這樣一個疑問。【https://blog.csdn.net/okok__TXF/article/details/147009731】
// DefaultSingletonBeanRegistry.java
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
————————————————
版權聲明:本文為博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
原文鏈接:https://blog.csdn.net/okok__TXF/article/details/147009731
getSingleton(String beanName, boolean allowEarlyReference) 如果allowEarlyReference是true的話,就用三級緩存來解決循環依賴。下面以Spring5.3.31再次來追蹤一下源碼。
本文章示例代碼見該倉庫:【spring】中的“spring”模塊。
倉庫地址:https://gitee.com/quercus-sp204/sourcecode-and-demos
2.循環依賴分析
①無AOP的循環依賴
下面舉一個例子,一個丈夫和一個妻子的循環依賴。
// 這是Main測試類
@Configuration
@ComponentScan("com.feng.myspring")
public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
Hansband h = context.getBean(Hansband.class);
h.say();
}
}
//1.丈夫
public class Hansband {
@Autowired
private Wife wife;
public void say() {
System.out.println("我是丈夫");
wife.eat();
}
public Hansband( ) {
}
public void eat() {
System.out.println("丈夫吃飯");
}
}
//2.妻子
public class Wife {
@Autowired
private Hansband hansband;
public void say(){
System.out.println("我是妻子");
hansband.eat();
}
public Wife() {
}
public void eat() {
System.out.println("妻子吃飯");
}
}
然后再配置類里面創建了一個丈夫和妻子對象
@Configuration
public class Config01 {
@Bean
public Hansband hansband(){
return new Hansband();
}
@Bean
public Wife wife(){
return new Wife();
}
}
然后上面的項目創建好了之后,現在就開始分析如下方法
// DefaultSingletonBeanRegistry.java
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
首先理解一下這其中的變量都是啥。
- singletonObjects:單例對象的緩存,bean名稱對應一個bean實例。主要存放的是已經完成實例化、屬性填充和初始化所有步驟的單例Bean實例,這樣的Bean能夠直接提供給用戶使用,稱之為終態Bean或叫成熟Bean。【一級緩存】
- earlySingletonObjects:早期單例對象的緩存,bean名稱對應一個bean實例。主要存放的已經完成實例化,但屬性還沒自動賦值的Bean,這些Bean還不能提供用戶使用,只是用于提前暴露的Bean實例,把這樣的Bean稱之為臨時Bean或早期的Bean(半成品Bean) 【二級緩存】
- singletonFactories:單例工廠的緩存,bean名稱對應一個對象工廠。存放的是ObjectFactory的匿名內部類實例,調用ObjectFactory.getObject()最終會調用getEarlyBeanReference方法,該方法可以獲取提前暴露的單例bean引用。【三級緩存】

上圖是getSingleton(String beanName, boolean allowEarlyReference)方法的大致流程。然后就沒了?肯定不是的。
回顧容器刷新
現在回顧一下容器刷新階段,里面會調用這樣的方法
// AbstractApplicationContext.java
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
...
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();
}
// DefaultListableBeanFactory.java
@Override
public void preInstantiateSingletons() throws BeansException {
...
// Trigger initialization of all non-lazy singleton beans...
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
...
getBean(beanName); // 會調用這個
...
}
}
...
}
// AbstractBeanFactory.java
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType,
@Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
.......... // ===== 【重要】
}
上面最后是到了AbstractBeanFactory::doGetBean(xxx)方法了。下面就來著重分析一下這個doGetBean. 這里只挑重點,并不是一行一行地來。
// 一、AbstractBeanFactory.java
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
...
// 1.檢查單例緩存
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
....
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
//2.原型作用域(prototype) 的 Bean 不支持循環依賴,
//因為每次獲取都會創建新實例,Spring 無法通過緩存解決循環依賴。
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
....
try {
.....
// 3.Create bean instance.
if (mbd.isSingleton()) {
// 4.調用了重載的getSingleton(xx,xx)方法===
// 第二個參數是lambda表達式
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
....
}
});
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
...
}
return adaptBeanInstance(name, beanInstance, requiredType);
}
// 二、DefaultSingletonBeanRegistry.java ----- 重載的getSingleton
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
....
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
.....
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
/* getObject()實際執行的是這個。上面的lambda
() -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
....
}
}
*/
// 實際調用的createBean(beanName, mbd, args);---見下面的三
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
.....
}
.....
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
afterSingletonCreation(beanName);
}
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
// 三、AbstractAutowireCapableBeanFactory.java
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
...
try {
// 創建實例bean
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
...
return beanInstance;
}
catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
...
}
...
}
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// 1,得到bean實例,先從緩存中找,找不到就構造一個
BeanWrapper instanceWrapper = null;
//調用 createBeanInstance 方法根據 Bean 定義和構造函數參數創建一個新的 BeanWrapper 實例。
//從 BeanWrapper 中獲取實際的 Bean 實例和 Bean 的類型,并將類型信息記錄到 Bean 定義中。
instanceWrapper = createBeanInstance(beanName, mbd, args);
....
// 2.low post-processors to modify the merged bean definition.
// 應用合并后的 Bean 定義后置處理器
synchronized (mbd.postProcessingLock) {
...
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
...
}
//3.提前暴露單例 Bean 以處理循環引用
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
....
// 這里是不是就放到了三級緩存中去了,注意一下參數哦。。getEarlyBeanReference
// 將一個 ObjectFactory 放入單例工廠緩存中,該工廠會調用 getEarlyBeanReference 方法
// 該方法會返回一個早期的 Bean 引用,以便在循環依賴時可以提前獲取到 Bean 的引用。
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
Object exposedObject = bean;
try {
// 4.填充bean屬性
populateBean(beanName, mbd, instanceWrapper);
// 5.初始化bean【本文開頭的上篇文章】
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
......
}
if (earlySingletonExposure) {
.....
}
...
return exposedObject;
}
doCreateBean 方法是 Spring 框架中用于實際創建 Bean 實例的核心方法。它在 AbstractAutowireCapableBeanFactory 類中實現。該方法承擔了創建 Bean 實例、屬性填充、初始化以及處理循環依賴等重要任務。從最后一個方法我們可以大致總結三個關于spring創建bean的核心步驟,從上到下按順序。
- 實例化Bean : 可以理解為new一個空的bean對象 【
createBeanInstance】 - 填充Bean屬性 : 對bean的依賴屬性進行填充,對@Value @Autowired @Resource注解標注的屬性注入對象引用。【
populateBean】 - 調用Bean初始化方法 : @PostConstruct、init-metohd、等等【
initializeBean】
這一節我們從最開始的getBean(beanName);,一直往下,發現getBean好像就是創建對象嚯。 【這句話后面會用到的。。。】
示例分析
那么案例中,我們是字段注入的,肯定就是發生在“填充Bean屬性” 這個階段才會產生的循環依賴問題!!接下來就以上面的“丈夫、妻子”為例子。

上圖是正在創建Hansband的bean對象、到了屬性填充這個步驟了。進入這個populateBean方法里面,

上圖是populateBean里面,調用這個AutowiredAnnotationBeanPostProcessor :: postProcessProperties(xx)方法。

上圖中,在這個方法里面,調用 findAutowiringMetadata 方法,根據 Bean 的名稱、類型以及屬性值集合來查找自動注入的元數據。自動注入元數據包含了 Bean 中需要自動注入的字段、方法等信息,這些信息是通過 @Autowired、@Resource 等注解標記的。可以發現綠色箭頭指向的是wife了。隨后,調用metadata.inject(bean, beanName, pvs);來執行執行依賴注入操作。

上圖是metadata.inject里面的內容,對目標對象執行依賴注入操作。for循環遍歷注入元素并執行注入操作。 element.inject(target, beanName, pvs);。

上圖中,最后如果value不是null,field.set(bean, value):將解析好的依賴值注入到目標 Bean 的字段中。所以重點應該是上面的resolveFieldValue 核心邏輯

上圖是解析字段的核心邏輯。
首先, 創建依賴描述符(DependencyDescriptor),可以看到將field傳了進去,【field很明顯就是wife嘛,上圖中也可以看出來】。
然后,準備依賴解析環境....這個就不說了。其次,調用了這一行代碼 beanFactory.resolveDependency(xxx);,這個是Spring 依賴解析的核心方法,根據 DependencyDescriptor 查找匹配的依賴值【可以很明顯感覺到,這個又是核心邏輯了】。 找到依賴值后,會緩存起來。
最后會返回這個value嘛。

上圖是beanFactory.resolveDependency,先會檢查是否需要延遲解析代理(處理 @Lazy 等場景),若無需延遲解析,返回 null,進入下一步。然后,調用核心解析方法 doResolveDependency。
// ContextAnnotationAutowireCandidateResolver.java
// 檢查是否需要延遲解析
public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) {
return (isLazy(descriptor) ? buildLazyResolutionProxy(descriptor, beanName) : null);
}
接著進入doResolveDependency方法里面看看。

// DefaultListableBeanFactory.java
@Nullable
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
...
// 一陣噼里啪啦
if (instanceCandidate instanceof Class) {
instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
}
}
// DependencyDescriptor.java
public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)
throws BeansException {
return beanFactory.getBean(beanName); //
}
看到這里,明白了沒,一切都回來了!!! 前面不是說過嗎getBean好像就是創建對象嚯。我們在創建Hansband對象的時候,這個時候并沒有創建Wife對象啊,現在兜兜轉轉又回來了。doGetBean() 現在里面是wife了。!!!!【遞歸進去,Hansband對象的創建還是卡在populateBean方法的】

然后Wife又會走一遍上面的流程。我們在最上面創建好Hansband之后,在填充Hansband屬性之前,有這樣一段代碼。
//3.提前暴露單例 Bean 以處理循環引用
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
....
// 這里是不是就放到了三級緩存中去了,注意一下參數哦。。getEarlyBeanReference
// 將一個 ObjectFactory 放入單例工廠緩存中,該工廠會調用 getEarlyBeanReference 方法
// 該方法會返回一個早期的 Bean 引用,以便在循環依賴時可以提前獲取到 Bean 的引用。
// 在后續再緩存中查找Bean時會觸發匿名內部類getEarlyBeanReference()方法回調
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
//4. 填充屬性
.....
populateBean(beanName, mbd, instanceWrapper);
創建Wife的時候同理啊,又會調用一遍addSingletonFactory,此時是把wife放到singletonFactories里面了。此時singletonFactories里面就會有兩個鍵值對。
hansband -- getEarlyBeanReference("hansband", mbd, bean)
wife -- getEarlyBeanReference("wife", mbd, bean)
然后就輪到執行Wife的屬性填充了,發現需要Hansband的類型的bean,順著上面梳理的流程,最后又會回到下面這里,只不過此時的參數:descriptor就是hansband,beanName是Wife了。
// DefaultListableBeanFactory.java
@Nullable
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
...
// 一陣噼里啪啦
if (instanceCandidate instanceof Class) {
instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
// beanFactory.getBean("hansband"); 遞歸進去
}
}
在Wife的屬性填充的時候,遞歸進去拿Hansband對象,會有如下顯示【這個時候,三級緩存里面就可以看到有兩個鍵值對了,在這里將Hansband的早期對象拿到,并把它從三級緩存移動到二級緩存中去】

這樣在Wife屬性填充的時候,實現了提前將Hansband曝光給Wife完成屬性依賴注入。緊接著,Wife就可以繼續完成后面的初始化邏輯,產生一個成熟的Wife。
創建好之后,會addSingleton("wife", xxxx)
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);
}
}
請看下面的流程圖解釋。

上圖流程中,創建wife的時候,提前注入了一個hansband還沒有初始化好的對象【注入了一個早期bean】,這樣不會有什么問題嗎?
wife創建完全之后,會返回到hansband的“bean初始化階段”,然后hansband就會初始化ok并放入單例池。由于wife中的早期bean和 創建hansband中的bean是同一個引用,故沒有啥問題的。
嘶,看了一下,要三級緩存有這個必要嗎?圖中二級緩存干啥的,getEarlyBeanReference經過調試,發現就是返回了一個bean。看來還是疑點重重,請看下一節。
②有AOP的循環依賴
在上面普通bean的循環依賴場景下,可以看出三級緩存貌似并沒有什么卵用。【實際上確實是的,在普通的循環依賴的情況下,三級緩存沒有任何作用。
】經過反復參考求證,發現三級緩存是和spring 的 AOP掛鉤的!
AOP CSDN地址:https://blog.csdn.net/okok__TXF/article/details/147397816
看一下上一小節的getEarlyBeanReference(beanName, mbd, bean) 到底做了什么
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
}
}
return exposedObject;
}
在未啟動AOP代理之前,這個方法中的SmartInstantiationAwareBeanPostProcessor如下所示【AutowiredAnnotationBeanPostProcessor::getEarlyBeanReference()方法就是單純的返回bean】

打開AOP之后,變成了AnnotationAwareAspectJAutoProxyCreator~ 那么它的getEarlyBeanReference()方法有變化嗎?

// AbstractAutoProxyCreator.java
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
/*========== earlyProxyReferences【重要】
跟蹤哪些 Bean 的代理對象已在 提前暴露階段 生成,他的主要作用大概如下
1. 防止重復代理:避免在 Bean 初始化階段重復創建代理對象,
如果有循環依賴,那么該代理對象在屬性填充階段被創建過了
2. 保證代理對象一致性:確保循環依賴注入的代理對象與最終暴露的代理對象是同一實例
*/
this.earlyProxyReferences.put(cacheKey, bean); // ======
return wrapIfNecessary(bean, beanName, cacheKey); // 下一層
}
// 到這里來了
// 決定是否為給定的 Bean 創建代理對象
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
....
// Create proxy if we have advice.
// getAdvicesAndAdvisorsForBean獲取適用于該 Bean 的通知和增強器
// 該方法會根據 Bean 的類型和名稱,從 Spring 容器中查找所有匹配的通知和增強器,并返回一個數組
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
// DO_NOT_PROXY 就是 null
if (specificInterceptors != DO_NOT_PROXY) { // 不為null
this.advisedBeans.put(cacheKey, Boolean.TRUE);
//調用 createProxy 方法創建代理對象
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
可以大致了解到這個是返回了一個代理對象,可見開啟AOP之前和開啟AOP之后,果然有不一樣。現在我們將案例變成如下有AOP的循環依賴。
// 接口
public interface AopMan {
void aopManSay();
}
@Component("husband")
public class AopHusband implements AopMan{
@Autowired
private AopWoman wife;
@Override
public void aopManSay() {
System.out.println("【AOP】Husband say 哦吼");
}
}
// 接口
public interface AopWoman {
void aopWomanSay();
}
@Component("wife")
public class AopWife implements AopWoman{
@Autowired
private AopMan husband;
@Override
public void aopWomanSay() {
System.out.println("【Aop】Wife say 哈哈");
husband.aopManSay();
}
}
// 創建切面
@Component
@Aspect
public class ManAdvice {
private static final String manExpression = "execution(* com.feng.myspring.aopobj.*.aopManSay*(..))";
//環繞通知
@Around(manExpression)
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("##########【環繞通知中的前置通知】##########");
Object returnVale = joinPoint.proceed();
System.out.println("##########【環繞通知中的后置通知】##########");
return returnVale;
}
}
// 主啟動測試類
@Configuration
@ComponentScan("com.feng.myspring")
@EnableAspectJAutoProxy(proxyTargetClass = false)
public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
// ②有Aop的循環依賴
AopMan husband = context.getBean("husband", AopMan.class);
husband.aopManSay();
System.out.println(husband.getClass()); // 打印一下類型
AopWoman wife = context.getBean("wife", AopWoman.class);
System.out.println(wife.getClass()); // 打印一下類型
}
}
上述例子中,wife的aopWomanSay()方法里面會調用husband的aopManSay方法,但是此時我是對husband的aopManSay配置了環繞通知的,那么請各位想一下此時wife里面注入的husband是原來的對象嗎?肯定不是的,是代理對象,注入的是husband的代理對象,可以運行一下Main測試類看看結果【結果如下】
【Aop】Wife say 哈哈
##########【環繞通知中的前置通知】##########
【AOP】Husband say 哦吼
##########【環繞通知中的后置通知】##########
class com.sun.proxy.$Proxy18 // husband是一個代理對象
class com.feng.myspring.aopobj.AopWife// wife仍然是原來
我們都知道springbean的創建過程,是 1.緩存查詢;2.創建對象;3.屬性填充、注入依賴;4.執行初始化操作;5.這個bean創建好了。大致這樣五個過程,在沒有循環依賴的前提下,開啟aop會生成bean的代理對象,這個生成代理對象的時機是在 “4.執行初始化操作” 這一步的,但是本文討論的都是在第三步哦~~【AOP見下面文章】。所以,三級緩存的存在就是為了提前用對象工廠獲取代理對象,并賦值給wife的husband屬性【代理對象】依賴注入。
AOP CSDN地址:https://blog.csdn.net/okok__TXF/article/details/147397816
回到本小節開始的時候,開啟aop之后,AnnotationAwareAspectJAutoProxyCreator通過getEarlyBeanReference里面的wrapIfNecessary拿到的代理實例,我們對husband進行了AOP代理的話,那么此時getEarlyBeanReference將返回一個代理后的對象,而不是實例化階段創建的對象,這樣就意味著wife中注入的husband將是一個代理對象而不是husband的實例化階段創建后的對象。
Spring是在何處將husband的代理對象放進去的呢?

在完成husband初始化后,Spring又調用了一次getSingleton方法,允許早期引用為false。在前面分析的時候在為wife中注入husband時已經將三級緩存中的工廠取出,并從工廠中獲取到了一個husband代理對象放入到了二級緩存中,并且從三級緩存中移除掉,所以這里的這個getSingleton方法做的時間就是從二級緩存中獲取到這個代理后的husband對象。
3.疑問
1.為什么三級緩存要弄一個對象工廠添加進去,我直接往三級緩存放入對象的代理對象不行嗎?
這個工廠的目的在于延遲對實例化階段生成的對象的代理,只有真正發生循環依賴的時候,才去提前生成代理對象,否則只會創建一個工廠并將其放入到三級緩存中,但是不會去通過這個工廠去真正創建對象。
讀者在此處思考一下,如果看過AOP,那么肯定就會知道,代理對象的生成是在bean的初始化操作中的后置處理器的postProcessAfterInitialization這一步的,而我們循環依賴發生在屬性填充這一步。發生循環依賴的時候,需要注入代理對象,但是還沒到代理對象生成那一步。
try {
// 4.填充bean屬性
populateBean(beanName, mbd, instanceWrapper);
// 5.初始化bean【本文開頭的上篇文章】
exposedObject = initializeBean(beanName, exposedObject, mbd);
} .....
在 Spring 框架中,三級緩存(singletonFactories)存儲的是 ObjectFactory,而非直接存儲代理對象,這一設計是為了解決循環依賴中代理對象生成的 時機問題
2.用二級緩存不行嗎?
循環依賴的簡單場景(無代理) : 是沒問題的
假設有兩個 Bean:A 和 B,它們互相依賴。Spring 的解決流程如下:
- 創建 A:實例化 A(調用構造方法),此時 A 還未完成屬性填充和初始化。
- 暴露早期對象:將 A 的原始對象包裝成
ObjectFactory,存入 三級緩存(singletonFactories)。 - 填充 A 的屬性:發現 A 依賴 B,開始創建 B。
- 創建 B:實例化 B,填充 B 的屬性時發現依賴 A。
- 從三級緩存獲取 A:通過
ObjectFactory.getObject()獲取 A 的早期引用(原始對象),注入到 B 中。 - 完成 B 的初始化:B 初始化完成后,存入一級緩存(
singletonObjects)。 - 完成 A 的初始化:將 A 的最終對象存入一級緩存,替換三級緩存中的臨時對象。
若沒有代理需求,二級緩存(earlySingletonObjects)似乎可以直接存儲原始對象,無需三級緩存。但問題在于:當 Bean 需要被代理時,必須確保注入的是代理對象而非原始對象。
循環依賴 + 代理的復雜場景
假設 A 和 B 都需要被 AOP 代理(例如被 @Transactional 標記),此時若僅用二級緩存,會引發以下問題:
- A 的創建流程:
- 實例化 A(原始對象)。
- 將 A 的原始對象存入二級緩存(
earlySingletonObjects)。 - 填充屬性時發現依賴 B,開始創建 B。
- B 的創建流程:
- 實例化 B(原始對象)。
- 填充 B 的屬性時,從二級緩存獲取 A 的原始對象(未代理)。
- 完成 B 的初始化后,生成 B 的代理對象,存入一級緩存。
- 完成 A 的初始化:
- 在 A 的初始化后階段(
postProcessAfterInitialization),生成 A 的代理對象。 - 最終緩存中的 A 是代理對象,但 B 中注入的 A 是原始對象,導致不一致!
- 在 A 的初始化后階段(
說實話,我自己都不能說服我自己。寫的好勉強。。。這些疑問還是看這篇文章吧。【參考里面的第二篇文章】
end.參考
-
https://mp.weixin.qq.com/s/dSRQBSG42MYNa992PvtnJA 【阿里云開發者 -- 一文詳解Spring Bean循環依賴】
-
http://www.rzrgm.cn/daimzh/p/13256413.html 【博客園 面試必殺技,講一講Spring中的循環依賴 】質量很高
-
原文鏈接:https://blog.csdn.net/chaitoudaren/article/details/105060882 【CSDN Spring源碼最難問題】

浙公網安備 33010602011771號