筆記簡(jiǎn)述
本學(xué)習(xí)筆記主要是介紹Spring中的事件通知是如何實(shí)現(xiàn)的,同步和異步事件通知的用法和實(shí)現(xiàn)細(xì)節(jié)以及Spring提供的常見的Event,如果實(shí)際開發(fā)中需要根據(jù)事件推送完成相應(yīng)的功能,該如何選擇Event
Spring更多可查看Spring 源碼學(xué)習(xí)
目錄
Spring Event事件通知機(jī)制 源碼學(xué)習(xí)
1、監(jiān)聽者模式
2、DEMO(同步)
3、Spring實(shí)現(xiàn)細(xì)節(jié)
4、Spring Event
4.1 ContextRefreshedEvent
4.2 ServletRequestHandledEvent
5、異步Pushlish以及DEMO
1、監(jiān)聽者模式
學(xué)習(xí)spring的事件通知機(jī)制肯定要先了解監(jiān)聽者模式(監(jiān)聽者模式和觀察者模式有什么區(qū)別?)
監(jiān)聽者模式包含了一個(gè)監(jiān)聽者Listener與之對(duì)應(yīng)的事件Event,還有一個(gè)事件發(fā)布者EventPublish,過程就是EventPublish發(fā)布一個(gè)事件,被監(jiān)聽者捕獲到,然后執(zhí)行事件相應(yīng)的方法
觀察者模式是一對(duì)多的模式,一個(gè)被觀察者Observable和多個(gè)觀察者Observer,被觀察者中存儲(chǔ)了所有的觀察者對(duì)象,當(dāng)被觀察者接收到一個(gè)外界的消息,就會(huì)遍歷廣播推算消息給所有的觀察者
例如日常生活中的訂閱報(bào)紙,報(bào)紙老板A,現(xiàn)在小明和老板打招呼說我要訂報(bào)紙(這個(gè)過程就相當(dāng)于觀察者的注冊(cè)),老板A就會(huì)拿出自己的小本本記下小明,下次小王、小芳也是類似的操作,現(xiàn)在老板A就有了三個(gè)觀察者了,然后老板會(huì)自動(dòng)的把報(bào)紙送到三位的家里,突然有一天小明說不想訂報(bào)紙了,老板就在自己的小本本上劃掉小明的名字(觀察者的取消注冊(cè)),等到送報(bào)紙的時(shí)候就不再送到小明家里。
2、DEMO(同步)
事件定義
public class EventDemo extends ApplicationEvent {
private String message;
public EventDemo(Object source, String message) {
super(source);
this.message = message;
}
public String getMessage() {
return message;
}
}
事件監(jiān)聽者
@Component
public class EventDemoListern implements ApplicationListener<EventDemo> {
@Override
public void onApplicationEvent(EventDemo event) {
System.out.println("receiver " + event.getMessage());
}
}
事件發(fā)布
@Component
public class EventDemoPublish {
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
public void publish(String message){
EventDemo demo = new EventDemo(this, message);
applicationEventPublisher.publishEvent(demo);
}
}
最后獲取到EventDemoPublish這個(gè)bean,直接publish就可以完成操作了。
3、Spring實(shí)現(xiàn)細(xì)節(jié)
在spring中由于類的細(xì)節(jié)太多,參數(shù)方法也非常的多,不太建議通篇的一個(gè)一個(gè)看,優(yōu)先關(guān)注我們關(guān)注的點(diǎn),然后打斷點(diǎn)的方式具體了解需要的參數(shù)和必備的方法等
在AbstractApplicationContext類
就有publishEvent()方法,先分析下。
protected void publishEvent(Object event, ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Publishing event in " + getDisplayName() + ": " + event);
}
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
// 如果是ApplicationEvent對(duì)象
applicationEvent = (ApplicationEvent) event;
}
else {
applicationEvent = new PayloadApplicationEvent<Object>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();
}
// 否則就包裝為PayloadApplicationEvent時(shí)間,并獲取對(duì)應(yīng)的事件類型
}
if (this.earlyApplicationEvents != null) {
//初始化時(shí)候的事件容器,默認(rèn)為null的
this.earlyApplicationEvents.add(applicationEvent);
}
else {
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
// 監(jiān)聽者容器?大概的就這個(gè)意思,推送消息給監(jiān)聽器
}
// 如果當(dāng)前命名空間還有父親節(jié)點(diǎn),也需要給父親推送該消息
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}
ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException {
if (this.applicationEventMulticaster == null) {
// 意味著肯定需要提前初始化這個(gè)監(jiān)聽器容器
throw new IllegalStateException("ApplicationEventMulticaster not initialized - " +
"call 'refresh' before multicasting events via the context: " + this);
}
return this.applicationEventMulticaster;
}
通過分析,又回到了refresh這個(gè)核心函數(shù)中
public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
// 初始化監(jiān)聽器容器
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
// 手動(dòng)實(shí)現(xiàn)了名稱為applicationEventMulticaster的bean
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
// 還必須得是ApplicationEventMulticaster類
if (logger.isDebugEnabled()) {
logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}
else {
// 否則就默認(rèn)自定義一個(gè)名稱為SimpleApplicationEventMulticaster的監(jiān)聽器容器
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
"': using default [" + this.applicationEventMulticaster + "]");
}
}
}
private final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<ApplicationListener<?>>();
// 默認(rèn)是一個(gè)空列表,而不是null
// 注冊(cè)監(jiān)聽器,并且加入到監(jiān)聽器容器中
protected void registerListeners() {
// Register statically specified listeners first.
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
// 把提前存儲(chǔ)好的監(jiān)聽器添加到監(jiān)聽器容器中
}
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
// 獲取類型是ApplicationListener的beanName集合,此處不會(huì)去實(shí)例化bean
for (String listenerBeanName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
// 初始化事件為null
if (earlyEventsToProcess != null) {
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}
這樣就很清楚了,在spring進(jìn)行refresh的時(shí)候就完成了監(jiān)聽器容器和監(jiān)聽器的初始化工作(可以很方便的注冊(cè)自己需要的監(jiān)聽器或者自定義的監(jiān)聽器容器對(duì)象),只需要獲取到容器就可以直接publish事件了。
4、Spring Event
前面已經(jīng)說了監(jiān)聽器一般和具體的某一個(gè)事件綁定的(這點(diǎn)就和觀察者模式非常不一樣了),那么就來看看spring已經(jīng)幫我們實(shí)現(xiàn)了哪些Event,具體如下圖
- ApplicationContextEvent(Context...的抽象類)
- ContextClosedEvent 生命周期關(guān)閉
- ContextRefreshedEvent refresh完成
- ContextStartedEvent 生命周期啟動(dòng)
- ContextStoppedEvent 生命周期停止
- PayloadApplicationEvent
- RequestHandledEvent
- ServletRequestHandledEvent RequestHandledEvent的子類,Spring MVC 請(qǐng)求完成之后推送的事件
下面就ContextRefreshedEvent和ServletRequestHandledEvent具體分析下如何使用以及其實(shí)現(xiàn)的原理
4.1 ContextRefreshedEvent
在refresh結(jié)束之后推送的一個(gè)事件,這個(gè)時(shí)候大部分bean的實(shí)例化已經(jīng)完成了,并且傳遞了this這個(gè)數(shù)據(jù),那么如果有需要的話,可以在這個(gè)地方實(shí)現(xiàn)對(duì)Spring上下文數(shù)據(jù)的統(tǒng)計(jì)或者監(jiān)控,簡(jiǎn)直不能爽歪歪,但是個(gè)人開發(fā)目前還沒遇到具體的使用場(chǎng)景
4.2 ServletRequestHandledEvent
Spring MVC 中的事件,直接跳到DispatcherServlet類
,后來到了FrameworkServlet 抽象類
如果可以推送事件的話,則利用web的上下文推送事件,其中包含了一個(gè)請(qǐng)求的基本信息,如果需要定制化統(tǒng)計(jì)整個(gè)HTTP請(qǐng)求的情況,完全可以通過這個(gè)事件推送實(shí)現(xiàn)
不過這里有一點(diǎn)需要注意到,還有個(gè)publishEvents字段,如果需要使用,需要設(shè)置為true,如下圖web.xml配置即可
5、異步Pushlish以及DEMO
異步推送采用的是多線程的方法,具體看SimpleApplicationEventMulticaster類
,也就是上面說的監(jiān)聽器容器
public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
// 獲取符合事件類型的監(jiān)聽器集合
Executor executor = getTaskExecutor();
if (executor != null) {
// 如果線程池不為null,則提交一個(gè)新任務(wù)交由線程池運(yùn)行
executor.execute(new Runnable() {
@Override
public void run() {
invokeListener(listener, event);
}
});
}
else {
// 否則就是同步執(zhí)行
invokeListener(listener, event);
}
}
}
protected void invokeListener(ApplicationListener listener, ApplicationEvent event) {
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) {
// 錯(cuò)誤處理器不為null
try {
listener.onApplicationEvent(event);
}
catch (Throwable err) {
errorHandler.handleError(err);
// 當(dāng)推送消息出現(xiàn)異常,利用錯(cuò)誤處理器去處理該錯(cuò)誤
}
}
else {
try {
listener.onApplicationEvent(event);
}
catch (ClassCastException ex) {
String msg = ex.getMessage();
if (msg == null || msg.startsWith(event.getClass().getName())) {
// Possibly a lambda-defined listener which we could not resolve the generic event type for
Log logger = LogFactory.getLog(getClass());
if (logger.isDebugEnabled()) {
logger.debug("Non-matching event type for listener: " + listener, ex);
}
}
else {
throw ex;
}
}
}
}
- 注入一個(gè)線程池可實(shí)現(xiàn)異步處理
- 使用errorHandler可以做到最后的事件處理的兜底方案
那么問題來了,如何注入一個(gè)線程池呢?往哪里注入呢?
只需要手動(dòng)實(shí)現(xiàn)applicationEventMulticaster的bean,并且利用Set注入的方法注入了一個(gè)線程池,線程池也需要實(shí)例化,就直接使用了spring自帶的簡(jiǎn)單異步任務(wù)線程池
從同步改成異步,代碼不需要修改,直接注入這么一個(gè)bean即可(當(dāng)然了XML配置和Config配置均可)