Spring實(shí)現(xiàn)Bean的初始化和銷毀的方式
在前面的章節(jié)當(dāng)中介紹完畢了ApplicationContext,也就是應(yīng)用上下文。我們說BeanFactory是面向開發(fā)者的,ApplicationContext才是面向使用者的,實(shí)際上也是如此。在ApplicationContest當(dāng)中我們對BeanFactory進(jìn)行了進(jìn)一步的集成,實(shí)現(xiàn)了自動化的XML文件讀取,注冊BeanFacoryPostProcess,注冊BeanPostProcess,以及提前初始化單列Bean。
以上都是屬于Bean生命周期的一部分,是通過Spring自動管理的,除此之外Spring還允許用戶自定義初始化與銷毀方法,同樣也可以通過Spring自動化調(diào)用。
一、Bean的初始化
在實(shí)現(xiàn)初始化與銷毀之前我們需要先考慮一下什么時候進(jìn)行初始化,什么時候要銷毀。
很簡單,初始化發(fā)生在創(chuàng)建Bean的時候,而銷毀發(fā)生在容器或者虛擬機(jī)關(guān)閉的時候。那么對于初始化來說他是針對對應(yīng)的Bean的,也就是說在創(chuàng)建的時候執(zhí)行,而銷毀是一個統(tǒng)一的行為,當(dāng)容器關(guān)閉時候需要對指定Bean進(jìn)行統(tǒng)一的銷毀。
那么明白了上述邏輯后我們就開始我們的邏輯實(shí)現(xiàn)
首先容器的初始化行為是正對Bean本身,實(shí)際上和BeanPostProcessor非常類似,只不過初始化的行為更具象罷了。
首先我們需要在BeanDefinition當(dāng)中添加初始化的方法名,該名稱用于用戶通過XML/注解方式自定義初始化的方法
private String initMethodName;
第二步,定義用于初始化的父類,這樣我們的Bean就可以集成該類,重寫初始化方法。
public interface InitializingBean { void afterPropertiesSet(); }
現(xiàn)在再回到AbstractAutowireCapableBeanFactory當(dāng)中的初始化方法initializeBean當(dāng)中,在這里執(zhí)行完了BeanPostProcess的前置增強(qiáng)之后,執(zhí)行用戶定義的初始化方法。
private void initializeBean(String beanName, Object bean,BeanDefinition beanDefinition) { // 執(zhí)行初始化之前的BeanPostProcessor前置處理 Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName); // 執(zhí)行初始化方法 try { invokeInitMethods(beanName , wrappedBean , beanDefinition); } catch (Exception e) { throw new BeansException("調(diào)用 bean 的 init 方法[" + beanName + "] 失敗", e); } // 執(zhí)行初始化之前的BeanPostProcessor后置 wrappedBean = applyBeanPostProcessorsAfterInitialization(bean , beanName); }
初始化的邏輯也很簡單,首先判斷用戶是否實(shí)現(xiàn)了InitializingBean這個接口,如果實(shí)現(xiàn)了則調(diào)用其重寫的afterPropertiesSet初始化方法。之后再判斷用戶是否是自己指定了自定義的初始化方法,如果指定則通過反射獲取到該方法并執(zhí)行即可。
private void invokeInitMethods(String beanName, Object bean, BeanDefinition beanDefinition) throws Exception{ if (bean instanceof InitializingBean){ ((InitializingBean) bean).afterPropertiesSet(); } // 處理用戶自定義的初始化方法 if (StrUtil.isNotEmpty(beanDefinition.getInitMethodName())){ Method initMethod = ClassUtil.getPublicMethod(bean.getClass(), beanDefinition.getInitMethodName()); if (initMethod == null) { throw new BeansException(String.format("在Bean:%s 當(dāng)中找不到名為:%s 的初始化方法",beanName,beanDefinition.getInitMethodName())); } initMethod.invoke(bean); } }
二、Bean的銷毀
Bean的銷毀相對于初始化來說最大的區(qū)別在于,初始化時正對單個Bean,銷毀是針對所有要銷毀的Bean。
也就是說,我們需要定一個Map集合存儲所有要銷毀的Bean,在容器關(guān)閉時遍歷集合,執(zhí)行其銷毀方法。
首先我們需要在BeanDefinition當(dāng)中添加銷毀的方法名,該名稱用于用戶通過XML/注解方式自定義銷毀的方法
private String destroyMethodName;
第二步,定義用于銷毀的父類,這樣我們的Bean就可以集成該類,重寫銷毀方法。
public interface DisposableBean { public void destroy(); }
然后我們需要回到ConfigurableApplicationContext,因?yàn)閷?shí)際上Bean的銷毀時應(yīng)用上下文的行為,在我們關(guān)閉ApplicationContext的時候才會執(zhí)行。
public interface ConfigurableApplicationContext extends ApplicationContext { /** * 刷新容器,重新加載并初始化所有配置和Bean */ void refresh(); /** * 關(guān)閉ApplicationContext */ void close(); void registerShutdownHook(); }
在AbstractApplicationContext實(shí)現(xiàn)這些方法,先看下面的代碼,這里可能會有一個疑問就是getBeanFactory().destroySingletons(); 通過完整的代碼我們可以知道getBeanFactory()返回的是一個DefaultListableBeanFactory的對象,那么我們也要在其父類當(dāng)中實(shí)現(xiàn)該方法。
/** * 關(guān)閉ApplicationContext */@Override public void close() { doClose(); } private void doClose() { destroyBeans(); } private void destroyBeans() { getBeanFactory().destroySingletons(); } @Override public void registerShutdownHook() { Thread shutdownHook = new Thread(this::doClose); Runtime.getRuntime().addShutdownHook(shutdownHook); }
首先我們要明確一點(diǎn),在Spring當(dāng)中所有接口都被指責(zé)化了,也就是說不同接口有著不同的功能,而Bean的銷毀也屬于單列Bean的生命周期當(dāng)中,所以我們要在DefaultSingletonBeanRegistry當(dāng)中實(shí)現(xiàn)該方法,除此之外destroySingletons該方法我們還需要在ConfigurableBeanFactory當(dāng)中定義。
其中ConfigurableBeanFactory與DefaultSingletonBeanRegistry之間都繼承于SingletonBeanRegistry,但是我們不在SingletonBeanRegistry當(dāng)中定義destroySingletons接口,二者時通過組合繼承實(shí)現(xiàn)的
public void destroySingletons(){ Set<String> beanNames = disposableBeans.keySet(); for (String beanName : beanNames) { DisposableBean disposableBean = disposableBeans.remove(beanName); // 執(zhí)行銷毀方法 disposableBean.destroy(); } }
public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, SingletonBeanRegistry { /** * 添加一個 BeanPostProcessor 到工廠中。 * BeanPostProcessor 可以在 Bean 初始化前后執(zhí)行自定義邏輯。 * * @param beanPostProcessor 要添加的 BeanPostProcessor 實(shí)例 */ void addBeanPostProcessor(BeanPostProcessor beanPostProcessor); /** * 銷毀單例bean */ void destroySingletons(); }
其實(shí)到這里所有的邏輯就結(jié)束了,下面寫一個測試
package org.qlspringframework.beans.ioc.bean; import org.qlspringframework.beans.factory.DisposableBean; import org.qlspringframework.beans.factory.InitializingBean; /** * @author jixu * @title People * @date 2025/4/7 09:54 */public class People implements DisposableBean, InitializingBean { private String name; private Integer age; private Car car; public Car getCar() { return car; } public void setCar(Car car) { this.car = car; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "People{" + "name='" + name + '\'' + ", age=" + age + ", car=" + car + '}'; } @Override public void destroy() { System.out.println("People destroy"); } @Override public void afterPropertiesSet() { System.out.println("People init"); } }
public class InitAndDestroyMethodTest { @Test public void testInitAndDestroy(){ ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml"); classPathXmlApplicationContext.registerShutdownHook(); } }
總結(jié)
以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
關(guān)于Integer.parseInt()方法的使用
這篇文章主要介紹了關(guān)于Integer.parseInt()方法的使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-11-11Spring Cloud Eureka 注冊與發(fā)現(xiàn)操作步驟詳解
這篇文章主要介紹了Spring Cloud Eureka 注冊與發(fā)現(xiàn)操作步驟詳解,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-03-03java中的Io(input與output)操作總結(jié)(一)
所謂IO,也就是Input與Output的縮寫。在java中,IO涉及的范圍比較大,這里主要討論針對文件內(nèi)容的讀寫,感興趣的朋友可以了解下2013-01-01springboot項(xiàng)目開啟https協(xié)議的項(xiàng)目實(shí)現(xiàn)
本文主要介紹了springboot項(xiàng)目開啟https協(xié)議的項(xiàng)目實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07Spring?Boot中優(yōu)雅地處理參數(shù)傳遞的技巧分享
最近一直在學(xué)習(xí)Spring Boot,今天將其中的從前臺過來的參數(shù)傳遞總結(jié)一下,下面這篇文章主要給大家介紹了關(guān)于Spring?Boot中優(yōu)雅地處理參數(shù)傳遞的技巧,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-05-05