天天看點

Spring中的ApplicationListener和ContextRefreshedEvent

ApplicationListener和ContextRefreshedEvent一般都是成對出現的。最近在面試中問到了被面試者對于這兩個的用法,面試者大多數被問懵了。可見基礎知識的掌握程度。基于此本文将介紹它們的用法。

事件機制作為一種程式設計機制,在許多語言中都提供了支援。JAVA語言也不例外,java中的事件機制的參與者有3種角色:

  1. event object
  2. event source
  3. event listener

這三個角色的含義字面上很好解,它們就定義了事件機制的一個基本模型。作為一種常用的程式設計設計機制,許多開源架構的設計中都使用了事件機制。SpringFramework也不例外。

在IOC的容器的啟動過程,當所有的bean都已經處理完成之後,spring ioc容器會有一個釋出事件的動作。從 AbstractApplicationContext 的源碼中就可以看出:

1 2 3 4 5 6 7 8 9 10 11

protected

void

finishRefresh() {

// Initialize lifecycle processor for this context.

initLifecycleProcessor();

// Propagate refresh to lifecycle processor first.

getLifecycleProcessor().onRefresh();

// Publish the final event.

publishEvent(

new

ContextRefreshedEvent(

this

));

// 業餘草:www.xttblog.com

// Participate in LiveBeansView MBean, if active.

LiveBeansView.registerApplicationContext(

this

);

}

這樣,當ioc容器加載處理完相應的bean之後,也給我們提供了一個機會(先有InitializingBean,後有ApplicationListener<ContextRefreshedEvent>),可以去做一些自己想做的事。其實這也就是spring ioc容器給提供的一個擴充的地方。我們可以這樣使用這個擴充機制。

1 2

org.springframework.context.ApplicationEvent

org.springframework.context.ApplicationListener

一個最簡單的方式就是,讓我們的bean實作ApplicationListener接口,這樣當釋出事件時,spring的ioc容器就會以容器的執行個體對象作為事件源類,并從中找到事件的監聽者,此時ApplicationListener接口執行個體中的onApplicationEvent(E event)方法就會被調用,我們的邏輯代碼就會寫在此處。這樣我們的目的就達到了。但這也帶來一個思考,有人可能會想,這樣的代碼我們也可以通過實作spring的InitializingBean接口來實作啊,也會被spring容器去自動調用,但是大家應該想到,如果我們現在想做的事,是必須要等到所有的bean都被處理完成之後再進行,此時InitializingBean接口的實作就不合适了,是以需要深刻了解事件機制的應用場合。

曾經有一位同僚利用 ApplicationListener,重複加載了好幾次 xml 配置檔案。是以基礎知識一定要掌握。

下面是一個完整的例子:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36

public

class

ApplicationContextListener 

implements

ApplicationListener<ContextRefreshedEvent> {

private

static

Logger _log = LoggerFactory.getLogger(ApplicationContextListener.

class

);

@Override

public

void

onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {

// root application context

if

(

null

== contextRefreshedEvent.getApplicationContext().getParent()) {

_log.debug(

">>>>> spring初始化完畢 <<<<<"

);

// spring初始化完畢後,通過反射調用所有使用BaseService注解的initMapper方法

Map<String, Object> baseServices =

contextRefreshedEvent.getApplicationContext().getBeansWithAnnotation(BaseService.

class

);

for

(Object service : baseServices.values()) {

_log.debug(

">>>>> {}.initMapper()"

, service.getClass().getName());

try

{

Method initMapper = service.getClass().getMethod(

"initMapper"

);

initMapper.invoke(service);

catch

(Exception e) {

_log.error(

"初始化BaseService的initMapper方法異常"

, e);

e.printStackTrace();

}

}

// 系統入口初始化,業餘草:www.xttblog.com

Map<String, BaseInterface> baseInterfaceBeans =

contextRefreshedEvent.getApplicationContext().getBeansOfType(BaseInterface.

class

);

for

(Object service : baseInterfaceBeans.values()) {

_log.debug(

">>>>> {}.init()"

, service.getClass().getName());

try

{

Method init = service.getClass().getMethod(

"init"

);

init.invoke(service);

catch

(Exception e) {

_log.error(

"初始化BaseInterface的init方法異常"

, e);

e.printStackTrace();

}

}

}

}

}

以上就是ApplicationListener和ContextRefreshedEvent的相關用法。

轉載自https://www.xttblog.com/?p=2053

繼續閱讀