Spring事件驱动模型与工程中的使用

Spring事件驱动模型与工程中的使用

本文参考开涛博客链接

事件驱动模型简介

事件驱动模型也就是我们常说的观察者,或者发布-订阅模型,简单理解:

  1. 首先是一种对象间的一对多的关系;例如交通信号灯,信号灯是目标(一方),行人注视着信号灯(多方);
  2. 当目标发生改变(发布),观察者(订阅者)就可以接收到改变;
  3. 观察者如何处理(行人如何行走,是快/慢/不走,目标不管),目标不干涉,松耦合了他们之间的关系

接下来看一个用户注册的例子:
此处输入图片的描述

用户注册成功后,其他操作:
1. 添加积分
2. 发确认邮件
3. 发送游戏礼包
4. 索引用户数据
……

问题

  1. UserService与其他Service耦合严重,增删比较麻烦
  2. 有些功能可能调用第三方系统,速度较慢,需要异步支持
  3. 有些功能与主要业务不想关,比如日志,可以异步处理,快速返回

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

增加了一个Listener来解耦UserService和其他服务,即注册成功后,只需要通知相关的监听器,不需要关心他们如何处理。

这是一个典型的事件处理模型/观察者,解耦目标对象和它的依赖对象,目标只需要通知它的依赖对象,具体怎么处理,依赖对象自己决定。

上边其实也使用了DIP(依赖倒置原则),依赖于抽象,而不是具体。还有就是IOC思想,即以前主动去创建依赖的Service,现在只是被动等待别人注入进来

Spring提供的事件驱动模型/观察者抽象

首先看下Spring提供的事件驱动模型:
此处输入图片的描述
事件
具体实现:ApplicationEvent:

  • 继承自JDK的EventObject,JDK要求所有事件都继承它,并通过source得到事件源
  • 系统默认提供了如下ApplicationEvent事件实现:
    此处输入图片的描述
    只有一个ApplicationContextEvent,表示ApplicationContext容器事件,且其又有如下实现:
  • ContextStartedEvent:ApplicationContext启动后触发的事件;
  • ContextStopedEvent:ApplicationContext停止后触发的事件;
  • ContextRefreshedEvent:ApplicationContext初始化或刷新完成后触发的事件;(容器初始化完成后调用)
  • ContextClosedEvent:ApplicationContext关闭后触发的时间(如web容器关闭时自动会触发Spring容器的关闭,如果是普通java应用,需要调用ctx.registerShutdownHook();注册虚拟机关闭时的钩子才行)

目标(事件发布者)
具体代表者是:ApplicationEventPublisher及ApplicationEventMulticaster,系统提供了默认实现:
此处输入图片的描述

  1. ApplicationContext接口继承了ApplicationEventPublisher,并在AbstractApplicationContext实现了具体代码,实际执行是委托给了ApplicationEventMulticaster(可以认为是多播):
    /**
     * Publish the given event to all listeners.
     * <p>Note: Listeners get initialized after the MessageSource, to be able
     * to access it within listener implementations. Thus, MessageSource
     * implementations cannot publish events.
     * @param event the event to publish (may be application-specific or a
     * standard framework event)
     */
    public void publishEvent(ApplicationEvent event) {
        ......
        getApplicationEventMulticaster().multicastEvent(event);
        if (this.parent != null) {
            this.parent.publishEvent(event);
        }
    }

我们常用的ClassPathXmlApplicationContext、XmlWebApplicationContext等继承AbstractApplicationContext,所以自动拥有这功能。
2.ApplicationContext自动到本地容器里找一个名字为ApplicationEventMultcaster实现,如果没有自己new一个SimpleApplicationEventMulticaster,其中SimpleApplicationEventMulticaster发布事件的代码:

    public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
        ......
        for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
            Executor executor = getTaskExecutor();
            if (executor != null) {
                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        invokeListener(listener, event);
                    }
                });
            }
            else {
                invokeListener(listener, event);
            }
        }
    }

如果给它一个executor,他就可以异步支持发布事件,否则就是同步发送。

监听器
具体代表者是ApplicationListener:

  1. 继承自JDK的EventListener,JDK要求所有监听器继承它
  2. ApplicationListener接口:
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    void onApplicationEvent(E event);
}

其只提供了onApplicationEvent方法,我们需要在该方法实现内部判断事件类型来处理,也没有提供按顺序触发监听器的语义,所以Spring提供了另一个借口SmartApplicationListener:

public interface SmartApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {
    // 如果实现支持该事件类型,那么返回true
    boolean supportsEventType(Class<? extends ApplicationEvent> eventType);
    // 如果实现支持"目标"类型,那么返回true
    boolean supportsSourceType(Class<?> sourceType);
}

该接口可方便实现去判断支持的时间类型、目标类型

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值