Spring如何使用三級快取解決迴圈依賴

程式設計の小白發表於2021-06-11

Spring如何使用三級快取解決迴圈依賴

首先來了解一下什麼是迴圈依賴

@Component
public class A {

    @Autowired
    B b;
}

@Component
public class B {

    @Autowired
    A a;
}

在物件A建立過程中,需要注入B,因為容器中沒有B,則去建立B,B建立過程中又需要注入A,而A在等待B的建立,B在等待A的建立,導致兩者都無法建立成功,無法加入到單例池供使用者使用。

Spring則通過三級快取來解決迴圈依賴的問題,另外如果物件的作用範圍是Prototype,則無法通過三級快取解決迴圈依賴,會丟擲BeanCurrentlyInCreationException異常,構造注入的方式,也無法解決迴圈依賴,只有set注入可以解決。

那麼三級快取又是什麼呢?

三級快取就是三個Map

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {

	//一級快取(單例池,經過完成生命週期的物件會放入其中)
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

    //二級快取(剛例項化還未初始化的原始物件會放入其中)
    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
	
    //三級快取(存放建立某個物件的工廠)
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

Spring Bean物件從建立到初始化大致會經過四個流程

getSingleton()doCreateBean()populateBean()addSingleton()

  • getSingleton:從單例池中獲取bean物件,如果沒有,則進行建立

  • doCreateBean():建立bean物件

  • populateBean():填充依賴,如果被填充的物件不存在於單例池,則進行建立等四個流程

  • addSingleton():將初始化完成的物件加入到單例池

迴圈依賴的物件在三級快取中的遷移過程

  • A 建立過程中需要 B, 於是 A 將自己放到三級快取裡面,去例項化 B

  • B 例項化的時候發現需要 A,於是 B 先查一級快取,沒有,再查二級快取,還是沒有,再查三級快取

    找到了A,然後把三級快取中的 A 放到二級快取,並刪除三級快取中的 A

  • B 順利初始化完畢,將自己放到一級快取中(此時 B 中的 A 還是建立中狀態,並沒有完全初始化),刪除三級快取中的 B

    然後接著回來建立 A,此時 B 已經完成建立,直接從一級快取中拿到 B,完成 A 的建立,並將 A 新增到單例池,刪除二級快取中的 A

圖示:

相關文章