天天看點

Spring IOC容器初始化後,執行一些邏輯操作的幾種實作方式

Spring IOC容器初始化後,執行一些邏輯操作的幾種實作方式

解析8種實作方式
使用介紹
1.實作ApplicationContextAware,重寫setApplicationContext()方法
2.實作InitializingBean,重寫afterPropertiesSet()方法
3. 在類中的方法上,添加@PostConstruct注解。(@PreDestroy登出時使用)
4.類實作BeanPostProcessor接口,重寫postProcessBeforeInitialization()、postProcessAfterInitialization()方法
5.類實作 SmartLifecycle,重寫相關方法
6.實作ApplicationContextListener<ContextRefreshedEvent>,重寫onApplicationEvent()方法
7.類實作ApplicationRunner,重寫run()方法
8.類實作CommandLineRunner,重寫run()方法
寫在開篇
在項目的開發中,通常都會用到 Spring 來進行項目管理。在某些應用中,我們希望當Spring 容器将所有的 Bean 都初始化完成後,做一個操作(例如:将資料庫中的字典,加載到記憶體中)。那麼如何在 Spring IOC 容器初始化完成後,自動觸發某個方法來完成某些業務邏輯配置相關的操作執行呢?一共能找到的介紹有如下7種,但是這7種還各自都有些不同之處。

解析8種實作方式
類實作ApplicationContextAware,重寫setApplicationContext()方法
類實作InitializingBean,重寫afterPropertiesSet()方法
在類中的方法上,添加@PostConstruct注解。(@PreDestroy登出時使用)
類實作BeanPostProcessor,重寫postProcessBeforeInitialization()、postProcessAfterInitialization()方法
類實作 SmartLifecycle,重寫相關方法
類實作ApplicationContextListener,重寫onApplicationEvent()方法
類實作ApplicationRunner,重寫run()方法
類實作CommandLineRunner,重寫run()方法
說明:
第 1 、2 種方式,是在所有 bean 對象注冊到 IOC 容器完成之後,通過 後置處理器 或者Spring 啟動後的其他後置操作,來針對指定實作 ApplicationContextAware 或 InitializingBean 的一個bean類進行操作,達到配置全局的目的。比如說:将資料庫中的字典,加載到記憶體中 這種,并不需要每一個bean都配置的功能。(提示:說到底第1、2種方式,和本文提及的 Spring IOC 初始化後。還是有一些出入的,本文也就一并都放到這裡介紹了)

第 3 種方式,和 Spring 的啟動流程無關,隻和 Servlet 容器相關。(提示:Spring 項目在啟動 Servlet(此處以 Tomcat 為例)時,已經完成了 IOC 的相關操作,滿足本文題目在 Spring IOC 初始化後)

第 4 種方式,是對 Spring IOC 容器中每個bean對象的前置、後置增強。(提示:也和本文要求 Spring IOC 容器初始化後有出入。本文也就一并都放到這裡介紹了))

第 5 種方式,根據源碼我們了解到 LifecycleProcessor 處理器是在Spring 容器啟動最後一步 Last step: finishRefresh();這個方法中調用到的(提示:此時 Spring IOC 容器的初始化工作已經完成滿足本文題目在 Spring IOC 初始化後)

第 6 種方式,使用 ApplicationListener 監聽機制。監聽 ApplicationContextListener<ContextRefreshedEvent>的方式,Spring IOC 容器在所有的bean都初始化完成并被成功裝載後會觸發該事件(提示:監聽器方式 也可以滿足本文題目在 Spring IOC 初始化後,執行部分邏輯操作)

第 7、8 種方式,實作 ApplicationRunner、CommandLineRunner 接口的方式。都是在容器啟動完成的時執行。(提示:這種方式也可以滿足本文題目在 Spring IOC 初始化後,執行部分邏輯操作)

綜合來說: 隻有3、5、6、7、8 這五種方式,是徹底在 Spring IOC容器初始化後,執行一些邏輯操作。其它三種方式隻能說是變相滿足本文要求吧

使用介紹
1.實作ApplicationContextAware,重寫setApplicationContext()方法
實作ApplicationContextAware接口并重寫setApplicationContext()方法擷取ApplicationContext執行個體,這個要追溯到ApplicationContextAwareProcessor類。首先我們來看一下 ApplicationContextAwareProcessor 這個類,它是BeanPostProcessor(後置處理器)的一個實作類,此處附 Spring 啟動源碼來了解這個後置處理器。

此處源碼分析, 你可參考:Spring IOC 源碼解析一文學習更清晰。根據代碼,重點:在注冊後置處理器時,Spring IOC 容器已經初始化完成,是以我們可以通過這種方式,在 IOC 容器初始化後,執行一些邏輯操作。

@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();

//1.此處 obtainFreshBeanFactory()方法,來完成Spring IOC容器的 定位、加載、注冊 等操作
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);

try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);

// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);

// Register bean processors that intercept bean creation.
//2.此處用來注冊配置後置處理器(說明在配置後置處理器時,IOC容器已經建立完成)
registerBeanPostProcessors(beanFactory);

//省略部分源碼
} catch (BeansException ex) {
// Destroy already created singletons to avoid dangling resources.
destroyBeans();

// Reset 'active' flag.
cancelRefresh(ex);
} finally {
resetCommonCaches();
}
}
} 
示例:(本例使用 Spring Boot架構)

/**
* TODO 實作 ApplicationContextAware 接口
*
* @author liuzebiao
* @Date 2020-5-12 15:34
*/
@Component
public class AfterIocInitialConfig implements ApplicationContextAware {

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
//在此處執行邏輯操作
System.out.println("在此執行一些邏輯操作");
}
}


2.實作InitializingBean,重寫afterPropertiesSet()方法
(bean 配置檔案屬性 init-method 用于在bean初始化時指定執行方法,可以用來替代繼承 InitializingBean接口)

InitializingBean接口為bean提供了初始化方法的方式,它隻包括afterPropertiesSet()方法,凡是繼承該接口的類,在初始化 bean 的時候都會執行該方法。(注意:重寫了InitializingBean的 bean 在初始化時才會執行,沒重寫的 bean 是不會被執行的)

@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();

//1.此處 obtainFreshBeanFactory()方法,來完成Spring IOC容器的 定位、加載、注冊 等操作
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);

try {
// 省略部分代碼

// Instantiate all remaining (non-lazy-init) singletons.
//2.初始化所有的單例 bean(實作InitializingBean,重寫afterPropertiesSet()方法,在此處執行)
finishBeanFactoryInitialization(beanFactory);

// Last step: publish corresponding event.
finishRefresh();

//省略部分源碼
} catch (BeansException ex) {
// Destroy already created singletons to avoid dangling resources.
destroyBeans();

// Reset 'active' flag.
cancelRefresh(ex);
} finally {
resetCommonCaches();
}
}
} 


示例:(本例使用 Spring Boot架構)

/**
* TODO 實作 InitializingBean 接口
*
* @author liuzebiao
* @Date 2020-5-12 15:34
*/
@Component
public class AfterIocInitialConfig implements InitializingBean {

@Override
public void afterPropertiesSet() throws Exception {
//在此處執行邏輯操作
System.out.println("在此執行一些邏輯操作");
}
} 

3. 在類中的方法上,添加@PostConstruct注解。(@PreDestroy登出時使用)
@PostConstruct 說明:

被 @PostConstruct 修飾的方法會在伺服器加載 Servlet 的時候運作,并且隻會被伺服器調用一次,類似于 Servlet 的 init() 方法。被@PostConstruct 修飾的方法會在構造函數之後,init() 方法之前運作。

@PostConstruct 注釋用于在依賴關系注入完成之後需要執行的方法上,以執行任何初始化。此方法必須在将類放入服務之前調用。支援依賴關系注入的所有類都必須支援此注釋。即使類沒有請求注入任何資源,用@PostConstruct 注釋的方法也必須被調用。注意:隻有一個方法可以用此注釋進行注釋。

@PreDestroy 說明:

被 @PreDestroy 修飾的方法會在伺服器解除安裝 Servlet 的時候運作,并且隻會被伺服器調用一次,類似于 Servlet 的 destroy() 方法。被 @PreDestroy 修飾的方法會在 destroy() 方法之後運作,在 Servlet 被徹底解除安裝之前。

@PreDestroy 注釋作為回調通知用于各方法,以表示該執行個體正處于被容器移除的過程中。用 @PreDestroy 注釋的方法通常用于釋放它已經持有的資源,所有支援 @PostConstruct 的容器管理對象都必須支援此注釋

應用 @PostConstruct、@PreDestroy 注釋的方法必須遵守以下所有标準:

該方法不得有任何參數,除非是在EJB 攔截器(interceptor)的情況下,它可以帶有一個 InvocationContext 對象;
該方法的傳回類型必須為 void;
該方法不得抛出已檢查異常;
應用@PostConstruct 的方法可以使 pulbic、protected、package private 或 private;
除了應用程式用戶端之外,該方法不能是 static;該方法可以是 final;
示例:(本例使用 Spring Boot架構)

/**
* TODO @PostConstruct、@PreDestroy注釋的使用
*
* @author liuzebiao
* @Date 2020-5-12 15:34
*/
@Component
public class AfterIocInitialConfig{

//在伺服器加載 Servlet 的時候執行
@PostConstruct
public void doSomething(){
//在此處執行邏輯操作
System.out.println("在此執行一些邏輯操作");
}

//在伺服器解除安裝 Servlet 的時候執行
@PreDestroy
public void shutDownDoSomething(){
System.out.println("解除安裝Servlet時執行");
}
} 


@PostConstruct 注解,顯然和 Spring IOC容器的啟動沒有多大關系,它隻和 Servlet 容器的加載有關系。加載 Servlet 時, IOC 容器其實已經初始化完成。是以使用 @PostConstruct 注解,可以滿足 Spring IOC 容器初始化後,執行一些邏輯操作。


4.類實作BeanPostProcessor接口,重寫postProcessBeforeInitialization()、postProcessAfterInitialization()方法
BeanPostProcessor是 Spring IOC 容器給我們提供的一個擴充接口。注意:該接口會在 Spring IOC 容器的每一個 bean 初始化前、後做一些相關操作,記住是每一個!!!。接口聲明如下:

public interface BeanPostProcessor {
//bean初始化方法調用前被調用
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
//bean初始化方法調用後被調用
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
} 


比如說目前 IOC 容器,有200個bean對象。那麼這200個bean對象都會在初始化前、後來調用被重寫的postProcessBeforeInitialization()、postProcessAfterInitialization()方法,相當于是 bean 對象初始化的前置、後置增強。

示例:(本例使用 Spring Boot架構)

/**
* TODO @BeanPostProcessor注釋的使用
*
* @author liuzebiao
* @Date 2020-5-12 15:34
*/
@Component
public class AfterIocInitialConfig implements BeanPostProcessor {

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println(beanName);//目前要初始化的 beanName
System.out.println(beanName + ":對象初始化前執行一些操作");
return bean;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println(beanName +":對象初始化後執行一些操作");
return bean;
}
} 

5.類實作 SmartLifecycle,重寫相關方法
本子產品内容,部分摘自:https://blog.csdn.net/catoop/article/details/71274561

在使用 Spring 開發時,我們都知道,所有 bean都交給 Spring IOC 容器來統一管理,其中包括每一個 bean 的加載和初始化。有時候,我們需要在 Spring 加載和初始化所有bean後,接着執行一些任務或者啟動需要的異步服務,這樣我們可以使用 SmartLifecycle 來做到。

SmartLifecycle 是一個接口,繼承自 Lifecycle(生命周期)、Phased(如果有多個實作 Liftcycle接口的類,通過getPhase()方法來确定執行先後順序)兩個接口。當 Spring 容器加載所有 bean 并完成初始化之後,會接着回調實作該接口的類中對應的方法(start()方法)。

LifecycleProcessor 處理器,在源碼中的執行位置,如下所示:

@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();

// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);

try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);

//省略

// Last step: publish corresponding event.
// 重點:LifecycleProcessor 在此處被調用(繼續進入方法檢視)
finishRefresh();
}

catch (BeansException ex) {
//省略
}

finally {
resetCommonCaches();
}
}
}


protected void finishRefresh() {

// Initialize lifecycle processor for this context.
// 重點1:為目前context初始化 LifecycleProcessor()
initLifecycleProcessor();

// Propagate refresh to lifecycle processor first.
// 重點2:調用 lifecycleProcessor 的 onRefresh() 方法(如果我們定義了一個類,實作了 lifecycle 相關接口,則會在此處調用自定義的 onRefresh() 方法)
getLifecycleProcessor().onRefresh();

// Publish the final event.
// 事件機制釋出(在下一種實作方式 ApplicationContextListener 中會涉及到)
publishEvent(new ContextRefreshedEvent(this));

// Participate in LiveBeansView MBean, if active.
LiveBeansView.registerApplicationContext(this);
} 


如果IOC容器沒有 lifecycleProcessor,則會使用 DefaultLifecycleProcessor 類,此處附DefaultLifecycleProcessor 類源碼(省略部分源碼)

public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactoryAware {

private volatile long timeoutPerShutdownPhase = 30000;

private volatile boolean running;

/**
* Specify the maximum time allotted in milliseconds for the shutdown of
* any phase (group of SmartLifecycle beans with the same 'phase' value).
* <p>The default value is 30 seconds.
*/
public void setTimeoutPerShutdownPhase(long timeoutPerShutdownPhase) {
this.timeoutPerShutdownPhase = timeoutPerShutdownPhase;
}

/**
* Start all registered beans that implement {@link Lifecycle} and are <i>not</i>
* already running. Any bean that implements {@link SmartLifecycle} will be
* started within its 'phase', and all phases will be ordered from lowest to
* highest value. All beans that do not implement {@link SmartLifecycle} will be
* started in the default phase 0. A bean declared as a dependency of another bean
* will be started before the dependent bean regardless of the declared phase.
*/
@Override
public void start() {
startBeans(false);
this.running = true;
}

/**
* Stop all registered beans that implement {@link Lifecycle} and <i>are</i>
* currently running. Any bean that implements {@link SmartLifecycle} will be
* stopped within its 'phase', and all phases will be ordered from highest to
* lowest value. All beans that do not implement {@link SmartLifecycle} will be
* stopped in the default phase 0. A bean declared as dependent on another bean
* will be stopped before the dependency bean regardless of the declared phase.
*/
@Override
public void stop() {
stopBeans();
this.running = false;
}

@Override
public void onRefresh() {
startBeans(true);
this.running = true;
}

@Override
public void onClose() {
stopBeans();
this.running = false;
}

@Override
public boolean isRunning() {
return this.running;
}
} 


示例:(本例使用 Spring Boot架構)

/**
* TODO SmartLifecycle 接口的使用
*
* @author liuzebiao
* @Date 2020-5-12 10:36
*/
@Component
public class AfterIocInitialConfig implements SmartLifecycle, ApplicationContextAware {


private boolean isRunning = false;

private ApplicationContext context = null;

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
}

/**
* 1. 我們主要在該方法中啟動任務或者其他異步服務,比如開啟MQ接收消息
* 2. 當上下文被重新整理(所有對象已被執行個體化和初始化之後)時,将調用該方法,預設生命周期處理器将檢查每個SmartLifecycle對象的isAutoStartup()方法傳回的布爾值。
* 如果為“true”,則該方法會被調用,而不是等待顯式調用自己的start()方法。
*/
@Override
public void start() {
System.out.println("執行start()方法");
// 執行完其他業務後,可以修改 isRunning = true
isRunning = true;
}

/**
* 如果工程中有多個實作接口SmartLifecycle的類,則這些類的start的執行順序按getPhase方法傳回值從小到大執行。
* 例如:1比2先執行,-1比0先執行。 stop方法的執行順序則相反,getPhase傳回值較大類的stop方法先被調用,小的後被調用。
*/
@Override
public int getPhase() {
// 預設為0
return 0;
}

/**
* 根據該方法的傳回值決定是否執行start方法。
* 傳回true時start方法會被自動執行,傳回false則不會。
*/
@Override
public boolean isAutoStartup() {
// return false;
return true;
}

/**
* SmartLifecycle子類才有的方法,當isRunning方法傳回true時,該方法才會被調用。
*/
/*@Override
public void stop(Runnable callback) {
System.out.println("stop(Runnable)");

// 如果你讓isRunning傳回true,需要執行stop這個方法,那麼就不要忘記調用callback.run()。
// 否則在你程式退出時,Spring的DefaultLifecycleProcessor會認為你這個TestSmartLifecycle沒有stop完成,程式會一直卡着結束不了,等待一定時間(預設逾時時間30秒)後才會自動結束。
// PS:如果你想修改這個預設逾時時間,可以按下面思路做,當然下面代碼是springmvc配置檔案形式的參考,在SpringBoot中自然不是配置xml來完成,這裡隻是提供一種思路。
// <bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor">
// <!-- timeout value in milliseconds -->
// <property name="timeoutPerShutdownPhase" value="10000"/>
// </bean>
callback.run();

isRunning = false;
}*/


/**
* 1. 隻有該方法傳回false時,start方法才會被執行。
* 2. 隻有該方法傳回true時,stop(Runnable callback)或stop()方法才會被執行。
*/
@Override
public boolean isRunning() {
// 預設傳回false
return isRunning;
}

/**
* 接口Lifecycle的子類的方法,隻有非SmartLifecycle的子類才會執行該方法。<br/>
* 1. 該方法隻對直接實作接口Lifecycle的類才起作用,對實作SmartLifecycle接口的類無效。<br/>
* 2. 方法stop()和方法stop(Runnable callback)的差別隻在于,後者是SmartLifecycle子類的專屬。
*/
@Override
public void stop() {
System.out.println("執行stop()方法");

isRunning = false;
}
} 


6.實作ApplicationContextListener<ContextRefreshedEvent>,重寫onApplicationEvent()方法
ApplicationContextListener 事件機制是觀察者設計模式的實作,通過ApplicationEvent類和ApplicationListener接口,可以實作ApplicationContext事件處理;如果容器中存在 ApplicationListener 的Bean,當 ApplicationContext 調用 publishEvent 方法時,對應的Bean會被觸發。publishEvent() 方法,同剛剛介紹的 Lifecycle 類似,也是在 finishRefresh() 方法中調用的。如下所示:

protected void finishRefresh() {

// Initialize lifecycle processor for this context.
initLifecycleProcessor();

// Propagate refresh to lifecycle processor first.
getLifecycleProcessor().onRefresh();

// Publish the final event.
// 重點:事件機制釋出(在下一種實作方式 ApplicationContextListener 中會涉及到)
publishEvent(new ContextRefreshedEvent(this));

// Participate in LiveBeansView MBean, if active.
LiveBeansView.registerApplicationContext(this);
} 


其中 Spring 有一些内置的事件,當完成某種操作時會發出某些事件動作。比如監聽ContextRefreshedEvent事件,當所有的bean都初始化完成并被成功裝載後會觸發該事件,實作ApplicationListener<ContextRefreshedEvent>接口可以收到監聽動作,然後可以寫自己的邏輯。同樣事件我們可以自定義、監聽也可以自定義,完全根據自己的業務邏輯來處理。

Spring内置事件    描述
ContextRefreshedEvent    ApplicationContext 被初始化或重新整理時,該事件被觸發。這也可以在 ConfigurableApplicationContext接口中使用 refresh() 方法來發生。此處的初始化是指:所有的Bean被成功裝載,後處理Bean被檢測并激活,所有Singleton Bean 被預執行個體化,ApplicationContext容器已就緒可用
ContextStartedEvent    當使用 ConfigurableApplicationContext (ApplicationContext子接口)接口中的 start() 方法啟動 ApplicationContext 時,該事件被釋出。你可以調查你的資料庫,或者你可以在接受到這個事件後重新開機任何停止的應用程式。
ContextStoppedEvent    當使用 ConfigurableApplicationContext 接口中的 stop() 停止 ApplicationContext 時,釋出這個事件。你可以在接受到這個事件後做必要的清理的工作。
ContextClosedEvent    當使用 ConfigurableApplicationContext 接口中的 close() 方法關閉 ApplicationContext 時,該事件被釋出。一個已關閉的上下文到達生命周期末端;它不能被重新整理或重新開機。
RequestHandledEvent    這是一個 web-specific 事件,告訴所有 bean HTTP 請求已經被服務。隻能應用于使用DispatcherServlet的Web應用。在使用Spring作為前端的MVC控制器時,當Spring處理使用者請求結束後,系統會自動觸發該事件。
示例:(本例使用 Spring Boot架構)

/**
* TODO ApplicationListener 接口的使用
*
* @author liuzebiao
* @Date 2020-5-12 15:34
*/
@Component
public class AfterIocInitialConfig implements ApplicationListener<ContextRefreshedEvent> {

@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
System.out.println("監聽到所有的bean都已經初始化完成");
}
} 

附錄:

我們可以監聽 ServletWebServerInitializedEvent 類,來監聽 Servlet 是否初始化完成;
也可以監聽 ApplicationReadyEvent 類,來監聽應用(項目)是否啟動完成。 etc.
7.類實作ApplicationRunner,重寫run()方法
在開發中可能會有這樣的情景。需要在容器啟動的時候執行一些内容。比如讀取配置檔案,資料庫連接配接之類的。Spring 項目中,我們可以通過實作 ApplicationRunner接口,重寫run() 方法。它的執行時機為容器啟動完成的時候。

示例:(本例使用 Spring Boot架構)

/**
* TODO ApplicationRunner 接口的使用
*
* @author liuzebiao
* @Date 2020-5-12 15:34
*/
@Component
public class AfterIocInitialConfig implements ApplicationRunner {

@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("目前項目已經啟動完成");
}
} 


8.類實作CommandLineRunner,重寫run()方法
SpringBoot 中額外給我們提供了一個接口,這個接口是 CommandLineRunner。它和 ApplicationRunner功能一樣,也都需要重寫run()方法。該接口也是在容器啟動完成的時候執行。

它們的不同之處在于:(其它使用都相同)

ApplicationRunner 可以在 Spring、SpringBoot 項目中使用;CommandLineRunner 隻能在 Spring Boot 中使用
ApplicationRunner 中 run() 方法的參數為 ApplicationArguments,而 CommandLineRunner 中 run() 方法的參數為 String數組。
示例:(本例使用 Spring Boot架構)

/**
* TODO CommandLineRunner 接口的使用
*
* @author liuzebiao
* @Date 2020-5-12 15:34
*/
@Component
public class AfterIocInitialConfig implements CommandLineRunner {

@Override
public void run(String... args) throws Exception {
System.out.println("目前項目已經啟動完成");
}
}      

         在某些應用中,我們希望,當spring 容器将所有的bean都初始化完成後,做一個操作(例如:将資料庫中的字典,加載到記憶體中),這時我們可以實作一個接口,如下:

  1. package com.yk.test.executor.processor  
  2. public class InstantiationTracingBeanPostProcessor implements ApplicationListener<ContextRefreshedEvent> {  
  3.     @Resource
  4.     private MsEmailService msgEmailService;
  5.     @Override  
  6.     public void onApplicationEvent(ContextRefreshedEvent event) {  
  7.       //需要執行的邏輯代碼,當spring容器初始化完成後就會執行該方法。  
  8.  }  
  9. }  

        同時執行個體化該bean:    

  1. <!--      當Spring容器啟動完成後執行下面的這個Bean -->  
  2.     <bean class="com.yk.test.executor.processor.InstantiationTracingBeanPostProcessor"/>  

        但是這個時候,會存在一個問題,在web 項目中(spring mvc),系統會存在兩個容器,一個是root application context ,另一個就是我們自己的 projectName-servlet  context(作為root application context的子容器)。

這種情況下,就會造成onApplicationEvent方法被執行兩次。為了避免上面提到的問題,我們可以隻在root application context初始化完成後調用邏輯代碼,其他的容器的初始化完成,則不做任何處理,修改後代碼

如下:

  1. @Override  
  2.   public void onApplicationEvent(ContextRefreshedEvent event) {  
  3.     if(event.getApplicationContext().getParent() == null){//root application context 沒有parent,他就是老大.  
  4.          //需要執行的邏輯代碼,當spring容器初始化完成後就會執行該方法。  
  5.     }  
  6.   }