一.簡(jiǎn)介
當(dāng)一個(gè)事件發(fā)生時(shí),我們希望另外一個(gè)服務(wù)能夠感知到這個(gè)事件.發(fā)布這個(gè)事件的對(duì)象不需要關(guān)心哪個(gè)對(duì)象會(huì)監(jiān)聽(tīng)這個(gè)事件并去處理它,當(dāng)然監(jiān)聽(tīng)者也不需要知道發(fā)布這個(gè)事件的對(duì)象是誰(shuí),我只關(guān)心如何去處理這些事件.這樣業(yè)務(wù)之間的邏輯會(huì)更加解耦提高可擴(kuò)展性.
從Spring框架的一開(kāi)始蔑歌,應(yīng)用程序事件(ApplicationEvent)就成為松散耦合組件交換信息的一種手段,其實(shí)現(xiàn)原理為觀察者模式.
二.基本用法
1.定義事件
所有的事件均需繼承自 ApplicationEvent.ApplicationEvent是個(gè)抽象類(lèi),這樣我們可以在實(shí)現(xiàn)類(lèi)中定義一些用到的數(shù)據(jù).繼承 ApplicationEvent 后必須要重載構(gòu)造方法,參數(shù)可以自定義.第一個(gè) Object source 表示事件的發(fā)布體,發(fā)布事件時(shí)可以用 this.String data 表示自定義的數(shù)據(jù),這里也可以用其他復(fù)雜對(duì)象來(lái)代替.
public class NormalEvent extends ApplicationEvent {
@Getter
private String data;
public NormalEvent(Object source, String data) {
super(source);
this.data = data;
}
}
2.發(fā)布事件
發(fā)布事件需要我們拿到 ApplicationContext 對(duì)象,在這個(gè)對(duì)象中調(diào)用publishEvent 方法.拿到 ApplicationContext 對(duì)象方法由很多.
這里先使用最簡(jiǎn)單的構(gòu)造方法注入方式.
注意:發(fā)布事件時(shí),第一個(gè)參數(shù)為 this.
@RestController
public class EventController1 {
private final ApplicationContext applicationContext;
public EventController1(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@GetMapping("/get1")
public String getString1() {
applicationContext.publishEvent(new NormalEvent(this, "NormalEvent"));
return "get1";
}
}
3.事件監(jiān)聽(tīng)器
注冊(cè)事件監(jiān)聽(tīng)器方式也很多,但是用 @EventListener 方式是最簡(jiǎn)單的,在任何方法public void 方法中加上注解都可以. @EventListener 將方法標(biāo)記為應(yīng)用程序事件的偵聽(tīng)器的注釋姜性。
方法的入口參數(shù)即為事件,當(dāng)入口參數(shù)為 Object 時(shí),任何事件均會(huì)監(jiān)聽(tīng)到.
@Slf4j
@Component
public class NormalEventHandler {
/**
* @param event 任何類(lèi)型的事件都會(huì)接受
*/
@EventListener
public void event(Object event) {
log.error("NormalEventHandler(Object) 接收到事件:{}", event.getClass());
}
/**
* @param event 會(huì)接受 NormalEvent 類(lèi)型的事件,順序不定
*/
@EventListener
public void event1(NormalEvent event) {
log.error("NormalEventHandler_event1(NormalEvent) 接收到事件:{}", event.getClass());
}
/**
* @param event 會(huì)接受 會(huì)接受 NormalEvent 類(lèi)型的事件,順序不定
*/
@EventListener
public void event2(NormalEvent event) {
log.error("NormalEventHandler_event2(NormalEvent) 接收到事件:{}", event.getClass());
}
}
三.SmartEvent
二中介紹的時(shí)間監(jiān)聽(tīng)器之間都是毫無(wú)關(guān)系的.經(jīng)常發(fā)布的事件需要被多個(gè)監(jiān)聽(tīng)器監(jiān)聽(tīng)到,并且需要一定的順序去執(zhí)行這些監(jiān)聽(tīng)器.
1.定義事件
這個(gè)和普通的事件沒(méi)有任何區(qū)別.
public class SmartEvent extends ApplicationEvent {
@Getter
private String data;
public SmartEvent(Object source, String data) {
super(source);
this.data = data;
}
}
2.發(fā)布事件
這個(gè)也和普通的事件發(fā)布沒(méi)有任何區(qū)別
@RestController
public class EventController1 {
private final ApplicationContext applicationContext;
public EventController1(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@GetMapping("/get3")
public String getString3() {
applicationContext.publishEvent(new SmartEvent(this, "SmartEvent"));
return "get3";
}
}
3.Smart事件監(jiān)聽(tīng)器
這時(shí)時(shí)間監(jiān)聽(tīng)器需要 實(shí)現(xiàn) SmartApplicationListener 接口.SmartApplicationListener 是標(biāo)準(zhǔn)的 ApplicationListener 以及Ordered 接口的擴(kuò)展變體.暴露更多的元數(shù)據(jù),如支持的時(shí)間以及源類(lèi)型.
需要實(shí)現(xiàn) supportsEventType , supportsSourceType , getOrder 三個(gè)方法.
- supportsEventType:表示支持的事件類(lèi)型,,可以為多個(gè),只要該方法返回 true 即可.
- supportsSourceType:表示支持的源類(lèi)型,即是從哪個(gè)類(lèi)中發(fā)布的這個(gè)事件.
- getOrder:監(jiān)聽(tīng)器執(zhí)行的優(yōu)先級(jí).數(shù)字越大優(yōu)先級(jí)越低.
@Slf4j
@Component
public class SmartEventHandler1 implements SmartApplicationListener {
@Override
public boolean supportsEventType(Class<? extends ApplicationEvent> aClass) {
// 只接收 SmartEvent 類(lèi)型的事件,只有 SmartEvent 類(lèi)型的事件才會(huì)執(zhí)行下面的邏輯
return aClass == SmartEvent.class;
}
@Override
public boolean supportsSourceType(Class<?> aClass) {
// //只有在 EventController1 內(nèi)發(fā)布的 SmartEvent 事件時(shí)才會(huì)執(zhí)行下面邏輯
return aClass == EventController1.class;
}
@Override
public void onApplicationEvent(ApplicationEvent applicationEvent) {
SmartEvent event = (SmartEvent) applicationEvent;
log.error("SmartEventHandler1:{}", event.getData());
}
@Override
public int getOrder() {
return 1;
}
}
四.事件反饋
經(jīng)常給系統(tǒng)一個(gè)輸入就希望系統(tǒng)能夠給一個(gè)反饋,如接口調(diào)用.因?yàn)榘l(fā)布者不關(guān)系監(jiān)聽(tīng)者,監(jiān)聽(tīng)者也不關(guān)心發(fā)布者,所以正常情況下沒(méi)有辦法給接口一個(gè)返回,這時(shí)只能使用統(tǒng)一異常處理.
1.異常事件
public class NormalExceptionEvent extends ApplicationEvent {
@Getter
private String data;
public NormalExceptionEvent(Object source, String data) {
super(source);
this.data = data;
}
}
2.發(fā)布異常事件
@RestController
public class EventController1 {
private final ApplicationContext applicationContext;
public EventController1(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@GetMapping("/get2")
public String getString2() {
applicationContext.publishEvent(new NormalExceptionEvent(this, "NormalExceptionEvent"));
return "get2";
}
}
3.定義異常類(lèi)
public class EventException extends RuntimeException {
@Getter
@Setter
private String mess;
public EventException(String mess) {
this.mess = mess;
}
}
4.定義異常處理類(lèi)
@RestControllerAdvice
public class EventExceptionHandler {
@ExceptionHandler(value = EventException.class)
public String handler1(EventException exception) {
return "error:" + exception.getMess();
}
}
5.拋出異常
@Slf4j
@Component
public class NormalExceptionEventHandler {
/**
* @param event 會(huì)接受 NormalEvent 類(lèi)型的事件,順序不定
*/
@EventListener
public void event1(NormalExceptionEvent event) {
log.error("NormalEventHandler_event1(NormalEvent) 接收到事件:{}", event.getClass());
throw new EventException("NormalExceptionEvent!");
}
}
注:經(jīng)測(cè)試,普通的事件和 SmartEvent 在 SpringBoot2.0.6 中均支持統(tǒng)一異常處理.但是之前好像有遇到有些 SpringBoot 版本不支持的情況.