1. 程式人生 > >Spring延遲初始化——lazy-init與depends-on

Spring延遲初始化——lazy-init與depends-on

一、lazy-init

lazy-init是延遲初始化的意思。
spring中容器都是儘早的建立和配置所有的單例bean,因此當容器在啟動時,就會去配置和建立單例bean。
這樣做的好處是在程式剛執行時就可以將配置的錯誤或者環境問題立刻暴露出來。當然,壞處就是啟動時,因為要初始化所有的單例bean,系統開銷會很大,啟動過程比較慢。

如果不想單例bean提前例項化,可以設定lazy-initialized延遲載入,只有在第一次請求的時候採取初始化,而不是在啟動容器時初始化。

在XML中,屬性lazy-init控制元素的初始化。

<bean id="order" class="twm.spring.start.Order" lazy-init="true"/>
<bean name="customer" class="twm.spring.start.Customer"/>

ApplicationContext載入上面的配置啟動時,customer會預先初始化,而order則不會。

如果一個非延遲的單例bean依賴了標記了lazy-init的bean,那麼這個標記了lazy-init的bean也會在容器啟動時被建立(延遲初始化失效)。
設定為延遲載入的bean一旦注入給不延遲的單例bean,就意味著它並不會被延遲載入了。

<bean id="notify" class="twm.spring.start.NotifyServiceByCellPhoneImpl" lazy-init="true" />
<bean id="order" class="twm.spring.start.Order">
    <constructor-arg name="notifyservice" ref="notify" />
</bean>

例如這個例子,notify bean雖然設定了延遲載入,但由於它需要注入到order bean(沒有設定延遲載入),因此notify bean並不會延遲載入了。

通過設定元素的default-lazy-init屬性,可以設定容器級別的延遲載入。看樣例:

如果是通過Java程式碼初始化bean則需要實現Lazy介面重寫value方法即可

    class MyBean implements Lazy{

        @Override
        public boolean value() {
            return true;
        }
    }

二、depends-on

思考一個問題:有兩個類並沒有依賴注入的關係,但是初始化過程卻有先後順序關係。
比如read bean初始化時需要讀取配置,write bean初始化時會寫入配置。那麼read bean只能在write bean之後初始化。

建立一個類MockFile.java,裡面放一個靜態變數,用來模擬檔案儲存:

public class MockFile {
    private static String filecontent;

    public static String getFilecontent() {
        return filecontent;
    }
    public static void setFilecontent(String filecontent) {
        MockFile.filecontent = filecontent;
    }
}

建立ReadWork.java:

public class ReadWork {
    public ReadWork() {
        System.out.print("ReadWork建構函式初始化,讀取模擬檔案內容:");
        System.out.println(MockFile.getFilecontent());
    }
}

建立WriteWork.java:

public WriteWork() throws InterruptedException {
    System.out.println("WriteWork建構函式初始化,並向模擬檔案寫入內容:property:better");
    Thread.sleep(3000);
    MockFile.setFilecontent("property:better");
}

在容器配置檔案beans.xml中註冊物件:

<bean id="writeWork" class="twm.spring.depends.WriteWork" />
<bean id="readWork" class="twm.spring.depends.ReadWork"  />

啟動觸發容器初始化:

public static void main(String[] args) throws Exception {
    new ClassPathXmlApplicationContext("beans.xml");
}

執行程式:

首先會輸出:

WriteWork 建構函式初始化,並向模擬檔案寫入內容:property:better

3秒鐘後會輸出:

ReadWork 建構函式初始化,讀取模擬檔案內容:property:better

由此可知,容器會按配置檔案中的先後順序初始化物件bean,並且初始過程非併發執行的。

現在來給writeWork bean加上延遲載入屬性:

<bean id="writeWork" class="twm.spring.depends.WriteWork" lazy-init="true" />

再執行程式,只會輸出:

ReadWork 建構函式初始化,讀取模擬檔案內容:null

因為writeWork bean加上延遲載入屬性,而程式中又沒有請求呼叫writeWork bean 的地方,所以writeWork bean不會被例項化。
因此模擬檔案不會被寫入內容。
我想讓write bean在read bean之前初始化,怎麼辦呢?
在readWork上加上depends-on屬性就可以了:

<bean id="readWork" class="twm.spring.depends.ReadWork" depends-on="writeWork" />

這是告訴容器readWork的初始化,依賴於writeWork,所以你必須讓writeWork先初始化。

也可以依賴多個bean,為depends-on屬性值列表可用逗號,空白,分號分隔。

<bean id="readWork" class="twm.spring.depends.ReadWork" depends-on="writeWork,logger,dbconnect" />

如果是通過Java程式碼的方式初始化則需要實現DepondsOn介面

    class MyBean implements DependsOn {
        @Override
        public String[] value() {
            return new String[beanname...];
        }
    }