說明:所有的代碼基於SpringBoot 2.0.3版本
SpringBoot 事件架構
SpringBoot整個事件框架由四部分組成:事件(Event)、事件發佈者(Publisher)、事件分發器(dispatcher)和事件監聽器(Listener)。
事件(Event)
事件是事件發佈者和事件監聽器之間通信的載體,事件本身包括事件發佈者信息、具體事件信息,在事件的消費流程中事件是有方向的,事件只能從事件發佈者流向事件監聽器,事件不可以反向傳播。
- 事件繼承關係
SpringBoot中事件都直接或間接繼承自Java的EventObject類。EventObject類主要定義了事件源“source”,所有的事件都包含source的引用,source是最初產生事件的對象。ApplicationEvent是SpringBoot框架中的事件基類,系統預置事件和自定義事件必須繼承ApplicationEvent. - 自定義事件
通過繼承ApplicationEvent可以輕鬆自定義事件。在自定義事件中可以根據業務需要添加必要的字段來詳細的描述事件本身,方便事件監聽者獲取事件信息並根據信息做出響應。
UserEvent.class
import org.springframework.context.ApplicationEvent;
public class UserEvent extends ApplicationEvent {
private final String action;
private final String userName;
public UserEvent(Object source, String action, String userName) {
super(source);
this.action = action;
this.userName = userName;
}
public String getAction() {
return action;
}
public String getUserName() {
return userName;
}
}
- 系統事件
SpringBoot已經預置了多個與應用生命週期綁定的事件,下面將按照事件發生的先後順序簡單介紹- ApplicationStartingEvent
Spring Application啓動事件。事件產生的時機爲ApplicationListeners註冊之後,Environment或ApplicationContext可用之前,事件源爲Spring Application自身,ApplicationStartingEvent在生命週期過程中可能會被修改,請謹慎使用。 - ApplicationEnvironmentPreparedEvent
事件產生的時機爲Spring Application已經啓動,Environment第一次可用。 - ApplicationPreparedEvent
事件產生的時機爲Spring Application已經啓動,Application Context已經完全準備好但是還沒有進行刷新,在該階段已經開始加載Bean定義並且Environment已經完全可用。 - ApplicationStartedEvent
事件產生的時機爲Application Context已經完成刷新,ApplicationRunner application和CommandLineRunner調用之前。 - ApplicationReadyEvent
Spring Application已經準備好對外提供服務。 - ApplicationFailedEvent
應用啓動失敗
- ApplicationStartingEvent
事件發佈者(Publisher)
事件的發佈主體,Publisher通過事件向事件監聽器(Listener)發送信息觸發業務處理邏輯。在SpringBoot中,事件的發佈者通常爲xxxContext,在SpringBoot的框架中Context有“父子”關係,子Contenxt發佈的事件,父Context會重複發佈一次,事件會被重複消費,如果對應的處理邏輯有副作用,則會影響業務的正確性,在使用的時候一定要根據自己的業務選擇合適的Context發佈事件。
事件分發器(dispatcher)
事件分發器的主要職責是:將事件準確的分發給該事件的監聽器。通常事件分發器需要維護一個事件到監聽器之間的映射關係,以方便快速、準確的分發事件。很多框架的事件分發機制都是通過類似的原理實現,比如Zookeeper,Java NIO中的WatchService,Apache Commons FileAlterationListener接口等。SpringBoot中的事件分發器的接口爲ApplicationEventMulticaster,該接口共有7個方法,按照功能可以分爲三類:增加監聽器、移除監聽器、事件多播
- 增加監聽器
void addApplicationListener(ApplicationListener<?> listener)
void addApplicationListenerBean(String listenerBeanName)
- 移除監聽器
void removeApplicationListener(ApplicationListener<?> listener)
void removeApplicationListenerBean(String listenerBeanName)
void removeAllListeners()
- 事件多播
void multicastEvent(ApplicationEvent event)
void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType)
事件監聽器(Listener)
事件監聽器是對特定事件感興趣的對象,在收到感興趣的事件後通常執行特定的業務邏輯,從而達到業務流程或代碼架構解耦的效果。
監聽器的實現
- 實現ApplicationListener接口
接口只有一個方法用於在事件發生時執行特定的業務邏輯,一個簡單的UserEvent事件監聽器如下
import org.springframework.context.ApplicationListener;
@Component
public class UserEventService implements ApplicationListener<UserEvent> {
@Override
public void onApplicationEvent(UserEvent event) {
System.out.println(event.getAction());
}
}
一個監聽器需要進行註冊才能被dispatcher分發,在SpringBoot中註冊監聽器有兩種方式,第一種調用SpringApplication的addListeners方法,第二種利用SpringBoot的自動管理Bean的功能,將監聽器註冊爲一個普通的Bean,例子中使用第二種方法。
- 使用@EventListener註解
import org.springframework.stereotype.Component;
@Component
public class UserEventListener {
@EventListener
public void execute(UserEvent event) {
System.out.println("@"+event.getAction());
}
}
異步調用
- 自定義異步執行器Executor
自定義異步執行器只需要實現AsyncConfigurer接口即可,同時可以自定義異常處理器,AsyncConfigurer如下
public interface AsyncConfigurer {
@Nullable
default Executor getAsyncExecutor() {
return null;
}
@Nullable
default AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return null;
}
}
一個簡單的實現如下:
@Configuration
@EnableAsync
public class AsyncConfigurerImpl implements AsyncConfigurer {
private static final Logger log = LoggerFactory.getLogger(AsyncConfigurerImpl.class);
ThreadFactory factory = new ThreadFactoryBuilder().setNameFormat("AsyncEexecutor-%d").build();
@Override
public Executor getAsyncExecutor() {
Executor executor = new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(), factory);
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncUncaughtExceptionHandler() {
@Override
public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {
log.error("async exception {},method {}, args {}", throwable.getMessage(), method.getName(), objects);
}
};
}
}
必須同時添加註解@Configuration和@EnableAsync
- 使用@Async開啓方法異步調用
在類或方法上添加註解@Async即可實現方法的異步調用
import org.springframework.context.ApplicationListener;
@Component
@Async
public class UserEventService implements ApplicationListener<UserEvent> {
@Override
public void onApplicationEvent(UserEvent event) {
System.out.println(event.getAction());
}
}
總結
SpringBoot的事件機制整體非常的簡介上手容易,但是一不小心可能會出現事件重複發佈重複消費的問題給業務帶來損害,另外業務和SpringBoot緊耦合,如果業務需要遷移平臺對應的業務流程需要整改,如果不想和框架緊耦合可以考慮使用Google Guava中的EventBus或按照框架自己實現也是非常簡單的。