乐趣区

Spring中的事件监听机制

1.Spring Event

  • 在设计模式中, 观察者模式可以算得上是一种非常经典的行为设计模式, 事件 — 事件发布者 — 事件监听者是事件驱动模型在设计层面的体现.
  • Spring 容器中通过 ApplicationEvent 类和 ApplicationListener 接口来处理事件的. 如果某个 bean 实现 ApplicationListener 接口并被部署到容器中, 那么每次对应的 ApplicationEvent 被发布到容器中都会通知该bean, 这是典型的观察者模式.
  • Spring的事件默认是同步的, 即调用 #publishEvent 方法发布事件后, 它会处于阻塞状态, 直到 #onApplicationEvent 接受到事件并处理返回之后才继续执行下去, 这种单线程同步的好处是可以进行事务管理.
  • 系统默认提供的容器事件的异步发布机制参数


2.Spring 提供的事件驱动模型

  • 事件

    • 其继承自 JDKEventObject,JDK要求所有的事件都继承它, 并通过 #resource 得到事件源, 我们的 AWT 事件体系也是继承自它.
    • 系统默认提供如下 ApplicationEvent 事件:

  • 目标(事件发布者)

    • 具体代表:ApplicationEventPublisher和 ApplicationEventMulticaster, 系统提供如下实现:

- `ApplicationContext` 接口继承了 `ApplicationEventPublisher`, 并在 `abstractApplicationContext#publishEvent` 方法实现具体代码, 实际执行委托给 `ApplicationEventMulticaster#multicastEvent` 方法.
- `ApplicationContext#initApplicationEventMulticaster` 方法会自动到本地容器里找一个名为 `ApplicationEventMulticaster` 的实现, 如果没有就 `new` 一个 `SimpleApplicationEventMulticaster`.
- 可以看到如果提供一个 `executor`, 它就可以异步支持发布事件, 否则为同步发布.

  • 监听器

    • 具体代表:ApplicationListener

      • 其继承自 JDKEventListener,JDK要求所有的监听器将继承它, 比如我们的 AWT 事件体系也是继承自它.
      • ApplicationListener接口: 其只提供了 #onApplicationEvent 方法, 我们需要在该方法实现内部判断事件类型来处理, 若想提供顺序触发监听器的语义, 则可以使用另一个接口:SmartApplicationListener


3.Spring 事件驱动案例

现假设一个用户注册的案例场景. 用户注册后, 系统需要给用户发送邮件告知用户注册是否成功, 需要给用户初始化积分, 后续可能会添加其他的操作, 如再发一条手机短信等, 希望程序具有拓展性符合开闭原则.

  • 如果不使用事件驱动, 代码可能会像这个样子:

要说代码有什么问题其实也不算, 因为大多数人在开发时第一直觉都会这么写, 写同步代码. 但是这么写, 实际上并不是特别符合隐含的设计需求, 假设增加更多的注册项 Service, 我们需要修改#register 方法, 并让 UserService 注入对应的 Service. 而实际上register 并不关心这些 ” 额外 ” 的操作, 如何将这些代码抽取出去, 这时可以考虑 Event 机制.

1. 无序

  • 定义用户注册事件

    • ApplicationEvent是由 Spring 提供的所有 Event 类的基类, 这里为了简单只传递name.

  • 定义用户注册服务(事件发布者)

    • 服务交给 Spring 容器管理.
    • ApplicationEventPublishAware是由 Spring 提供的用于 Service 注入 ApplicationEventPublisher 事件发布器的接口. 使用这个接口, 我们的 Service 就拥有发布事件的能力了.
    • 用户注册后, 不再是显示调用其他的业务Service, 而是发布一个用户注册事件

  • 定义邮件服务, 积分服务, 其他服务(事件订阅者)

    • 事件订阅者的服务同样需要托管于 Spring 容器
    • ApplicationListener<E extends ApplicationEvent>接口是 Spring 提供的事件订阅者必须实现的接口, 我们一般把 Service 关心的事件作为泛型传入.
    • 事件处理:ApplicationEvent#getSource拿到事件的具体内容, 本例中为name.



2. 有序

  • 当发布多个事件的时候, 他们的顺序是无序的. 如果要控制顺序, 则监听器 Service 需要实现 Order 接口或者使用SmartApplicationEventListener.
  • 通过 Spring 事件驱动模型, 我们完成了注册服务和其他服务之间的解耦, 这也是事件驱动的最大特性之一, 若后续要新增其他操作, 只需要添加相应的事件订阅者即可.

  • supportsEventType: 用于指定支持的事件类型, 只有支持的才调用 #onApplicationEvent 方法.
  • supportsSourceType: 支持的目标类型, 只有支持的才调用 #onApplicationEvent 方法.
  • getOrder: 顺序越小优先级越高, 监听器默认优先级为7.

4.Spring 对 Event 的注解支持

  • 注解式的事件发布者:Spring4.2之后,ApplicationEventPublisher自动被注入到容器中, 不再需要显示实现 Aware 接口

  • 注解式的事件订阅者:@EventListener注解完成了 ApplicationListener<E extends ApplicationEvent> 接口的使命.


5.Spring 对异步事件机制的支持

  • java配置通过 @EnableAsync 模块注解开启异步支持, 使用 @Async 注解对需要异步的监听器进行标注.


6.Spring 对事件监听机制的分析

1.Spring 事件监听模型

  • 事件 (ApplicationEvent): 继承JDKEventObjectSpring 项目中可以继承 ApplicationEvent 来定义自己的事件
  • 事件发布者 (Application): 实现这个接口, 就可以使得Spring 组件有发布事件的能力,ApplicationContext实现了此接口, 因此, 发布事件的方式有如下几种:

    • 通过实现 ApplicationEventPublisherAware 接口, 获取注入的 ApplicationPublisher 对象来进行事件发布.
    • 通过实现 ApplicationContextAware 接口, 获取注入的 ApplicationContext 来进行事件发布.
    • 通过 @autowired 注解直接注入 ApplicationEventPublisher 或者 ApplicationContext 对象来调用 AbstractApplicationContext#publishEvent 来委托 ApplicationEventMulticaster 进行事件发布

2.Spring 事件监听流程分析

  • 通过源码分析, 在 AbstractApplicationContext 类中, 定义了针对观察者的 add,get,register 等方法, 通过这一系列的方法向 ApplicationEventMulticaster 类中维护 listener 集合 Set. 改Set 存储了该发布者所有的 Listener, 所以ApplicationContext 容器会将注入到 Spring 中的 Listener 注册到 ApplicationEventMulticaster

  • 事件通过 AbstractApplicationContext#publishEvent 方法委托给 AbstractApplicationEventMulticaster 进行事件发布, 其中 ApplicationEventMulticaster 会先尝试从 ConfigurableListableBeanFactroy 中加载配置文件的类, 如果不存在就会默认 new 一个SimpleApplicationEventMulticaster.

  • 具体的事件发布会在 AbstractApplicaitonEventMulticaster#multicastEvent 中实现, 实现流程为: 先根据 event 获取 Listener 集合, 在线程池不为空的情况下, 异步发布特定类型的事件, 否则同步发布. 在 #invokeListener 方法中最后调用 Listener#onApplicationEvnet 方法实现了事件的发布.


7. 总结

  • 以上就是关于 Spring 事件监听机制的分析, 其本质上是观察者模式的实现. 通过事件监听机制能够将我们代码逻辑进行解耦, 提高代码的拓展性, 实现开闭原则.
退出移动版