本文探讨如下问题:
-
什么是领域事件
-
领域事件的用途
-
何时使用领域事件
-
基于Spring事件的实现
什么是领域事件
在EDA风格与Reactor模式一文中,我们从观察者模式聊到了EDA架构风格,然后聊了Reactor架构模式,最后以redis为例聊了Event-driven programming编程模式。
这些内容都是技术层面的,其共性是通过事件来进行解耦,提高系统的性能、扩展性、伸缩性、可运维性和灵活性。
而领域事件顾名思义也是通过事件来进行解耦,只是它是设计层面的技术。用于解耦领域设计中的组件:
-
聚合与聚合之间的解耦。聚合请参考
-
限界上下文之间的解耦。限界上小文后面再聊
何时使用领域事件?
在《领域驱动设计》的第七章,举了一个货运的例子。其中有个HandlingEvent,这就是一个事件对象。当货物被装货、卸货、密封、存放以及其他活动时就会创建一个HandlingEvent。
](https://p3-tt.byteimg.com/origin/pgc-image/a0fd500cbd8a478d887fc0db03ff2ca7?from=pc))
在这个例子中,是由人在货物被处理时,使用系统输入了一条HandlingEvent记录下来而已!而如果当发生了一个HandlingEvent后,需要后续的处理(比如发送通知给相关人员),该怎么办呢?这就是领域事件所要解决的问题!
在《领域驱动设计》中并没有提到领域事件,领域事件是其后出现的。
实际上,当出现类似如下关键词汇时,都可以考虑使用领域事件:
-
当......
-
如果发生......
-
当......的时候,请通知我
-
发生......时
下面通过Spring的事件来实现上例中的具体流程。
Spring事件实例代码
- 新建HandlingEvent类,继承ApplicationEvent,表示这是一个事件
public class HandlingEvent extends ApplicationEvent {
private String actionName;
public VisitEvent(Object source,String actionName) {
super(source);
this.actionName = actionName;
}
}
-
编写事件发布类EventPublisher,实现ApplicationEventPublisherAware,用于注入ApplicationEventPublisher,通过publishEvent方法来发布事件
-
Spring默认是同步事件,如果需要异步事件,需要添加EnableAsync注解
@Service
@EnableAsync
public class EventPublisher implements ApplicationEventPublisherAware {
private ApplicationEventPublisher publisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher)
{
this.publisher = applicationEventPublisher;
}
public void publish(ApplicationEvent event) {
publisher.publishEvent(event);
}
}
-
编写监听类,只需要在监听方法上添加eventListener注解,参数为需要监听的事件类即可
-
如果需要异步处理,添加Async注解
-
可以通过Async的value属性来选择线程池。
推荐选择自定义线程池,默认线程池没有限制线程数量,可能导致OOM
@Component
public class HandlingListener {
@EventListener@Async("eventThread")
public void processHandlingEvent(HandlingEvent event)
{// 处理事件}
- 调用EventPublisher的publish方法,即可进行事件的发布
eventPublisher.publish(new HandlingEvent(this, "move"));
整体的流程如下:
-
EventPublisher添加HandlingEvent
-
HandlingListener监听到了HandlingEvent,执行对应的方法
我们看下Spring中如何实现这个流程的。
Spring事件实现
](https://p1-tt.byteimg.com/origin/pgc-image/e460ab9325de4dca8233d8b916cfc932?from=pc))
- Spring启动时,EventListenerMethodProcessor会将标识了EventListener注解的方法添加到Context中。这里就是HandlingListener中的processHandlingEvent方法
for (Method method : annotatedMethods.keySet()) {
for (EventListenerFactory factory : factories)
{
if (factory.supportsMethod(method))
{
Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
ApplicationListener<?> applicationListener =factory.createApplicationListener(beanName, targetType, methodToUse);
if (applicationListener instanceof ApplicationListenerMethodAdapter) {
((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
}
context.addApplicationListener(applicationListener);break;
}
}
}
- AbstractApplicationContext实现了ApplicationContext接口,其中维护了扫描到的ApplicationListener
@Override
public void addApplicationListener(ApplicationListener<?> listener)
{
Assert.notNull(listener, "ApplicationListener must not be null");
if (this.applicationEventMulticaster != null)
{
this.applicationEventMulticaster.addApplicationListener(listener);
}
this.applicationListeners.add(listener);
}
-
在EventPublisher发布事件时,触发ApplicationEventPublisher的publishEvent方法
-
ApplicationContext接口继承了ApplicationEventPublisher接口,最终触发AbstractApplicationContext的publishEvent方法,将事件广播出去
ApplicationContext接口为什么要继承ApplicationEventPublisher接口?因为Spring中定义了几个默认的事件ContextRefreshedEvent,ContextStartedEvent,ContextStoppedEvent,ContextClosedEvent,RequestHandledEvent,而ApplicationContext就是这些事件的布者。
less复制代码protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
......
else {
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType); // 广播事件
}
.....
}
- 最终触发SimpleApplicationEventMulticaster的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);
}
}
}
- 获取ApplicationListener,然后使用Executor去执行listener
参考资料
-
Spring源码
-
《领域驱动设计:软件核心复杂性应对之道》
-
《实现领域驱动设计》
作者:一瑜一琂
链接:https://juejin.cn/post/6895181027820601357
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
评论 (0)