簡介
事件驅動模型,也即是我們通常說的觀察者設計模式的一種實現方式。
概念
定義對象間的一種一對多的依賴關係,當一個對象的狀態發生變化時,所有依賴它的對象都得到通知並自動更新。
核心組成
- 事件源:負責產生事件的對象。比如我們常見的按鈕,按鈕就是一個事件源,能夠產生“點擊”這個事件
- 事件監聽器/事件處理器:負責處理事件的對象
- 事件:或者稱爲事件對象,是事件源和事件監聽器之間的信息橋樑。是整個事件模型驅動的核心
圖片引自:https://blog.csdn.net/zrudong/article/details/78567473
Spring中事件驅動模型核心組成
- 事件對象:ApplicationContextEvent
- 事件源:AbstractApplicationContext
- 事件監聽器/事件處理器:ApplicationListener的實現類
- 事件發佈者:ApplicationEventPublisher
- 事件廣播器:ApplicationEventMulticaster
案例說明
用戶註冊功能需要實現:
- 註冊用戶
- 加積分
- 發確認郵件
- 如果是遊戲帳戶,可能贈送遊戲大禮包
- 索引用戶數據
以下案例通過使用Spring事件監聽機制實現註冊核心功能和輔助業務解耦
部分核心代碼如下:
// 事件本身
public class RegisterEvent extends ApplicationEvent {
private static final long serialVersionUID = 1L;
// User對象爲事件源
public RegisterEvent(User user) {
super(user);
}
}
-----
// 監聽用戶註冊事件,異步發送郵件
@Component
public class EmailRegisterListener implements ApplicationListener<RegisterEvent> {
// 異步執行
@Async
@Override
public void onApplicationEvent(final RegisterEvent event) {
System.out.println("註冊成功,發送確認郵件給:" + ((User)event.getSource()).getUsername());
}
}
------
// 監聽用戶註冊事件,索引用戶信息
@Component
public class
IndexRegisterListener implements ApplicationListener<RegisterEvent> {
@Async
@Override
public void onApplicationEvent(final RegisterEvent event) {
System.out.println("註冊成功,索引用戶信息:" + ((User)event.getSource()).getUsername());
}
}
---------
@Service
public class RegisterService {
@Autowired
private ApplicationContext applicationContext;
public void register(String username, String password) {
System.out.println(username + "註冊成功!");
// 發佈事件
publishRegisterEvent(new User(username, password));
}
private void publishRegisterEvent(User user) {
applicationContext.publishEvent(new RegisterEvent(user));
}
}
--------
public static void main(String[] args) {
// 通過配置文件方式初始化上下文
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring-config-register.xml");
RegisterService registerService = context.getBean(RegisterService.class);
registerService.register("long", "123");
}
-----------------------
<context:component-scan base-package="com.sishuok"/>
<!-- 任務調度器 -->
<task:scheduler id="scheduler" pool-size="10"/>
<!-- 任務執行器 -->
<task:executor id="executor" pool-size="10"/>
<!--開啓註解調度支持 @Async @Scheduled-->
<task:annotation-driven executor="executor" scheduler="scheduler" proxy-target-class="true"/>
測試輸出如下:
long註冊成功!
註冊成功,發送確認郵件給:long
註冊成功,索引用戶信息:long
註冊成功,贈送遊戲大禮包給:long
註冊成功,贈送積分給:long
案例核心
- 事件源 :User 對象
- 事件類型 :註冊事件RegisterEvent
- 事件監聽器 : IndexRegisterListener 、EmailRegisterListener
- 事件發佈: …ApplicationContext 繼承了ApplicationEventPublisher,具有事件發佈的能力
- 異步執行註解: @Async
原理分析
事件廣播器初始化
初始化方法和調用鏈如下:
// org.springframework.context.support.AbstractApplicationContext#refresh()
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
this.prepareRefresh();
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);
try {
this.postProcessBeanFactory(beanFactory);
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);
this.initMessageSource();
// 事件廣播器初始化
this.initApplicationEventMulticaster();
this.onRefresh();
// 註冊事件監聽器
this.registerListeners();
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();
} catch (BeansException var9) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
}
this.destroyBeans();
this.cancelRefresh(var9);
throw var9;
} finally {
this.resetCommonCaches();
}
}
}
初始化核心代碼
public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
// 判斷是否已加載bean:applicationEventMulticaster
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
// 已加載,applicationEventMulticaster 賦值
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isTraceEnabled()) {
logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}
else {
// 未加載,創建SimpleApplicationEventMulticaster對象並賦值
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
// 註冊bean
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isTraceEnabled()) {
logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
}
}
}
- 由
AbstractApplicationContext
類圖可知,AbstractApplicationContext
本身具有事件發佈的功能 - 事件廣播器初始化過程完成了事件創博對象的創建、註冊和注入到具有事件發佈功能的AbstractApplicationContext``中。
事件對象ApplicationContextEvent分析
先說下EventObject
對象,java中所有事件對象均爲EventObject子類。類聲明和註釋如下:
/**
* <p>
* 從中派生所有事件狀態對象的根類。
* The root class from which all event state objects shall be derived.
* <p>
* All Events are constructed with a reference to the object, the "source",
* that is logically deemed to be the object upon which the Event in question
* initially occurred upon.
*
* @since JDK1.1
*/
public class EventObject implements java.io.Serializable {
ApplicationContextEvent類圖如下:
- ApplicationContextEvent也不例外,也爲EventObject的派生類。
- 由ApplicationContextEvent派生出四個和應用上下文相關的事件
- ContextStartedEvent:容器啓動後觸發的事件。
- ContextRefreshedEvent:容器初始化或者刷新完成後觸發的事件。
- ContextStopedEvent:容器停止後觸發的事件。
- ContextClosedEvent:容器關閉後觸發的事件。
源碼如下:
public abstract class ApplicationContextEvent extends ApplicationEvent {
/**
* Create a new ContextStartedEvent.
* @param source the {@code ApplicationContext} that the event is raised for
* (must not be {@code null})
*/
public ApplicationContextEvent(ApplicationContext source) { //1
super(source);
}
/**
* Get the {@code ApplicationContext} that the event was raised for.
*/
public final ApplicationContext getApplicationContext() { //2
return (ApplicationContext) getSource();
}
}
1.
構造函數,聲明事件源對象ApplicationContext
。查看源碼可知,由ApplicationContextEvent 派生出的四個應用上下文事件,事件源對象也爲ApplicationContext
,eg:
public class ContextClosedEvent extends ApplicationContextEvent {
/**
* Creates a new ContextClosedEvent.
* @param source the {@code ApplicationContext} that has been closed
* (must not be {@code null})
*/
public ContextClosedEvent(ApplicationContext source) {
super(source);
}
}
2.
獲取事件源對象方法,即獲取應用上下文對象。
註冊監聽事件
核心代碼如下
// org.springframework.context.support.AbstractApplicationContext#registerListeners()
protected void registerListeners() {
// Register statically specified listeners first.
//首先註冊靜態指定的偵聽器。 --- Spring本身的事件監聽器
for (ApplicationListener<?> listener : getApplicationListeners()) {
// 註冊到時間廣播器中
getApplicationEventMulticaster().addApplicationListener(listener);
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let post-processors apply to them!
// 獲取ApplicationListeners實現類
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String listenerBeanName : listenerBeanNames) {
// 註冊到時間廣播器中
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
// Publish early application events now that we finally have a multicaster...
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if (earlyEventsToProcess != null) {
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}
事件發佈和事件監聽器執行
事件發佈調用鏈和源碼如下:
// org.springframework.context.support.AbstractApplicationContext#publishEvent(Object event, @Nullable ResolvableType eventType)
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
// Decorate event as an ApplicationEvent if necessary
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
// 轉成spring事件
applicationEvent = (ApplicationEvent) event;
}
else {
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
}
}
// Multicast right now if possible - or lazily once the multicaster is initialized
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
// 通過事件廣播器發佈事件
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
// Publish event via parent context as well...
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}
發佈事件方法詳情如下
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
Executor executor = getTaskExecutor();
// 獲取註冊到事件廣播器中監聽指定事件類型的事件集合,遍歷執行對應事件監聽器
//type:org.springframework.context.event.ContextRefreshedEvent
//type:com.sishuok.register.RegisterEvent
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
// taskExecutor屬性存在,異步執行監聽事件
// eg-1: 使用@Async
// eg-2:
// <bean id="applicationEventMulticaster"class="org.springframework.context.event.SimpleApplicationEventMulticaster">
// <!-- 注入任務執行器 這樣就實現了異步調用(缺點是全局的,要麼全部異步,要麼全部同步(刪除這個屬性即是同步)) -->
// <property name="taskExecutor" ref="executor"/>
// </bean>
executor.execute(() -> invokeListener(listener, event));
}
else {
// 同步執行監聽事件
invokeListener(listener, event);
}
}
}
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) {
try {
// 執行對應監聽事件
doInvokeListener(listener, event);
}
catch (Throwable err) {
errorHandler.handleError(err);
}
}
else {
doInvokeListener(listener, event);
}
}
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
// 執行對應事件
listener.onApplicationEvent(event);
}
catch (ClassCastException ex) {
String msg = ex.getMessage();
if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
// Possibly a lambda-defined listener which we could not resolve the generic event type for
// -> let's suppress the exception and just log a debug message.
Log logger = LogFactory.getLog(getClass());
if (logger.isTraceEnabled()) {
logger.trace("Non-matching event type for listener: " + listener, ex);
}
}
else {
throw ex;
}
}
}
相關
參考
- https://blog.csdn.net/zrudong/article/details/78567473
- https://www.iteye.com/blog/jinnianshilongnian-1902886
源碼
https://github.com/hdlxt/event