## 第十七章:Spring事件 ### Java事件/监听器编程模型 - 设计模式-观察者模式扩展 - 可观者对象(消息发送者)- `java.util.Observable` - 观察者 - `java.util.Observer` - 标准化接口 - 事件对象 - `java.util.EventObject` - 事件监听器 - `java.util.EventListener` ### 面向接口的事件/监听器设计模式 事件/监听器场景举例 | Java 技术规范 | 事件接口 | 监听器接口 | | --------------- | --------------------------------------- | ------------------------------------------ | | JavaBeans | `java.beans.PropertyChangeEvent` | `java.beans.PropertyChangeListener` | | Java AWT | `java.awt.event.MouseEvent` | `java.awt.event.MouseListener` | | Java Swing | `javax.swing.event.MenuEvent` | `javax.swing.event.MenuListener` | | Java Preference | `java.util.prefs.PreferenceChangeEvent` | `java.util.prefs.PreferenceChangeListener` | ### 面向注解的事件/监听器设计模式 事件/监听器注解场景举例 | Java 技术规范 | 事件注解 | 监听器注解 | | ------------- | -------------------------------- | --------------------------------------- | | Servlet 3.0+ | | `@javax.servlet.annotation.WebListener` | | JPA 1.0+ | `@javax.persistence.PostPersist` | | | Java Common | `@PostConstruct` | | | EJB 3.0+ | `@javax.ejb.PrePassivate` | | | JSF 2.0+ | `@javax.faces.event.ListenerFor` | | ### Spring 标准事件 - ApplicationEvent - `org.springframework.context.ApplicationEvent` - Java 标准事件java.util.EventObject 扩展 - 扩展特性:事件发生时间戳 - Spring 应用上下文ApplicationEvent 扩展 -ApplicationContextEvent - `org.springframework.context.event.ApplicationContextEvent` - Spring 应用上下文(ApplicationContext)作为事件源 - 具体实现: - `org.springframework.context.event.ContextClosedEvent` - `org.springframework.context.event.ContextRefreshedEvent` - `org.springframework.context.event.ContextStartedEvent` - `org.springframework.context.event.ContextStoppedEvent` ![img](assets/o_201025065102ContextStoppedEvent-16667064887562.png) ### 基于接口的 Spring 事件监听器 Java 标准事件监听器 `java.util.EventListener` 扩展 - 扩展接口 - `org.springframework.context.ApplicationListener` - 设计特点:单一类型事件处理 - 处理方法:`onApplicationEvent(ApplicationEvent)` - 事件类型:`org.springframework.context.ApplicationEvent` ### 基于注解的 Spring 事件监听器 Spring 注解 - `@org.springframework.context.event.EventListener` | 特性 | 说明 | | -------------------- | -------------------------------------------- | | 设计特点 | 支持多 `ApplicationEvent` 类型,无需接口约束 | | 注解目标 | 方法 | | 是否支持异步执行 | 支持 | | 是否支持泛型类型事件 | 支持 | | 是指支持顺序控制 | 支持,配合 `@Order` 注解控制 | ### 注册 Spring ApplicationListener - 基于 Spring 接口:向 Spring 应用上下文注册事件 - `ApplicationListener` 作为 Spring Bean 注册 - 通过 `ConfigurableApplicationContext#addApplicationListener` API 注册 - 基于 Spring 注解:`@org.springframework.context.event.EventListener` ### Spring事件发布器 - 方法一:通过 ApplicationEventPublisher 发布 Spring 事件 - 获取 ApplicationEventPublisher - 依赖注入 - 方法二:通过 ApplicationEventMulticaster 发布 Spring 事件 - 获取 ApplicationEventMulticaster - 依赖注入 - 依赖查找 ### Spring层次性上下文事件传播 - 发生说明 - 当 Spring 应用出现多层次 Spring 应用上下文(ApplicationContext)时,如 Spring WebMVC、Spring Boot 或 Spring Cloud 场景下,由子 `ApplicationContext` 发起 Spring 事件可能会传递到其 Parent `ApplicationContext`(直到 Root)的过程 - 如何避免 - 定位 Spring 事件源(ApplicationContext)进行过滤处理 ### Spring内建事件 `ApplicationContextEvent` 派生事件 - `ContextRefreshedEvent` :Spring 应用上下文就绪事件 - `ContextStartedEvent` :Spring 应用上下文启动事件 - `ContextStoppedEvent` :Spring 应用上下文停止事件 - `ContextClosedEvent` :Spring 应用上下文关闭事件 ### Spring 4.2 Payload 事件 Spring Payload 事件 - `org.springframework.context.PayloadApplicationEvent` - 使用场景:简化 Spring 事件发送,关注事件源主体 - 发送方法 - `ApplicationEventPublisher#publishEvent(java.lang.Object)` ### 自定义 Spring 事件 1. 扩展 `org.springframework.context.ApplicationEvent` 2. 实现 `org.springframework.context.ApplicationListener` 3. 将 `org.springframework.context.ApplicationListener` 注册到容器内 ### 依赖注入 ApplicationEventPublisher - 通过 `ApplicationEventPublisherAware` 回调接口 - 通过 `@Autowired ApplicationEventPublisher` ### 依赖查找 ApplicationEventMulticaster - 查找条件 - Bean 名称:`applicationEventMulticaster` - Bean 类型:`org.springframework.context.event.ApplicationEventMulticaster` ```scss AbstractApplicationContext#earlyApplicationEvents 在 prepareRefresh() 中从 null 被赋值,在 registerListeners() 的最后,重新被赋值为 null,并进行了早期事件的发布, 因为可能存在一个 Bean 同时实现 BeanPostProcessor 和 ApplicationEventPublisherAware,并在 ApplicationEventPublisherAware#setApplicationEventPublisher 方法中发送事件 因为实现了 BeanPostProcessor,所以这个 Bean 在 registerBeanPostProcessors(beanFactory); 这个方法中初始化,而此时 initApplicationEventMulticaster(); 还没有执行,所以 AbstractApplicationContext#applicationEventMulticaster 为空,无法发布事件,所以先将事件保存在 AbstractApplicationContext#earlyApplicationEvents 中 ``` ### ApplicationEventPublisher 底层实现 - 接口:org.springframework.context.event.ApplicationEventMulticaster - 抽象类:org.springframework.context.event.AbstractApplicationEventMulticaster - 实现类:`org.springframework.context.event.SimpleApplicationEventMulticaster` ### 同步和异步 Spring 事件广播 - 基于实现类 -org.springframework.context.event.SimpleApplicationEventMulticaster - 模式切换:setTaskExecutor(java.util.concurrent.Executor) 方法 - 默认模式:同步 - 异步模式:如 `java.util.concurrent.ThreadPoolExecutor` - 设计缺陷:不是基于接口契约编程,实现依赖于类本身 - 基于注解 -@org.springframework.context.event.EventListener - 模式切换 - 默认模式:同步 - 异步模式:标注 `@org.springframework.scheduling.annotation.Async` - 实现限制:无法直接实现同步/异步动态切换 区别: - 使用 `SimpleApplicationEventMulticaster#setTaskExecutor` 的影响是全局的,使用 `@Async` 是局部的 - 使用 `SimpleApplicationEventMulticaster#setTaskExecutor` 需要手动关闭线程池,使用 `@Async` 不需要 ### Spring 4.1 事件异常处理 - Spring 3.0 错误处理接口 - `org.springframework.util.ErrorHandler` - 使用场景 - Spring 事件(Events) - `SimpleApplicationEventMulticaster` Spring 4.1 开始支持 - Spring 本地调度(Scheduling) - `org.springframework.scheduling.concurrent.ConcurrentTaskScheduler` - `org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler` ### Spring 事件/监听器实现原理 - 核心类 -org.springframework.context.event.SimpleApplicationEventMulticaster - 设计模式:观察者模式扩展 - 被观察者 -org.springframework.context.ApplicationListener - API 添加 - 依赖查找 - 通知对象 - `org.springframework.context.ApplicationEvent` - 执行模式:同步/异步 - 异常处理:`org.springframework.util.ErrorHandler` - 泛型处理:`org.springframework.core.ResolvableType` ### 课外资料 #### Spring Boot 事件 | 事件类型 | 发生时机 | | ----------------------------------- | --------------------------------------- | | ApplicationStartingEvent | 当 Spring Boot 应用已启动时 | | ApplicationStartedEvent | 当 Spring Boot 应用已启动时 | | ApplicationEnvironmentPreparedEvent | 当 Spring Boot Environment 实例已准备时 | | ApplicationPreparedEvent | 当 Spring Boot 应用预备时 | | ApplicationReadyEvent | 当 Spring Boot 应用完全可用时 | | ApplicationFailedEvent | 当 Spring Boot 应用启动失败时 | #### Spring Cloud 事件 | 事件类型 | 发生时机 | | -------------------------- | ------------------------------------- | | EnvironmentChangeEvent | 当 Environment 示例配置属性发生变化时 | | HeartbeatEvent | 当 Discoveryclient 客户端发送心跳时 | | InstancePreRegisteredEvent | 当服务实例注册前 | | InstanceRegisteredEvent | 当服务实例注册后 | | RefreshEvent | 当 RefreshEndpoint 被调用时 | | RefreshScopeRefreshedEvent | 当 Refresh Scope Bean 刷新后 | ### 面试题 #### Spring 事件核心接口/组件? - Spring 事件 - `org.springframework.context.ApplicationEvent` - Spring 事件监听器 - `org.springframework.context.ApplicationListener` - Spring 事件发布器 - `org.springframework.context.ApplicationEventPublisher` - Spring 事件广播器 - `org.springframework.context.event.ApplicationEventMulticaster` #### Spring 同步和异步事件处理的使用场景? - Spring 同步事件 - 绝大多数 Spring 使用场景,如 `ContextRefreshedEvent` - Spring 异步事件 - 主要 `@EventListener` 与 `@Async` 配合,实现异步处理,不阻塞主线程,比如长时间的数据计算任务等。不要轻易调整 `SimpleApplicationEventMulticaster` 中关联的 `taskExecutor` 对象,除非使用者非常了解 Spring 事件机制,否则容易出现异常行为。