1、參考
2涨享、前言
公司項(xiàng)目中有個(gè)發(fā)送站內(nèi)信的功能狡相,是使用ApplicationEvent
和ApplicationListener
實(shí)現(xiàn)的定铜,對(duì)于這一塊,之前未接觸過,在該項(xiàng)目中蔚万,其具體功能如下:
-
ApplicationContext
發(fā)送ApplicationEvent
類型的站內(nèi)信 -
ApplicationListener
接收到站內(nèi)信信息驴剔,進(jìn)行入庫(kù)處理届巩,createTime
直接寫死成了new Date()
- 用戶界面讀取入庫(kù)后的數(shù)據(jù)旱易,根據(jù)
createTime
倒序呈現(xiàn)給用戶
產(chǎn)品經(jīng)理要求站內(nèi)信需要按發(fā)送的順序呈現(xiàn)給用戶,那問題來(lái)了:
ApplicationListener
接收到Event
信息時(shí)建丧,是否是有序的呢排龄?
3、Demo
基于spring boot
創(chuàng)建一個(gè)簡(jiǎn)易的ApplicationEvent
使用demo翎朱,基于該demo執(zhí)行并發(fā)測(cè)試橄维,初步驗(yàn)證是否為有序接收
3.1尺铣、CustomEvent
自定義Event
,繼承ApplicationEvent
争舞,并實(shí)現(xiàn)構(gòu)造方法
public class CustomEvent extends ApplicationEvent {
/**
* Create a new ApplicationEvent.
*
* @param source the object on which the event initially occurred (never {@code null})
*/
public CustomEvent(Object source) {
super(source);
}
}
3.2凛忿、CustomPublisher
Event
事件發(fā)布者,此處定義publish
方法作為發(fā)送入口竞川,提供給controller
調(diào)用店溢,方便測(cè)試
@Component
public class CustomPublisher {
@Autowired
private ApplicationContext applicationContext;
public void publish(CustomEvent customEvent) {
applicationContext.publishEvent(customEvent);
}
}
除了使用注解的方式,spring也提供了實(shí)現(xiàn)
ApplicationEventPublisherAware
接口的方式委乌,具體可戳參考鏈接
3.3床牧、CustomListener
Event事件監(jiān)聽器,接收Event事件并對(duì)其進(jìn)行處理遭贸,此處進(jìn)行簡(jiǎn)單的打印
@Component
public class CustomListener {
@EventListener
public void listen(CustomEvent customEvent) {
System.out.println(customEvent.getSource());
}
}
除了使用注解的方式戈咳,spring也提供了
ApplicationListener
接口的方式,具體可戳參考鏈接
3.4革砸、測(cè)試
@RestController
public class RestController {
@Autowired
private CustomPublisher customPublisher;
int i = 0;
@GetMapping(value = "/publish")
public void publish() {
CustomEvent customEvent = new CustomEvent(i++);
customPublisher.publish(customEvent);
}
}
使用postman
的runner
功能除秀,創(chuàng)建如下runner
糯累,進(jìn)行并發(fā)測(cè)試:
從結(jié)果來(lái)看算利,事件接收是有序的,查閱官網(wǎng)泳姐,可以看到這么一句話:
You can register as many event listeners as you wish, but note that, by default, event listeners receive events synchronously. This means that the publishEvent() method blocks until all listeners have finished processing the event.
大致意思是:event listeners
默認(rèn)上同步接收events
效拭,這意味著,publicEvent()
方法將會(huì)阻塞直至所有的listeners
處理完event
為止胖秒。
4缎患、源碼
追溯源碼,查看ApplicationEvent
的發(fā)布和接收功能是如何實(shí)現(xiàn)的阎肝,首先挤渔,程序入口位于ApplicationContext#publishEvent
:
default void publishEvent(ApplicationEvent event) {
publishEvent((Object) event);
}
方法內(nèi)調(diào)用了重載的publicEvent
,而該重載publishEvent
是一個(gè)接口方法风题,有以下實(shí)現(xiàn):
顯然判导,具體的實(shí)現(xiàn)類是AbstractApplicationContext#publishEvent
:
@Override
public void publishEvent(Object event) {
publishEvent(event, null);
}
繼續(xù)調(diào)用重載方法publishEvent(Object event, @Nullable ResolvableType eventType)
:
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Publishing event in " + getDisplayName() + ": " + event);
}
// Decorate event as an ApplicationEvent if necessary
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
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);
}
}
}
邏輯可以總結(jié)為以下三點(diǎn):
- 將入?yún)?code>event包裝成
applicationEvent
- 交由多播器進(jìn)行消息發(fā)布(
multicastEvent
) - 若父級(jí)上下文不為空,則父級(jí)上下文同樣需要進(jìn)行消息發(fā)布
顯然沛硅,核心方法在于multicastEvent
:
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
這里使用了線程池眼刃,調(diào)用了invokeListener
:
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);
}
}
繼續(xù)調(diào)用doInvokeListener
:
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
listener.onApplicationEvent(event);
}
catch (ClassCastException ex) {
// ……
}
}
onApplicationEvent
,是ApplicationListener
接口類中的方法摇肌,在3.3節(jié)中擂红,有提及自定義監(jiān)聽器可以繼承ApplicationListener
接口并實(shí)現(xiàn)onApplicationEvent
方法進(jìn)行監(jiān)聽,若是使用該種方法围小,則事件的發(fā)送和接收處理的源碼就已經(jīng)追溯完畢昵骤,不過本文中使用的是注解树碱,故需要繼續(xù)跟蹤下去,onApplicationEvent
的實(shí)現(xiàn)類頗多:
通過debug驗(yàn)證涉茧,
ApplicationListenerMethodAdapter
才是注解類listener
的具體實(shí)現(xiàn):
public void onApplicationEvent(ApplicationEvent event) {
processEvent(event);
}
查看processEvent
:
public void processEvent(ApplicationEvent event) {
Object[] args = resolveArguments(event);
if (shouldHandle(event, args)) {
Object result = doInvoke(args);
if (result != null) {
handleResult(result);
}
else {
logger.trace("No result object given - no result to handle");
}
}
}
可以看到核心在于doInvoke
:
protected Object doInvoke(Object... args) {
Object bean = getTargetBean();
ReflectionUtils.makeAccessible(this.method);
try {
return this.method.invoke(bean, args);
}
// ……
}
這里使用了反射赴恨,調(diào)用method
方法,那么伴栓,method
是什么時(shí)候賦值伦连,具體值又是什么呢,對(duì)method
進(jìn)行Find Usages
钳垮,可以看到:
public ApplicationListenerMethodAdapter(String beanName, Class<?> targetClass, Method method) {
this.beanName = beanName;
this.method = BridgeMethodResolver.findBridgedMethod(method);
// ……
}
method
是在ApplicationListenerMethodAdapter
構(gòu)造方法中賦值的惑淳,debug,可以看到具體值為:
public void com.kungyu.rabbitmq.CustomListener.listen(com.kungyu.rabbitmq.CustomEvent)
也就是我們自定義的監(jiān)聽方法饺窿,那么歧焦,ApplicationListenerMethodAdapter
構(gòu)造方法何時(shí)被調(diào)用,繼續(xù)Find Usages
:
public class DefaultEventListenerFactory implements EventListenerFactory, Ordered {
// ……
@Override
public ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method) {
return new ApplicationListenerMethodAdapter(beanName, type, method);
}
}
對(duì)createApplicationListener
進(jìn)行Find Usages
:
protected void processBean(
final List<EventListenerFactory> factories, final String beanName, final Class<?> targetType) {
// ……
ApplicationListener<?> applicationListener =
factory.createApplicationListener(beanName, targetType, methodToUse);
// ……
}
對(duì)processBean
進(jìn)行Find Usages
肚医,可以看到是在afterSingletonsInstantiated
進(jìn)行調(diào)用的绢馍,而afterSingletonsInstantiated
是類實(shí)例化相關(guān)的內(nèi)容,此處不予展開
5肠套、總結(jié)
從測(cè)試結(jié)果來(lái)看舰涌,ApplicationListener
是有序接收消息的,從源碼來(lái)看你稚,其實(shí)現(xiàn)方式也不算過于復(fù)雜瓷耙,是對(duì)知識(shí)點(diǎn)的一個(gè)很好的補(bǔ)充。另外刁赖,在中間件橫行的時(shí)代搁痛,業(yè)務(wù)角度上貌似這一功能沒怎么被使用,出于解耦的目的宇弛,應(yīng)該都會(huì)用MQ實(shí)現(xiàn)該功能了鸡典。