Spring中管理Bean依賴注入以後和Bean銷燬以前的行爲

    對於Singleton做用域的Bean,Spring容器將會跟蹤它們的生命週期,容器知道什麼時候實例化結束、什麼時候銷燬。Spring能夠管理Bean在實例化結束以後和Bean銷燬以前的行爲。
java

Bean依賴關係注入以後的行爲:app

    Spring提供了兩種方式在Bean所有屬性設置成功後執行特定的行爲:
ide

  • 在Spring配置文件中使用init-method屬性:這個屬性指定某個方法在Bean所有依賴關係設置結束後自動執行。這個方法寫在Bean裏面。使用這種方法不須要將代碼與Spring耦合在一塊兒,代碼污染小,推薦使用。this

  • 讓Bean實現InitializingBean接口:該接口提供了一個afterPropertiesSet() throwsException方法,在Bean裏面實現它。spa

    Spring容器會在爲該Bean注入依賴關係後,調用該Bean實現的afterPropertiesSet方法。先看例子:
設計

public interface Animal {
    public void eatFood();
}

public class Dog implements Animal, InitializingBean {
    private Food food;
    public Dog() {
        System.out.println("Spring實例化主調Bean...Dog實例");
    }
    public void setFood(Food food) {
        System.out.println("Spring執行依賴關係注入...");
        this.food = food;
    }
    
    //Animal的方法
    @Override
    public void eatFood() {
        System.out.println(food.getName() + "真好吃");
    }
    
    //自定義的用於在Spring中配置的當Bean初始化完成時調用的方法
    public void init() {
        System.out.println("正在執行初始化:init方法...");
    }
    
    //實現InitializingBean接口中的方法
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("正在執行初始化:afterPropertiesSet方法...");
    }
}

public class Food {
    private String name;
    public void setName(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public Food() {
        System.out.println("Spring實例化依賴Bean...");
    }
}

    上面的程序中定義了一個普通的init方法,實際上這個方法名是任意的,並不必定叫init,Spring也不會對這個init方法進行任何特別的處理。只是接下來會在Spring配置文件中使用init-method屬性指定該方法是一個「生命週期」方法。
code

    增長init-method="init" 來指定init方法應在Bean的所有屬性設置結束後自動執行,若是它不實現InitializingBean接口,上面的Dog類沒有實現任何Spring接口,只是增長了一個普通的init方法。它依然是一個普通的Java文件,代碼沒有污染。下面是Spring配置文件:xml

<!-- 使用init-method="init"來指定Bean的所有屬性設置結束後執行的方法 -->
<bean id="dog" class="com.abc.Dog" init-method="init">
    <property name="food" ref="food" />
</bean>
<bean id="food" class="com.abc.Food">
    <property name="name" value="骨頭" />
</bean>

    使用主程序獲取、並調用Dog類的eatFood方法:
接口

public class Test {
    public static void main(String args[]) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Dog d = context.getBean("dog", Dog.class);
        d.eatFood();
    }
}

    輸出結果:生命週期

Spring實例化依賴Bean...Food實例
Spring實例化主調Bean...Dog實例
Spring執行依賴關係注入...
正在執行初始化:afterPropertiesSet方法...
正在執行初始化:init方法...
骨頭真好吃

    經過上面的例子能夠看出:當Spring將Food注入到Dog以後——也就是完成依賴以後,程序先調用afterPropertiesSet方法進行初始化,在調用init-method中指定的方法進行初始化。

    對於實現InitializingBean接口的Bean,無需使用init-method屬性來指定初始化方法,配置該Bean實例與普通Bean實例徹底同樣,Spring容器會自動檢測Bean實例是否實現了特定生命週期接口,並決定是否須要執行生命週期方法。Spring在爲Bean完成注入全部依賴關係後,會自動調用該Bean實現的afterProperties方法。但InitializingBean接口污染了代碼,是侵入式設計,所以不推薦使用。

    另外,從上面的執行結果能夠看出,若是既實現了InitializingBean接口又使用了init-method來指定初始化方法,那麼兩個初始化方法都會被執行,先執行接口中的afterProperties方法,再執行自定義的初始化方法。


Bean銷燬以前的行爲:

    與定製初始化行爲類似,Spring也提供了兩種方式定製在Bean銷燬以前的特定行爲:

  • 使用destroy-method屬性:指定某個方法在Bean銷燬以前被自動執行。使用這種方法,不須要將代碼與Spring的接口耦合在一塊兒,代碼污染小,推薦使用。

  • 實現DisposableBean接口:該接口提供了一個destroy() throws Exception的方法。在Bean裏面實現它,這個方法將在Bean銷燬以前被Spring調用。

    例子與前文類似,這裏不贅述。

    singleton做用域的Bean一般會隨着容器的關閉而銷燬,但問題是:ApplicationContext容器在何時關閉呢?在基於Web的ApplicationContext實現中,系統已經提供了相應的代碼保證關閉Web應用時恰當的關閉Spring容器。但對於一個非Web應用的環境下,爲了讓Spring容器優雅的關閉,並自動調用singleton上的相應回調方法,則須要在JVM裏面註冊一個關閉鉤子(shutdown hook),這樣就能夠保證Spring容器被恰當關閉,並自動執行singleton的Bean裏面的相應回調方法。例如:

public class Test {
    public static void main(String args[]) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Dog d = context.getBean("dog", Dog.class);
        d.eatFood();
        //爲Spring容器註冊關閉鉤子
        context.registerShutdownHook();
    }
}

   

    除此以外,若是Spring容器中不少Bean都須要指定特定的生命週期行爲,則能夠考慮使用<beans>的default-init-method屬性和default-destroy-method屬性,這兩個屬性的做用相似於<bean>的init-method和destroy-method屬性的做用。但因爲前兩個屬性是<beans>標籤的,所以對標籤中的全部Bean都有效。即:若是<beans>標籤中的Bean配置了default-init-method="init",那麼若是<beans>標籤中的Bean配置了init方法,則該方法會被自動調用。

    下圖顯示了Spring容器中Bean實例完整的生命週期行爲:

    須要指出的是,當Bean實現了ApplicationAware、BeanNameAware接口以後,Spring容器會在該Bean初始化完成以後——也就是init-method屬性指定的方法(若是有)以後,再來回調setApplicationContext(ApplicationContext context)和setBeanName(String beanName)方法。

相關文章
相關標籤/搜索