最近在采用Spring事件框架编写业务开发时,由于Spring事件框架的功能局限性,无法很好的满足我们的开发需求。于是在阅读Spring事件源码后,对Spring事件框架针对性地进行了拓展,下面将详述该过程。
问题简述:
甲系统需要监听很多事件,例如A事件,B事件,C事件,对其进行相似的业务处理。
复制代码
常规解决方案1:
在甲系统中编写很多个监听器,分别监听A、B、C事件,并进行处理!例如:
public class SystemListener {
@EventListener
public void listenAEvent(AEvent event) {
//处理A事件
}
@EventListener
public void listenBEvent(BEvent event) {
//处理B事件
}
@EventListener
public void listenCEvent(CEvent event) {
//处理C事件
}
}
复制代码
这种方法看似简单清晰,但其实有一个致命的缺陷,就是当相关的事件非常多时(达到300个以上),这时需要写非常多类似的监听方法,代码的可读性和可维护性将特别差。
常规解决方案2:
在甲系统中提供一个事件基类,A、B、C多个事件都必须基础这个事件基类。这样就可以一个监听方法同时监听到A、B、C多个事件。例如:
@EventListener
public void listenEvent(BaseEvent event) {
//处理事件
System.out.println("handle event:" + event.getClass());
}
复制代码
经测试,这种方法确实是可以使用一个监听方法同时监听多个事件。但实际落地问题很大,因为甲系统强行要求其他模块抛出的事件继承自己提供的基类事件是不可行的。不仅造成强耦合,而且使得这些事件无法继承其他基类,这是不现实的。有人问,那甲系统为什么不可以直接监听ApplicationEvent呢???确实可以,但是这样使得甲系统监听到很多不愿意关注的事件,徒耗计算量,明显是不合理的。
常规解决方案3:
@EventListener注解标识多个事件,进行事件监听,例如:
@EventListener({AEvent.class, BEvent.class, CEvent.class})
public void listenEvent() {
System.out.println("handle event:" + event.getClass());
}
复制代码
经测试,这种方法是可行的,但有以下两个致命缺陷:
● 方法不能带参数,需要事件数据时不可行
● 事件太多,枚举起来太可怕
常规解决方案4:
我们还可以使用@EventListener的condition字段指定我们的监听条件,condition的值以SPEL表达。
@EventListener(condition = "")
public void listenEvent() {
System.out.println("handle event:" );
}
复制代码
经测试,这种方法是可行的,但缺陷是SPEL不够灵活,面对复杂的业务逻辑判断时,无法以SPEL方式表达。
框架拓展思路:
设计目标:
● 监听条件可以插入自定义逻辑,根据业务内容灵活判断
● 拓展插件可以以配置方式开启
效果预览:
@EventListener
@ListenerStrategy(strategy = TestEventListenerStrategy.class)
public void listenEvent(BaseEvent baseEvent) {
System.out.println("handle event:" );
}
复制代码
如上所示,我们只需在监听方法上加上一个@ListenerStrategy,加上我们自定义的策略实现,即可灵活地注入我们自己的监听逻辑。@ListenerStrategy定义如下:
@Retention(RetentionPolicy.RUNTIME)
@Target(value = ElementType.METHOD)
@Documented
public @interface ListenerStrategy {
Class<? extends EventListenerStrategy> strategy();
}
复制代码
策略接口定义如下:
/**
* 监听策略接口,开发者需要自定义策略,可以提供此接口实现,并将其注入Spring
*/
public interface EventListenerStrategy {
/**
* 判断此事件是否符合监听策略
* @param event
* @return
*/
boolean match(Class<? extends ApplicationEvent> event);
}
复制代码
我们尝试自定义策略只监听AEvent及其子类:
@Component
public class TestEventListenerStrategy implements EventListenerStrategy {
@Override
public boolean match(Class<? extends ApplicationEvent> event){
return AEvent.class.isAssignableFrom(event);
}
}
复制代码
效果:
单元测试完全满足我们的预期。
实现思路:
➢ 替换掉容器中默认的ApplicationEventMulticaster实例
翻阅Spring源码,我们发现,事件发布主要由ApplicationEventMulticaster的实现类进行。
仔细看,Spring的实现是如果容器中有名为applicationEventMulticaster的组件时,则使用容器中的组件,否则将默认实例化一个SimpleApplicationEventMulticaster使用。具体代码如下:
于是,我们自定义一个ApplicationEventMulticaster的实现,重写其中的监听匹配方法,并将其注入容器,便可开启我们的拓展组件功能了。代码实现如下:
/**
* 为实现策略监听的自定义事件分发器
*/
public class StrategyApplicationEventMulticaster extends SimpleApplicationEventMulticaster implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
protected boolean supportsEvent(ApplicationListener<?> listener, ResolvableType eventType, Class<?> sourceType) {
boolean superJudge = super.supportsEvent(listener, eventType, sourceType);
if (!superJudge) {
return false;
}
//判断是否开启了策略监听,若开启,则需要进行处理
if (listener instanceof ApplicationListenerMethodAdapter) {
Field field = ReflectionUtils.findField(ApplicationListenerMethodAdapter.class, "method");
if (field == null) {
throw new IllegalStateException("handle event error, can not find invokeMethod of listener");
}
ReflectionUtils.makeAccessible(field);
Method originMethod = (Method) ReflectionUtils.getField(field, listener);
field.setAccessible(false);
ListenerStrategy annotation = originMethod.getAnnotation(ListenerStrategy.class);
//如果没有策略监听则不管
if (annotation == null) {
return true;
}
Class<? extends EventListenerStrategy> strategyType = annotation.strategy();
EventListenerStrategy listenerStrategy = applicationContext.getBean(strategyType);
Class<?> event = eventType.resolve();
Class<? extends ApplicationEvent> applicationEvent = (Class<? extends ApplicationEvent>) event;
//不符合监听策略,则不监听
if (!listenerStrategy.match(applicationEvent)) {
return false;
}
}
return true;
}
}
复制代码
重写以上的supportsEvent方法即可改成我们想要的事件匹配逻辑。将其注入Spring容器中即可生效。当然,上面的实现有一定的缺陷,这个方法将在检测事件时反复调用,使用反射性能较低。面对这种情况,我们做一个预处理缓存即可。代码实现如下:
/**
* 为实现策略监听的自定义事件分发器
*/
public class StrategyApplicationEventMulticaster extends SimpleApplicationEventMulticaster implements ApplicationContextAware, SmartInitializingSingleton {
private ApplicationContext applicationContext;
private ConcurrentHashMap<ApplicationListener, ListenerStrategy> strategyMap= new ConcurrentHashMap<>();
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
protected boolean supportsEvent(ApplicationListener<?> listener, ResolvableType eventType, Class<?> sourceType) {
boolean superJudge = super.supportsEvent(listener, eventType, sourceType);
if (!superJudge) {
return false;
}
//判断是否开启了策略监听,若开启,则需要进行处理
if (listener instanceof ApplicationListenerMethodAdapter) {
ListenerStrategy annotation = strategyMap.get(listener);
if (annotation == null) {
return true;
}
Class<? extends EventListenerStrategy> strategyType = annotation.strategy();
EventListenerStrategy listenerStrategy = applicationContext.getBean(strategyType);
if (listenerStrategy == null) {
throw new IllegalStateException("no such eventListenerStrategy instance in applicationContext container; " + strategyType.getName());
}
Class<?> event = eventType.resolve();
Class<? extends ApplicationEvent> applicationEvent = (Class<? extends ApplicationEvent>) event;
//不符合监听策略,则不监听
if (!listenerStrategy.match(applicationEvent)) {
return false;
}
}
return true;
}
@Override
public void afterSingletonsInstantiated() {
for (ApplicationListener<?> listener : getApplicationListeners()) {
if (listener instanceof ApplicationListenerMethodAdapter) {
Field field = ReflectionUtils.findField(ApplicationListenerMethodAdapter.class, "method");
if (field == null) {
throw new IllegalStateException("handle event error, can not find invokeMethod of listener");
}
ReflectionUtils.makeAccessible(field);
Method originMethod = (Method) ReflectionUtils.getField(field, listener);
field.setAccessible(false);
ListenerStrategy annotation = originMethod.getAnnotation(ListenerStrategy.class);
//如果没有策略监听则不管
if (annotation == null) {
continue;
}
strategyMap.putIfAbsent(listener, annotation);
}
}
}
}
复制代码
以上内容皆为原创,引用请注明出处!有关不正确之处,欢迎各位同学批评指正!
作者:小超6379
链接:https://juejin.cn/post/6955783021224001567
来源:掘金