• 领域设计:领域事件


    本文探讨如下问题:

    • 什么是领域事件
    • 领域事件的用途
    • 何时使用领域事件
    • 基于Spring事件的实现

    什么是领域事件

    EDA风格与Reactor模式一文中,我们从观察者模式聊到了EDA架构风格,然后聊了Reactor架构模式,最后以redis为例聊了Event-driven programming编程模式。

    这些内容都是技术层面的,其共性是通过事件来进行解耦,提高系统的性能、扩展性、伸缩性、可运维性和灵活性。

    而领域事件顾名思义也是通过事件来进行解耦,只是它是设计层面的技术。用于解耦领域设计中的组件:

    何时使用领域事件?

    在《领域驱动设计》的第七章,举了一个货运的例子。其中有个HandlingEvent,这就是一个事件对象。当货物被装货、卸货、密封、存放以及其他活动时就会创建一个HandlingEvent。

    领域设计:领域事件

     

    在这个例子中,是由人在货物被处理时,使用系统输入了一条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@EnableAsyncpublic 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
    @Componentpublic class HandlingListener {
     @EventListener
     @Async("eventThread")
     public void processHandlingEvent(HandlingEvent event) {
     // 处理事件
     }
    
    • 调用EventPublisher的publish方法,即可进行事件的发布
    eventPublisher.publish(new HandlingEvent(this, "move"));
    

    整体的流程如下:

    • EventPublisher添加HandlingEvent
    • HandlingListener监听到了HandlingEvent,执行对应的方法

    我们看下Spring中如何实现这个流程的。

    Spring事件实现

    领域设计:领域事件

     

    • 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
    @Overridepublic 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就是这些事件的布者。

    protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
     ......
     else {
     getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType); // 广播事件
     }
     .....
    }
    
    • 最终触发SimpleApplicationEventMulticaster的multicastEvent方法
    @Overridepublic 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源码
    • 《领域驱动设计:软件核心复杂性应对之道》
    • 《实现领域驱动设计》

  • 相关阅读:
    shell 调试
    shell中的函数参数
    shell脚本执行的区别
    《C# 语言学习笔记》——C# 简介
    【SVN】SVN使用教程总结
    SVN Unable to connect to a repository at URL问题解决
    前后端分离(三)
    前后端分离(二)
    前后端分离(一)
    【git】Git的使用
  • 原文地址:https://www.cnblogs.com/ivaneye/p/14117353.html
Copyright © 2020-2023  润新知