spring 之 lazy-init Autowired depends-on
1 lazy-init
lazy-init是延遲初始化的意思。
spring中容器都是盡早的創建和配置所有的單例bean,因此當容器在啟動時,就會去配置和創建單例bean。 默認情況下 beans 的lazy-init 是沒有配置的,就相當於是:
default-lazy-init="false"
bean 的 lazy-init 默認繼承於 beans 的配置。 也就是說, 不配置任何lazy-init 的情況下: lazy-init = false
這樣做的好處是在程序剛運行時就可以將配置的錯誤或者環境問題立刻暴露出來。當然,壞處就是啟動時,因為要初始化所有的單例bean,系統開銷會很大,啟動過程比較慢。
如果不想單例bean提前實例化,可以設置lazy-initialized延遲加載,只有在第一次請求的時候采取初始化,而不是在啟動容器時初始化。
如果一個非延遲的單例bean依賴了標記了lazy-init的bean,那麽這個標記了lazy-init的bean也會在容器啟動時被創建(延遲初始化失效)。
設置為延遲加載的bean一旦註入給不延遲的單例bean,就意味著它並不會被延遲加載了。
可以看到代碼中對所有註冊的bean,即this.beanDefinitionNames,對於每個bean都會做如下判斷,如果成立就會執行依賴註入:
容器的初始化是在AbstractApplicationContext的refresh()方法中執行的,如下代碼對lazy-init進行了處理:
- finishBeanFactoryInitialization(beanFactory);
跟蹤下去可以找到真正的讀取lazy-init屬性進行懶加載相關處理的地方
- if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit())
可以看出,只有單例的bean才有可能在容器初始化的時候就完成依賴註入,當lazy-init屬性不配置(默認值)或者配置為false的時候,上述if就會成立,當然這裏默認不配置abstract屬性,所以它默認也是false。if成立,就會執行getBean從而進行依賴註入,這樣在容器初始化的過程中就已經實例化了Bean,當真正的請求bean的時候,其實只是從緩存中讀取而已。
而如果lazy-init屬性配置為true,那麽就會進行懶加載了,這樣在容器初始化的過程中不會進行依賴註入,只有當第一個getBean的時候才會實例化Bean。
2 lazy-init & depends-on
<!-- 移除 boss Bean 的屬性註入配置的信息 --> <bean id="boss" class="com.baobaotao.Boss" lazy-init="true"/> <!-- depends-on 測試 ,去掉 lazy-init配置後,也就是設置為 fasle, 那麽 --> <bean id="man" class="com.baobaotao.Man" depends-on="boss" lazy-init="true"/>
depends-on 是表明一種初始化的先後順序,它 和 直接 直接的屬性依賴(比如 Man 擁有一個 boss 屬性)還是不一樣的。 但是表現是差不多的。
具體來說, 就上面的例子, 如果 man 是延遲初始化的, 那麽 boss 是否延遲初始化的都不要緊。 但是 如果 mans 是立即初始化的,那麽 boss 也會立即被要求初始化, 即使 boss 設置了 lazy-init="true"。 如果boss 無法autowire 某些屬性,那麽容器就會拋出異常, man也就無法再完成初始化了!
為什麽會這樣呢? 觀察源碼可見:
AbstractBeanFactory.doGetBean 方法:
final RootBeanDefinition ex1 = this.getMergedLocalBeanDefinition(beanName); this.checkMergedBeanDefinition(ex1, beanName, args); String[] dependsOn = ex1.getDependsOn(); String[] scopeName; if(dependsOn != null) { // 這裏開始對 bean 所依賴的其他bean 進行處理 scopeName = dependsOn; int scope = dependsOn.length; for(int ex2 = 0; ex2 < scope; ++ex2) { String dep = scopeName[ex2]; if(this.isDependent(beanName, dep)) { throw new BeanCreationException(ex1.getResourceDescription(), beanName, "Circular depends-on relationship between \‘" + beanName + "\‘ and \‘" + dep + "\‘"); } this.registerDependentBean(dep, beanName); this.getBean(dep); } }
另外, 從異常堆棧中, 可見其中存在getBean 和 doGetBean 方法的反復的相互調用,而這個正是 spring 在處理 depends-on 依賴:
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:296) 這裏出現了反復相互調用 at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:759)
3 lazy-init & Autowired 註解
lazy-init 和 Autowired 其實並沒有明確的關系。 autowired 負責註入, 而lazy-init 關系到 初始化。 僅有的一點關系是: autowired 註入的 bean 必須是已經初始化成功了的bean。 換句話說, autowired 就相當於調用了 getBean,那麽不管lazy-init如何設置, 被autowire需要的被依賴的那個bean 必須初始化。
如果一個bean 需要autowire 註入 另一個無法的bean,那麽但是,這個bean 不要求立即初始化, 那麽這個autowire 潛在的錯誤是不會暴露出來的。 也就是說, 推遲了可能出現的異常。
參考:
http://blog.csdn.net/qq30211478/article/details/77847677?locationNum=4&fps=1
http://blog.csdn.net/u011734144/article/details/72632327?locationNum=8&fps=1 : Spring源碼分析之lazy-init屬性的配置
lazy-init 有3個選項, true, false, default
spring 之 lazy-init Autowired depends-on