侧边栏壁纸
博主头像
落叶人生博主等级

走进秋风,寻找秋天的落叶

  • 累计撰写 130562 篇文章
  • 累计创建 28 个标签
  • 累计收到 9 条评论
标签搜索

目 录CONTENT

文章目录

使用事件驱动进行代码解耦-Spring篇

2023-12-12 星期二 / 0 评论 / 0 点赞 / 37 阅读 / 14634 字

什么是事件驱动模型 事件驱动模型也就是我们常说的观察者,或者发布-订阅模型;理解它的几个关键点: 首先是一种对象间的一对多的依赖关系; 当一个对象的状态发生变化时,观察者(订阅者)都得到通知并做

什么是事件驱动模型

事件驱动模型也就是我们常说的观察者,或者发布-订阅模型;理解它的几个关键点:

  1. 首先是一种对象间的一对多的依赖关系;
  2. 当一个对象的状态发生变化时,观察者(订阅者)都得到通知并做相应的处理;
  3. 观察者如何处理,目标无需干涉,松散耦合了它们之间的关系。

常见问题

比如说订单状态变化的时候,能够通知到邮件服务,短信服务,积分变化等等; 如果你是个新手,想象一下你去实现这个业务的代码怎么去实现?肯定是一个OrderService里面引入积分Service,短信Service,邮件Service,还有很多很多Service,可能还要调用第三方接口。是不是发现问题所在了,Service耦合严重,又是还会出现循环引用的问题,代码超长,以至于不方便维护。

从如上例子可以看出,应该使用一个观察者来解耦这些Service之间的依赖关系,如图:

图中增加了一个Listener来解耦OrderService和其他Service,即注册成功后,只需要通知相关的监听器,不需要关心它们如何处理,处理起来非常容易。这就是一个典型的事件处理模型-观察者模式,解耦目标对象和它的依赖对象,目标只需要通知它的依赖对象,具体怎么处理,依赖对象自己决定。比如是异步还是同步,延迟还是非延迟等。
其实上边其实也使用了DIP(依赖倒置原则),依赖于抽象,而不是具体。
还是就是使用了IOC思想,即以前主动去创建它依赖的Service,现在只是被动等待别人注册进来。
主要目的是:松散耦合对象间的一对多的依赖关系。

常用事件驱动模型

  1. 设计模式里面的观察者模式

  2. JDK观察者模式

  3. JavaBean事件驱动

  4. spring事件驱动

  5. ......

JavaBean事件驱动

JavaBean规范提供了一种监听属性变化的事件驱动模型,提供操作JavaBean属性的类PropertyChangeSupport,PropertyEditorSupport以及PropertyChangeListener支持,PropertyEditorSupport就是目标,而PropertyChangeListener就是监听器。

Spring提供的事件

Spring提供的事件驱动模型/观察者抽象:首先看一下Spring提供的事件驱动模型体系图,


 

具体代表者是ApplicationEvent:
1、其继承自JDK的EventObject,JDK要求所有事件将继承它,并通过source得到事件源,比如我们的AWT事件体系也是继承自它;
2、系统默认提供了如下ApplicationEvent事件实现:

只有一个ApplicationContextEvent,表示Spring容器事件,且其又有如下实现:

  • ContextStartedEvent:Spring容器启动后触发的事件;
  • ContextStoppedEvent:Spring容器停止后触发的事件;
  • ContextRefreshedEvent:Spring容器初始化或刷新完成后触发的事件;
  • ContextClosedEvent:Spring容器关闭后触发的事件;

注:org.springframework.context.support.AbstractApplicationContext抽象类实现了LifeCycle的start和stop回调并发布ContextStartedEvent和ContextStoppedEvent事件。

事件发布

具体代表者是:ApplicationEventPublisher及ApplicationEventMulticaster,系统默认提供了如下实现:

1、ApplicationContext接口继承了ApplicationEventPublisher,并在AbstractApplicationContext实现了具体代码,实际执行是委托给ApplicationEventMulticaster(可以认为是多播):

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {    //省略部分代码    if (event instanceof ApplicationEvent) {        applicationEvent = (ApplicationEvent)event;    } else {        applicationEvent = new PayloadApplicationEvent(this, event);        if (eventType == null) {            eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();        }    }    if (this.earlyApplicationEvents != null) {        this.earlyApplicationEvents.add(applicationEvent);    } else {        this.getApplicationEventMulticaster().multicastEvent((ApplicationEvent)applicationEvent, eventType);    }    if (this.parent != null) {        if (this.parent instanceof AbstractApplicationContext) {            ((AbstractApplicationContext)this.parent).publishEvent(event, eventType);        } else {            this.parent.publishEvent(event);        }    }}

我们常用的ApplicationContext都继承自AbstractApplicationContext,如ClassPathXmlApplicationContext、XmlWebApplicationContext等,所以自动拥有这个功能。

2、ApplicationContext自动到本地容器里找一个名字为”applicationEventMulticaster“的ApplicationEventMulticaster实现,如果没有自己new一个SimpleApplicationEventMulticaster,代码如下:

protected void initApplicationEventMulticaster() {    ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();    if (beanFactory.containsLocalBean("applicationEventMulticaster")) {        this.applicationEventMulticaster = (ApplicationEventMulticaster)beanFactory.getBean("applicationEventMulticaster", ApplicationEventMulticaster.class);        if (this.logger.isDebugEnabled()) {            this.logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");        }    } else {        this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);        beanFactory.registerSingleton("applicationEventMulticaster", this.applicationEventMulticaster);        if (this.logger.isDebugEnabled()) {            this.logger.debug("Unable to locate ApplicationEventMulticaster with name 'applicationEventMulticaster': using default [" + this.applicationEventMulticaster + "]");        }    }}

其中SimpleApplicationEventMulticaster发布事件的代码如下:

public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {    ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);    Iterator var4 = this.getApplicationListeners(event, type).iterator();    while(var4.hasNext()) {        ApplicationListener<?> listener = (ApplicationListener)var4.next();        Executor executor = this.getTaskExecutor();        if (executor != null) {            executor.execute(() -> {                this.invokeListener(listener, event);            });        } else {            this.invokeListener(listener, event);        }    }}

可以看到如果给它一个executor(java.util.concurrent.Executor),它就可以异步支持发布事件了,否则就是同步发送。所以我们发送事件只需要通过ApplicationContext.publishEvent即可,没必要再创建自己的实现了。除非有必要。 

监听器

具体代表者是:ApplicationListener。其继承自JDK的EventListener,JDK要求所有监听器将继承它。

@FunctionalInterfacepublic interface ApplicationListener<E extends ApplicationEvent> extends EventListener {    void onApplicationEvent(E var1);}

它只提供了onApplicationEvent方法,我们需要在该方法实现内部判断事件类型来处理,或者指定某一个事件类型(泛型,实现ApplicationListener),并没有提供按顺序触发监听器的语义,所以Spring提供了另一个接口SmartApplicationListener:

public interface Ordered {    int getOrder();}public interface SmartApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {    boolean supportsEventType(Class<? extends ApplicationEvent> var1);    boolean supportsSourceType(@Nullable Class<?> var1);}

该接口支持判断的事件类型、目标类型,及执行顺序。

另外,google guava事件机制使用介绍见文章:使用事件驱动进行代码解耦-Guava篇

Spring事件机制示例

本示例模拟订单生成与更新时会发送短信与站内信简单业务场景demo

1. 自定义一些接口以及POJO

/** * 业务事件发布者 * @author dongsilin * @version 1.0 * @date 2019/1/24 */public interface BizEventPublisher {     /**      * 发布同步事件      * @param eventData      */     void publishEvent(Object eventData);     /**      * 发布异步事件      * @param eventData      */     void publishEventAsync(Object eventData);}/** * 业务事件发布者 * @author dongsilin * @version 1.0 * @date 2019/1/24 */public interface BizEventPublisher {     /**      * 发布同步事件      * @param eventData      */     void publishEvent(Object eventData);     /**      * 发布异步事件      * @param eventData      */     void publishEventAsync(Object eventData);}/** * 业务事件类型 * @author dongsilin * @version 1.0 * @date 2019/1/24 */public enum BizEventType {    ORDER_CREATE("订单-创建"),    ORDER_UPDATE("订单-修改"),    ;    String describe;    BizEventType(String describe) {        this.describe = describe;    }}/** * @author dongsilin * @version 1.0 * @date 2019/1/24 * 测试订单类 */@Datapublic class Order {    private long orderId;    private long userId;    public Order(long orderId, long userId) {        this.setOrderId(orderId);        this.setUserId(userId);    }}

2. 自定义业务事件,包含事件通用数据属性

/** * @author dongsilin * @version 1.0 * @date 2019/1/24 * 自定义应用业务事件 * @param <S> */@Datapublic class BizEvent<S> extends ApplicationEvent implements EventExecutor<S> {    private BizEventType eventType;    private S data;    private BizEvent(BizEventType eventType, S data) {        super(eventType);        this.eventType = eventType;        this.data = data;    }    public static<S> BizEvent of(BizEventType eventType, S data) {        return new BizEvent(eventType, data);    }    @Override    public void executeEvent(Consumer<S> executor) {        executor.accept(data);    }}

3. 业务事件发布配置管理

/** * @author dongsilin * @version 1.0 * @date 2019/1/24 * 业务事件发布配置管理 */@Slf4j@Componentpublic class BizEventPublisherConfiguration implements BizEventPublisher{    /** 注入事件发布管理器,用于发布自定义业务事件 */    @Autowired    private ApplicationEventPublisher eventPublisher;    /** 异步事件执行线程池 */    private ExecutorService executor = new ThreadPoolExecutor(        Runtime.getRuntime().availableProcessors(),        Runtime.getRuntime().availableProcessors() * 2,        5L,        TimeUnit.SECONDS,        new LinkedBlockingQueue<>(),        new ThreadFactoryBuilder().setNameFormat("spring-event-executor-pool-%d").build()    );    @Override    public void publishEvent(Object eventData) {        eventPublisher.publishEvent(eventData);    }    @Override    public void publishEventAsync(Object eventData) {        executor.submit(() -> eventPublisher.publishEvent(eventData));    }}

4. 业务事件监听配置管理

/** * @author dongsilin * @version 1.0 * @date 2019/1/24 * 业务事件监听配置管理 */@Slf4j@Componentpublic class BizEventListenerConfiguration implements ApplicationListener<BizEvent> {    @Autowired    private TestMsgService msgService;    /**     * 收到事件执行     * @param bizEvent     */    @Override    public void onApplicationEvent(BizEvent bizEvent) {        switch (bizEvent.getEventType()) {            // 订单创建,发送短信,.......            case ORDER_CREATE: bizEvent.executeEvent((data) -> {                msgService.sendPhoneMsg((Order) data);            });break;            // 订单修改,站内信提醒,.......            case ORDER_UPDATE: bizEvent.executeEvent((data) -> {                msgService.sendWebMsg((Order) data);            });break;            default: bizEvent.executeEvent((data) -> {                log.info("onApplicationEvent bizEvent.data = {}", data);            });        }    }}

5. 定义两个测试service,TestOrderService 和 TestMsgService

/** * @author dongsilin * @version 1.0 * @date 2019/1/24 */@Slf4j@Servicepublic class TestOrderService {    @Autowired    private BizEventPublisher bizEventPublisher;    public void create(Order order) {        ......        log.info("TestOrderService create order = {}", order);        bizEventPublisher.publishEvent(BizEvent.of(BizEventType.ORDER_CREATE, order));    }    public void update(Order order) {        ......        log.info("TestOrderService update order = {}", order);        bizEventPublisher.publishEventAsync(BizEvent.of(BizEventType.ORDER_UPDATE, order));    }}/** * @author dongsilin * @version 1.0 * @date 2019/1/24 */@Slf4j@Servicepublic class TestMsgService {    public void sendPhoneMsg(Order order) {        ......        log.info("TestMsgService sendPhoneMsg order = {}", order);    }    public void sendWebMsg(Order order) {        ......        log.info("TestMsgService sendWebMsg order = {}", order);    }}

此处TestOrderService 中并没有强制依赖注入TestMsgService,而是通过发布事件的方式将事件数据发布到事件管理中心,由事件管理者来统一管理事件处理方式,解决了各个业务service严重耦合的场景,实现软件开发中的“高内聚-低耦合”原则。

通过如上,大体了解了Spring的事件机制,可以使用该机制非常简单的完成事件流程。

广告 广告

评论区