當使用Spring並結合ProxyFactoryBean和@Inject Provider <>時,會在啓動過程中創建大量對象。Spring BeanFactory和類型搜索
我已經確定原因爲DefaultListableBeanFactory.doGetBeanNamesForType方法。通過迭代所有的bean定義並搜索可以滿足提供程序泛型參數的定義,可以滿足「@Inject Provider <>」。當遇到FactoryBean時,它首先被完全初始化,然後才被查詢getObjectType()。但是,ProxyFactoryBean通常在applicationContext.xml中設置,並依賴於它們代理的bean。完全初始化ProxyFactoryBean會導致內部bean的實例化。
所有這一切都很好,除非內部bean在那個時候不能被實例化 - 例如,因爲它依賴於一些其他的bean,直到原始的bean(提供者的那個)才能被初始化。沒有循環依賴性,只有過度渴望的初始化。
實施例:
class Bean1 { @Inject Provider<X> provider;}
class Bean2 { @Inject Bean1 bean1;}
applicationContext.xml:
<bean id="bean1" class="com.rb.springissues.sample.Bean1"/>
<bean id="bean2" class="com.rb.springissues.sample.Bean2"/>
<bean id="bean2Factory" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="bean2"/>
<property name="proxyTargetClass" value="true"/>
</bean>
在上述例子中的流動是(由彈簧完全管理):
- 實例化
bean1
。嘗試初始化bean1
並發現它有Provider<X>
。- 在上下文中遍歷所有
BeanDefinition
。對於每個人:- 如果它是「普通」bean,請評估類以查看它是否合適。
- 如果它是
FactoryBean
,請嘗試實例化並初始化FactoryBean
以查看「getObjectType()
」將返回的內容。- 爲了初始化
bean2Factory
,需要給它提供一個bean2
的實例。所以Spring試圖實例化並初始化bean2
。- 但是 -
bean2
無法初始化,因爲它依賴於bean1
- 導致Spring引發循環依賴性錯誤。
- 但是 -
- 爲了初始化
- 如果
FactoryObject
已正確創建,現在春天請求的類型和緩存響應(「好路」)。 - 如果我們遇到了一個異常(循環依賴錯誤),它會被捕獲並被忽略,但結果不會被緩存 - 所以如果我們得到另一個bean,它會再次做同樣的事情(並且一次又一次...)
- 在上下文中遍歷所有
參見https://github.com/bironran/spring_issues_proxy_factory爲全面描述和樣品。
我觀察到約500個定義的bean實例化並嘗試初始化超過300,000個對象(同一個bean一次又一次)由於此問題的實際應用程序。該創業公司被分鐘推遲和GC高峯。
此外,此問題呈指數級增長 - 任何無法解決的新依賴項都可能使應用程序加載的時間加倍。
很想聽聽關於如何解決的建議(參見github項目)。
你可以陳述你的商業案例,那就是你想要解決的問題嗎?我沒有看到它在這裏或你的GitHub頁面。在嘗試解決它帶來的問題之前,也許我們可以退後一步並質疑ProxyFactoryBean的用法。當然,除非這純粹是一項學術活動。 –
我們使用ProxyFactoryBean來應用性能監控,限制,事務管理,客戶選擇(作爲多客戶SaaS產品)並應用權限(在特定情況下)。 –
那麼基本上,爲了交叉的關切?我猜想還有其他的方法可以做,但是可以。 –