在《【Spring註解驅動開發】如何使用@Bean註解指定初始化和銷燬的方法?看這一篇就夠了!!》一文中,咱們講述瞭如何使用@Bean註解來指定bean初始化和銷燬的方法。具體的用法就是在@Bean註解中使用init-method屬性和destroy-method屬性來指定初始化方法和銷燬方法。除此以外,Spring中是否還提供了其餘的方式來對bean實例進行初始化和銷燬呢?java
項目工程源碼已經提交到GitHub:https://github.com/sunshinelyz/spring-annotationgit
Spring中提供了一個InitializingBean接口,InitializingBean接口爲bean提供了屬性初始化後的處理方法,它只包括afterPropertiesSet方法,凡是繼承該接口的類,在bean的屬性初始化後都會執行該方法。InitializingBean接口的源碼以下所示。github
package org.springframework.beans.factory; public interface InitializingBean { void afterPropertiesSet() throws Exception; }
根據InitializingBean接口中提供的afterPropertiesSet()方法的名字能夠推斷出:afterPropertiesSet()方法是在屬性賦好值以後調用的。那究竟是不是這樣呢?咱們來分析下afterPropertiesSet()方法的調用時機。spring
咱們定位到Spring中的org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory類下的invokeInitMethods()方法中,來查看Spring加載bean的方法。bash
題外話:不要問我爲何會是這個invokeInitMethods()方法,若是你和我同樣對Spring的源碼很是熟悉的話,你也會知道是這個invokeInitMethods()方法,哈哈哈哈!因此,小夥伴們不要只顧着使用Spring,仍是要多看看Spring的源碼啊!Spring框架中使用了大量優秀的設計模型,其代碼的編寫規範和嚴謹程度也是業界開源框架中首屈一指的,很是值得閱讀。微信
咱們來到AbstractAutowireCapableBeanFactory類下的invokeInitMethods()方法,以下所示。框架
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd) throws Throwable { //判斷該bean是否實現了實現了InitializingBean接口,若是實現了InitializingBean接口,則調用bean的afterPropertiesSet方法 boolean isInitializingBean = (bean instanceof InitializingBean); if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) { if (logger.isTraceEnabled()) { logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'"); } if (System.getSecurityManager() != null) { try { AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> { //調用afterPropertiesSet()方法 ((InitializingBean) bean).afterPropertiesSet(); return null; }, getAccessControlContext()); } catch (PrivilegedActionException pae) { throw pae.getException(); } } else { //調用afterPropertiesSet()方法 ((InitializingBean) bean).afterPropertiesSet(); } } if (mbd != null && bean.getClass() != NullBean.class) { String initMethodName = mbd.getInitMethodName(); if (StringUtils.hasLength(initMethodName) && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) && !mbd.isExternallyManagedInitMethod(initMethodName)) { //經過反射的方式調用init-method invokeCustomInitMethod(beanName, bean, mbd); } } }
分析上述代碼後,咱們能夠初步得出以下信息:ide
也就是說Spring爲bean提供了兩種初始化的方式,第一種實現InitializingBean接口,實現afterPropertiesSet方法,第二種配置文件或@Bean註解中經過init-method指定,兩種方式能夠同時使用,同時使用先調用afterPropertiesSet方法,後執行init-method指定的方法。學習
實現org.springframework.beans.factory.DisposableBean接口的bean在銷燬前,Spring將會調用DisposableBean接口的destroy()方法。咱們先來看下DisposableBean接口的源碼,以下所示。測試
package org.springframework.beans.factory; public interface DisposableBean { void destroy() throws Exception; }
能夠看到,在DisposableBean接口中只定義了一個destroy()方法。
在Bean生命週期結束前調用destory()方法作一些收尾工做,亦能夠使用destory-method。前者與Spring耦合高,使用類型強轉.方法名(),效率高。後者耦合低,使用反射,效率相對低
多例bean的生命週期不歸Spring容器來管理,這裏的DisposableBean中的方法是由Spring容器來調用的,因此若是一個多例實現了DisposableBean是沒有啥意義的,由於相應的方法根本不會被調用,固然在XML配置文件中指定了destroy方法,也是沒有意義的。因此,在多實例bean狀況下,Spring不會自動調用bean的銷燬方法。
建立一個Animal的類實現InitializingBean和DisposableBean接口,代碼以下:
package io.mykit.spring.plugins.register.bean; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.stereotype.Component; /** * @author binghe * @version 1.0.0 * @description 測試InitializingBean接口和DisposableBean接口 */ public class Animal implements InitializingBean, DisposableBean { public Animal(){ System.out.println("執行了Animal類的無參數構造方法"); } @Override public void afterPropertiesSet() throws Exception { System.out.println("執行了Animal類的初始化方法。。。。。"); } @Override public void destroy() throws Exception { System.out.println("執行了Animal類的銷燬方法。。。。。"); } }
接下來,咱們新建一個AnimalConfig類,並將Animal經過@Bean註解的方式註冊到Spring容器中,以下所示。
package io.mykit.spring.plugins.register.config; import io.mykit.spring.plugins.register.bean.Animal; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; /** * @author binghe * @version 1.0.0 * @description AnimalConfig */ @Configuration @ComponentScan("io.mykit.spring.plugins.register.bean") public class AnimalConfig { @Bean public Animal animal(){ return new Animal(); } }
接下來,咱們在BeanLifeCircleTest類中新增testBeanLifeCircle02()方法來進行測試,以下所示。
@Test public void testBeanLifeCircle02(){ //建立IOC容器 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AnimalConfig.class); System.out.println("IOC容器建立完成..."); //關閉IOC容器 context.close(); }
運行BeanLifeCircleTest類中的testBeanLifeCircle02()方法,輸出的結果信息以下所示。
執行了Animal類的無參數構造方法 執行了Animal類的初始化方法。。。。。 IOC容器建立完成... 執行了Animal類的銷燬方法。。。。。
從輸出的結果信息能夠看出:單實例bean下,IOC容器建立完成後,會自動調用bean的初始化方法;而在容器銷燬前,會自動調用bean的銷燬方法。
多實例bean的案例代碼基本與單實例bean的案例代碼相同,只不過在AnimalConfig類中,咱們在animal()方法上添加了@Scope("prototype")註解,以下所示。
package io.mykit.spring.plugins.register.config; import io.mykit.spring.plugins.register.bean.Animal; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Scope; /** * @author binghe * @version 1.0.0 * @description AnimalConfig */ @Configuration @ComponentScan("io.mykit.spring.plugins.register.bean") public class AnimalConfig { @Bean @Scope("prototype") public Animal animal(){ return new Animal(); } }
接下來,咱們在BeanLifeCircleTest類中新增testBeanLifeCircle03()方法來進行測試,以下所示。
@Test public void testBeanLifeCircle03(){ //建立IOC容器 AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AnimalConfig.class); System.out.println("IOC容器建立完成..."); System.out.println("-------"); //調用時建立對象 Object bean = ctx.getBean("animal"); System.out.println("-------"); //調用時建立對象 Object bean1 = ctx.getBean("animal"); System.out.println("-------"); //關閉IOC容器 ctx.close(); }
運行BeanLifeCircleTest類中的testBeanLifeCircle03()方法,輸出的結果信息以下所示。
IOC容器建立完成... ------- 執行了Animal類的無參數構造方法 執行了Animal類的初始化方法。。。。。 ------- 執行了Animal類的無參數構造方法 執行了Animal類的初始化方法。。。。。 -------
從輸出的結果信息中能夠看出:在多實例bean狀況下,Spring不會自動調用bean的銷燬方法。
好了,我們今天就聊到這兒吧!別忘了給個在看和轉發,讓更多的人看到,一塊兒學習一塊兒進步!!
項目工程源碼已經提交到GitHub:https://github.com/sunshinelyz/spring-annotation
若是以爲文章對你有點幫助,請微信搜索並關注「 冰河技術 」微信公衆號,跟冰河學習Spring註解驅動開發。公衆號回覆「spring註解」關鍵字,領取Spring註解驅動開發核心知識圖,讓Spring註解驅動開發再也不迷茫。