Spring干預(yù)Bean的生命周期的方法詳解
Spring 提供了多種機(jī)制讓我們能夠在 Bean 生命周期的不同節(jié)點(diǎn)“插入”自己的邏輯,這些機(jī)制可以分為兩大類(lèi):針對(duì)單個(gè) Bean 的干預(yù)和針對(duì)所有/多個(gè) Bean 的全局干預(yù)。
一、 針對(duì)單個(gè) Bean 的干預(yù)(最常用)
這些方法讓你為一個(gè)特定的 Bean 類(lèi)定義其初始化和銷(xiāo)毀邏輯。
1. 使用 JSR-250 注解(推薦方式)
這是現(xiàn)在最優(yōu)雅、也是 Spring 官方推薦的方式。它使用 Java 的標(biāo)準(zhǔn)注解,與 Spring 框架解耦。
@PostConstruct
:在依賴注入完成之后,執(zhí)行任何自定義的初始化邏輯。可以把它看作是 Bean 的“構(gòu)造函數(shù)完成后的補(bǔ)充構(gòu)造函數(shù)”。@PreDestroy
:在容器銷(xiāo)毀 Bean 之前,執(zhí)行清理工作。
示例:
import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; @Service public class DataProcessor { @Autowired private DataSource dataSource; // 依賴會(huì)被先注入 private Connection connection; public DataProcessor() { System.out.println("1. 調(diào)用構(gòu)造函數(shù): dataSource is null? " + (dataSource == null)); } @PostConstruct public void openConnection() { System.out.println("3. 執(zhí)行 @PostConstruct: dataSource is null? " + (dataSource == null)); System.out.println("正在打開(kāi)數(shù)據(jù)庫(kù)連接..."); // 此時(shí) dataSource 已經(jīng)被注入,可以安全使用 // this.connection = dataSource.getConnection(); } public void process() { System.out.println("正在處理數(shù)據(jù)..."); } @PreDestroy public void closeConnection() { System.out.println("執(zhí)行 @PreDestroy: 正在關(guān)閉數(shù)據(jù)庫(kù)連接..."); // if (this.connection != null) this.connection.close(); } }
優(yōu)點(diǎn):代碼簡(jiǎn)潔,與 Spring 解耦,符合 Java EE 規(guī)范。
2. 實(shí)現(xiàn) Spring 的特定接口
這是 Spring 早期提供的方式,需要你的 Bean 類(lèi)實(shí)現(xiàn) Spring 框架的接口。
InitializingBean
:實(shí)現(xiàn)它的afterPropertiesSet()
方法,效果同@PostConstruct
。DisposableBean
:實(shí)現(xiàn)它的destroy()
方法,效果同@PreDestroy
。
示例:
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.DisposableBean; @Component public class LegacyService implements InitializingBean, DisposableBean { @Override public void afterPropertiesSet() throws Exception { System.out.println("執(zhí)行 InitializingBean.afterPropertiesSet()"); } @Override public void destroy() throws Exception { System.out.println("執(zhí)行 DisposableBean.destroy()"); } }
缺點(diǎn):代碼與 Spring 框架產(chǎn)生了強(qiáng)耦合,不推薦在新代碼中使用。
3. 在配置中指定 init-method 和 destroy-method
這種方式完全將生命周期邏輯的調(diào)用與 Bean 類(lèi)本身解耦。適用于你無(wú)法修改其源代碼的第三方庫(kù) Bean。
XML 配置:
<bean id="myService" class="com.example.MyService" init-method="customInit" destroy-method="customCleanup"/>
Java 配置:
@Configuration public class AppConfig { @Bean(initMethod = "customInit", destroyMethod = "customCleanup") public MyService myService() { return new MyService(); } }
優(yōu)點(diǎn):對(duì)類(lèi)代碼無(wú)侵入。
三者執(zhí)行順序:@PostConstruct
-> InitializingBean.afterPropertiesSet()
-> init-method
。
二、 針對(duì)所有/多個(gè) Bean 的全局干預(yù)(高級(jí)/框架級(jí))
這些機(jī)制是 Spring 框架的強(qiáng)大武器,允許你對(duì)容器中所有(或部分)Bean 的生命周期進(jìn)行統(tǒng)一的、批量的處理。
1. BeanPostProcessor(Bean 后置處理器)
這是最重要、最強(qiáng)大的全局干預(yù)接口。它像一個(gè)“質(zhì)檢員”,容器中每一個(gè) Bean 在初始化前后都會(huì)經(jīng)過(guò)它進(jìn)行“檢查”和“加工”。
它有兩個(gè)核心方法:
postProcessBeforeInitialization(Object bean, String beanName)
:在任何 Bean 的初始化方法(如@PostConstruct
)調(diào)用前執(zhí)行。postProcessAfterInitialization(Object bean, String beanName)
:在任何 Bean 的初始化方法調(diào)用后執(zhí)行。
Spring 的 AOP(如 @Transactional
)就是通過(guò) BeanPostProcessor
實(shí)現(xiàn)的。它在 postProcessAfterInitialization
階段檢查 Bean,如果發(fā)現(xiàn)需要代理,就返回一個(gè)代理對(duì)象來(lái)替換原始的 Bean 實(shí)例。
示例: 創(chuàng)建一個(gè)打印所有 Bean 初始化信息的后置處理器。
import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.stereotype.Component; @Component public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("質(zhì)檢員[Before]: 正在檢查 " + beanName); return bean; // 必須返回 bean,否則后續(xù)流程拿不到對(duì)象 } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("質(zhì)檢員[After]: " + beanName + " 已完成初始化。"); // 這里可以返回原始 bean,也可以返回一個(gè)包裝后的代理 bean return bean; } }
2. Aware 系列接口
Aware
接口的作用是讓 Bean 能夠“感知”并獲取到它所在的容器環(huán)境資源。這發(fā)生在依賴注入之后,自定義初始化方法之前。
BeanNameAware
:讓 Bean 知道它在容器中的 ID。BeanFactoryAware
:讓 Bean 獲取到創(chuàng)建它的BeanFactory
,從而可以編程方式地獲取其他 Bean。ApplicationContextAware
:功能同上,但獲取的是功能更強(qiáng)大的ApplicationContext
。
示例:
import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; @Component public class AwareService implements ApplicationContextAware { private ApplicationContext context; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { System.out.println("AwareService 感知到了 ApplicationContext"); this.context = applicationContext; } public void doSomething() { // 可以通過(guò) context 手動(dòng)獲取其他 bean DataProcessor processor = context.getBean(DataProcessor.class); processor.process(); } }
注意:過(guò)度使用 Aware
接口會(huì)增加代碼與 Spring 容器的耦合度,應(yīng)謹(jǐn)慎使用。
總結(jié)
干預(yù)方式 | 作用階段 | 影響范圍 | 推薦度/適用場(chǎng)景 |
---|---|---|---|
@PostConstruct / @PreDestroy | 初始化后 / 銷(xiāo)毀前 | 單個(gè) Bean | 強(qiáng)烈推薦,代碼清晰,與框架解耦。 |
InitializingBean / DisposableBean | 初始化后 / 銷(xiāo)毀前 | 單個(gè) Bean | 不推薦,與 Spring 強(qiáng)耦合,用于兼容老代碼。 |
init-method / destroy-method | 初始化后 / 銷(xiāo)毀前 | 單個(gè) Bean | 推薦,用于無(wú)法修改源碼的第三方 Bean。 |
BeanPostProcessor | 初始化前后 | 所有 Bean | 高級(jí)/框架級(jí)。用于實(shí)現(xiàn) AOP、自定義注解處理等全局邏輯。 |
Aware 接口 | 初始化前 | 單個(gè) Bean | 謹(jǐn)慎使用。當(dāng) Bean 需要主動(dòng)與容器交互時(shí)使用。 |
簡(jiǎn)單來(lái)說(shuō),作為應(yīng)用開(kāi)發(fā)者,你的首選和絕大多數(shù)情況下的選擇應(yīng)該是 @PostConstruct
和 @PreDestroy
。 如果你需要開(kāi)發(fā)框架級(jí)別的功能,那么 BeanPostProcessor
則是你的核武器。
以上就是Spring干預(yù)Bean的生命周期的方法詳解的詳細(xì)內(nèi)容,更多關(guān)于Spring干預(yù)Bean生命周期的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java畢業(yè)設(shè)計(jì)實(shí)戰(zhàn)之工作管理系統(tǒng)的實(shí)現(xiàn)
這是一個(gè)使用了java+SSM+Jsp+Mysql開(kāi)發(fā)的工作干活管理系統(tǒng),是一個(gè)畢業(yè)設(shè)計(jì)的實(shí)戰(zhàn)練習(xí),具有管理系統(tǒng)該有的所有功能,感興趣的朋友快來(lái)看看吧2022-02-02intellij idea如何配置網(wǎng)絡(luò)代理
intellij idea所在的這臺(tái)電腦本身上不了網(wǎng),要通過(guò)代理上網(wǎng),那么intellij idea怎么設(shè)置代理上網(wǎng)呢?今天通過(guò)本文給大家分享intellij idea如何配置網(wǎng)絡(luò)代理,感興趣的朋友一起看看吧2023-10-10SpringBoot實(shí)現(xiàn)聯(lián)表查詢的代碼詳解
這篇文章主要介紹了SpringBoot中如何實(shí)現(xiàn)聯(lián)表查詢,文中通過(guò)代碼示例和圖文結(jié)合的方式講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-05-05java微信掃描公眾號(hào)二維碼實(shí)現(xiàn)登陸功能
這篇文章主要為大家詳細(xì)介紹了PHP微信掃描公眾號(hào)二維碼實(shí)現(xiàn)登陸功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07springboot實(shí)現(xiàn)jar運(yùn)行復(fù)制resources文件到指定的目錄(思路詳解)
這篇文章主要介紹了springboot實(shí)現(xiàn)jar運(yùn)行復(fù)制resources文件到指定的目錄,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-04-04解決springboot 2.x集成log4j2調(diào)試日志無(wú)法關(guān)閉的問(wèn)題
這篇文章主要介紹了解決springboot 2.x集成log4j2調(diào)試日志無(wú)法關(guān)閉的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07