Spring系列第十二講 lazy-init:bean延遲初始化

qwer1030274531發表於2020-11-02

bean實時初始化

在容器啟動過程中被建立組裝好的bean,稱為實時初始化的bean,spring中預設定義的bean都是實時初始化的bean,這些bean預設都是單例的,在容器啟動過程中會被建立好,然後放在spring容器中以供使用。

實時初始化bean的有一些優點

  • 更早發下bean定義的錯誤:實時初始化的bean如果定義有問題,會在容器啟動過程中會丟擲異常,讓開發者快速發現問題

  • 查詢bean更快:容器啟動完畢之後,實時初始化的bean已經完全建立好了,此時被快取在spring容器中,當我們需要使用的時候,容器直接返回就可以了,速度是非常快的。

案例

ActualTimeBean類

package com.javacode2018.lesson001.demo11;/**
 * 實時初始化的bean
 */public class ActualTimeBean {
    public ActualTimeBean() {
        System.out.println("我是實時初始化的bean!");
    }}12345678910

一會我們在spring中建立上面這個物件,建構函式中會輸出一段話,這段話會在spring容器建立過程中輸出。
actualTimeBean.xml

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="
       xmlns:xsi="
       xsi:schemaLocation="
    /spring-beans-4.3.xsd">
    <bean id="actualTimeBean" class="com.javacode2018.lesson001.demo11.ActualTimeBean"/></beans>123456789

測試用例

package com.javacode2018.lesson001.demo11;import org.junit.Test;import org.springframework.context.support.ClassPathXmlApplicationContext;/**
 * bean預設是實時初始化的,可以透過bean元素的lazy-init="true"將bean定義為延遲初始化
 */public class LazyBeanTest {
    @Test
    public void actualTimeBean() {
        System.out.println("spring容器啟動中...");
        String beanXml = "classpath:/com/javacode2018/lesson001/demo11/actualTimeBean.xml";
        new ClassPathXmlApplicationContext(beanXml); //啟動spring容器
        System.out.println("spring容器啟動完畢...");
    }}12345678910111213141516171819

注意上面程式碼,容器啟動前後有輸出,執行actualTimeBean輸出:

spring容器啟動中...我是實時初始化的bean!spring容器啟動完畢...123

可以看出actualTimeBean這個bean是在容器啟動過程中被建立好的。

延遲初始化的bean

從上面我們可以看出,實時初始化的bean都會在容器啟動過程中建立好,如果程式中定義的bean非常多,並且有些bean建立的過程中比較耗時的時候,會導致系統消耗的資源比較多,並且會讓整個啟動時間比較長,這個我估計大家都是有感受的,使用spring開發的系統比較大的時候,整個系統啟動耗時是比較長的,基本上多數時間都是在建立和組裝bean。

spring對這些問題也提供瞭解決方案:bean延遲初始化。

所謂延遲初始化,就是和實時初始化剛好相反,延遲初始化的bean在容器啟動過程中不會建立,而是需要使用的時候才會去建立,先說一下bean什麼時候會被使用:

  1. 被其他bean作為依賴進行注入的時候,比如透過property元素的ref屬性進行引用,透過構造器注入、透過set注入、透過自動注入,這些都會導致被依賴bean的建立

  2. 開發者自己寫程式碼向容器中查詢bean的時候,如呼叫容器的getBean方法獲取bean。

上面這2種情況會導致延遲初始化的bean被建立。

延遲bean的配置

在bean定義的時候透過lazy-init屬性來配置bean是否是延遲載入,true:延遲初始化,false:實時初始化

<bean lazy-init="是否是延遲初始化" />1

我們來2個案例看一下效果。

LazyInitBean類

package com.javacode2018.lesson001.demo11;public class LazyInitBean {
    public LazyInitBean() {
        System.out.println("我是延遲初始化的bean!");
    }}1234567

lazyInitBean.xml

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="
       xmlns:xsi="
       xsi:schemaLocation="
    /spring-beans-4.3.xsd">
    <bean id="lazyInitBean" class="com.javacode2018.lesson001.demo11.LazyInitBean" lazy-init="true"/></beans>123456789

注意上面的lazy-init="true"表示定義的這個bean是延遲初始化的bean。

測試用例

LazyBeanTest中加個方法

@Testpublic void lazyInitBean() {
    System.out.println("spring容器啟動中...");
    String beanXml = "classpath:/com/javacode2018/lesson001/demo11/lazyInitBean.xml";
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(beanXml); //啟動spring容器
    System.out.println("spring容器啟動完畢...");
    System.out.println("從容器中開始查詢LazyInitBean");
    LazyInitBean lazyInitBean = context.getBean(LazyInitBean.class);
    System.out.println("LazyInitBean:" + lazyInitBean);}12345678910

注意上面的輸出,容器啟動前後有輸出,然後又從容器中查詢LazyInitBean。

執行輸出

執行lazyInitBean方法,輸出:

spring容器啟動中...spring容器啟動完畢...從容器中開始查詢LazyInitBean
我是延遲初始化的bean!LazyInitBean:com.javacode2018.lesson001.demo11.LazyInitBean@11dc371512345

程式碼結合輸出可以看出來,LazyInitBean在容器啟動過程中並沒有建立,當我們呼叫context.getBean方法的時候,LazyInitBean才被建立的。

案例2

上面這種方式是我們主動從容器中獲取bean的時候,延遲初始化的bean才被容器建立的,下面我們再來看一下當延遲初始化的bean被其他實時初始化的bean依賴的時候,是什麼時候建立的。

ActualTimeDependencyLazyBean類

package com.javacode2018.lesson001.demo11;public class ActualTimeDependencyLazyBean {
    public ActualTimeDependencyLazyBean() {
        System.out.println("ActualTimeDependencyLazyBean例項化!");
    }
    private LazyInitBean lazyInitBean;
    public LazyInitBean getLazyInitBean() {
        return lazyInitBean;
    }
    public void setLazyInitBean(LazyInitBean lazyInitBean) {
        this.lazyInitBean = lazyInitBean;
        System.out.println("ActualTimeDependencyLazyBean.setLazyInitBean方法!");
    }}12345678910111213141516171819

ActualTimeDependencyLazyBean類中有個lazyInitBean屬性,對應的有get和set方法,我們將透過set方法將lazyInitBean物件注入。

actualTimeDependencyLazyBean.xml

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="
       xmlns:xsi="
       xsi:schemaLocation="
    /spring-beans-4.3.xsd">
    <bean id="lazyInitBean" class="com.javacode2018.lesson001.demo11.LazyInitBean" lazy-init="true"/>
    <bean id="actualTimeDependencyLazyBean" class="com.javacode2018.lesson001.demo11.ActualTimeDependencyLazyBean">
        <property name="lazyInitBean" ref="lazyInitBean"/>
    </bean></beans>123456789101112

注意上面定義了2個bean:

lazyInitBean:lazy-init為true,說明這個bean是延遲建立的

actualTimeDependencyLazyBean:透過property元素來注入lazyInitBean,actualTimeDependencyLazyBean中沒有指定lazy-init,預設為false,表示是實時建立的bean,會在容器建立過程中被初始化

測試用例

LazyBeanTest中加個方法,如下:

@Testpublic void actualTimeDependencyLazyBean() {
    System.out.println("spring容器啟動中...");
    String beanXml = "classpath:/com/javacode2018/lesson001/demo11/actualTimeDependencyLazyBean.xml";
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(beanXml); //啟動spring容器
    System.out.println("spring容器啟動完畢...");}1234567

執行輸出

spring容器啟動中...ActualTimeDependencyLazyBean例項化!我是延遲初始化的bean!ActualTimeDependencyLazyBean.setLazyInitBean方法!spring容器啟動完畢...12345

從容器中可以看到,xml中定義的2個bean都在容器啟動過程中被建立好了。

有些朋友比較迷茫,lazyInitBean的lazy-init為true,怎麼也在容器啟動過程中被建立呢?

由於actualTimeDependencyLazyBean為實時初始化的bean,而這個bean在建立過程中需要用到lazyInitBean,此時容器會去查詢lazyInitBean這個bean,然後會進行初始化,所以這2個bean都在容器啟動過程中被建立的。

總結 gansu/

延遲初始化的bean無法在程式啟動過程中迅速發現bean定義的問題,第一次獲取的時候可能耗時會比較長。在實際工作中用的比較少,作為了解,以後碰到的時候會有個印象。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/30239065/viewspace-2731545/,如需轉載,請註明出處,否則將追究法律責任。

相關文章