1 背景
在开发过程中,经常遇到发送事件来通知其他模块进行相应的业务处理;笔者实用的是spring自带的ApplicationEventPublisher
和EventListener
进行事件的发收;
但是开发时遇到一个问题:
如果事件很多,但是事件模式都差不多,就需要定义很多事件类来分别表示各种事件,例如,我们进行数据同步,每同步一条数据都要发送对应的事件,伪代码如下:
//事件类
class RegionEvent {private Region region;private OperationEnum operation;
}class UserEvent {private User user;private OperationEnum operation;
}//插入一个区域
regionDao.insert(Region region);
//发送插入区域事件
publisher.publishEvent(new RegionEvent(region, INSERT));//更新一个用户
userDao.update(User user);
//发送更新用户事件
publisher.publishEvent(new UserEvent(user, UPDATE));//区域事件监听器
@EventListener
public void onRegionEvent(RegionEvent event) {log.info("receive event: {}", event);
}//用户事件监听器
@EventListener
public void onUserEvent(UserEvent event) {log.info("receive event: {}", event);
}
此时,我们发现有太多冗余的代码,因为每插入一种类型的数据,就要对应的建立一个和该类型相关的事件类;自然而然地,我们想到可以使用泛型来简化以上逻辑。
1 泛型事件遇到的问题
我们定义一种泛型事件,来重新实现以上的逻辑,此时我们发现一个问题:发送的事件根本监听不到,伪代码如下:
class BaseEvent<T> {private T data;private OperationEnum operation;
}//发送插入区域事件
publisher.publishEvent(new BaseEvent<>(region, INSERT));
//发送更新用户事件
publisher.publishEvent(new BaseEvent<>(user, UPDATE));//区域事件监听器
@EventListener
public void onRegionEvent(BaseEvent<Region> event) {log.info("receive event: {}", event);
}//用户事件监听器
@EventListener
public void onUserEvent(BaseEvent<User> event) {log.info("receive event: {}", event);
}
这是由于spring在解析事件类型时,并没有对事件的泛型进行解析,导致在运行时所有publish的事件都被spring解析成了BaseEvent<?>事件,如果采用如下代码,则会监听到所有事件:
@EventListener
public void onUserEvent(BaseEvent<Object> event) {log.info("receive event: {}", event);
}@EventListener
public void onUserEvent(BaseEvent event) {log.info("receive event: {}", event);
}
2 解决方法
查阅了spring的文档后,发现spring已经考虑到这一点,官方文档原文如下:
In certain circumstances, this may become quite tedious if all events follow the same structure. In such a case, you can implement ResolvableTypeProvider to guide the framework beyond what the runtime environment provides. The following event shows how to do so:
大概翻译一下:
在某些情况下,如果所有事件类型都遵循相同的结构,这会是特别恶心的一件事。在这种情况下,你可以通过实现ResolvableTypeProvider接口,在运行时基于环境提供的信息来引导框架
我们基于spring提供的方法,对原有的泛型事件进行改造:
public class BaseEvent<T> implements ResolvableTypeProvider {private T data;private OperationEnum operation;@Overridepublic ResolvableType getResolvableType() {return ResolvableType.forClassWithGenerics(getClass(), ResolvableType.forClass(getData().getClass()));}
}
此时,使用上文的监听器就可以监听到对应的事件了;
3 原理
事件监听器和事件是通过事件类型进行匹配的,而事件类型的publish源码在AbstractApplicationContext类的
protected void publishEvent(Object event, @Nullable ResolvableType eventType)
方法中,如下:
ApplicationEvent applicationEvent;if (event instanceof ApplicationEvent) {//对于继承ApplicationEvent的事件,applicationEvent = (ApplicationEvent) event;}else {//对于非继承ApplicationEvent的事件,包装成PayloadApplicationEvent,//然后通过getResolvableType()获取事件类型applicationEvent = new PayloadApplicationEvent<>(this, event);if (eventType == null) {eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();}}// Multicast right now if possible - or lazily once the multicaster is initializedif (this.earlyApplicationEvents != null) {this.earlyApplicationEvents.add(applicationEvent);}else {getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);}
然后进入multicastEvent(applicationEvent, eventType)
方法:
@Overridepublic void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {//这里对于ApplicationEvent的子类事件,进行解析事件类型ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));Executor executor = getTaskExecutor();//根据上面解析到的eventType,获取对应的监听器,并依次执行回调方法for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {if (executor != null) {executor.execute(() -> invokeListener(listener, event));}else {invokeListener(listener, event);}}}
可以发现,关键在于如何解析事件类型,分别进入上文中resolveDefaultEventType()
方法和getResolvableType()
方法,可以看到解析事件类型的具体细节如下:
//针对PayloadApplicationEvent,通过下面的方法处理,可见@Overridepublic ResolvableType getResolvableType() {return ResolvableType.forClassWithGenerics(getClass(), ResolvableType.forInstance(getPayload()));}
//对于继承了ApplicationEvent的事件类private ResolvableType resolveDefaultEventType(ApplicationEvent event) {return ResolvableType.forInstance(event);}
上述两个方法用于根据事件构造事件的ResolvableType,关键代码在ResolvableType.forInstance()
:
public static ResolvableType forInstance(Object instance) {Assert.notNull(instance, "Instance must not be null");if (instance instanceof ResolvableTypeProvider) {ResolvableType type = ((ResolvableTypeProvider) instance).getResolvableType();if (type != null) {return type;}}return ResolvableType.forClass(instance.getClass());}
至此,可以看到,如果事件实现了ResolvableTypeProvider接口,则可以通过调用getResolvableType方法获取事件的带泛型类型,如果未实现该接口,则只能获取事件的原始类型,效果如下:
未实现接口的情况下:
实现接口后:
作者:TinyThing
链接:https://www.jianshu.com/p/fd0c358176b9
来源:简书