對於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)方法。