首页 > 基础资料 博客日记
Spring事件(Application Event)使用和源码
2024-09-07 21:00:07基础资料围观127次
Java资料网推荐Spring事件(Application Event)使用和源码这篇文章给大家,欢迎收藏Java资料网享受知识的乐趣
1、什么是Spring事件
Spring 框架中的事件处理机制是一种在Spring容器管理的bean之间异步传递消息的方式。Spring事件机制允许一个bean在发生某些事情时发布事件,而其他bean可以监听这些事件并做出反应。
Spring事件机制是观察者模式的一种实现,是除了事件源和监听者两个角色之外,还有一个 EventMultiCaster
的角色负责把事件转发(广播)给监听者。
2、事件的使用场景及优缺点
使用场景
- 用户认证通知:
- 当用户登录系统时,可以发布一个认证成功的事件,其他组件可以监听此事件并执行相关操作,如更新用户状态、记录登录日志等。
- 应用程序启动和关闭:
- 使用Spring的
ContextRefreshedEvent
和ContextClosedEvent
来监听应用程序的启动和关闭事件。
- 使用Spring的
- 消息队列处理:
- 在消息消费过程中,可以发布自定义事件,当消息被成功处理后,触发后续操作。
- 异步处理:
- 对于不需要即时返回结果的长时间运行任务,可以发布事件让其他服务异步处理。
- 缓存更新:
- 当数据更新时,发布事件通知缓存服务更新缓存条目。
- 工作流状态变化:
- 在复杂的业务流程中,当某个步骤完成时,发布事件通知其他步骤或服务进行相应的处理。
- 资源变更通知:
- 如数据源变更、配置文件更改等,通过事件通知系统中的相关组件重新加载配置。
- 定时任务调度:
- 在定时任务执行前后发布事件,允许其他组件在任务执行前后进行干预或处理。
- 事务管理:
- 监听事务的开始和结束事件,进行事务相关的自定义处理。
- 错误和异常处理:
- 当系统中发生错误或异常时,发布事件以便集中处理或记录错误信息。
优点
- 解耦:
- 事件发布者和监听者之间是解耦的,它们不需要知道对方的存在,只需对事件本身有共同的理解。
- 扩展性:
- 新的监听器可以很容易地添加到系统中,而不需要修改现有的代码。
- 异步处理能力:
- 支持异步事件处理,有助于提高应用程序的响应性和吞吐量。
- 灵活性:
- 可以根据需要定义和使用自定义事件,传递特定的数据。
- 简化组件间的通信:
- 减少了组件之间的直接调用,简化了组件间的通信逻辑。
- 重用性:
- 事件监听器可以在不同的上下文中重用,提高了代码的重用性。
- 易于集成:
- 事件机制可以很容易地集成到现有的Spring应用程序中。
缺点
- 性能开销:
- 事件的发布和监听可能会引入一些性能开销,尤其是在事件处理逻辑复杂或监听器数量较多的情况下。
- 调试困难:
- 由于事件的传递是异步的,可能会使得问题的调试变得更加困难。
- 事件顺序问题:
- 在某些情况下,事件处理的顺序可能不容易控制,可能会导致不可预期的结果。
- 过度使用:
- 如果过度使用事件机制,可能会导致系统结构变得复杂和难以理解。
- 安全风险:
- 如果事件携带敏感数据,不恰当的处理可能会带来安全风险。
- 依赖管理:
- 在某些情况下,事件监听器的依赖管理可能比直接的bean依赖更加复杂。
- 资源消耗:
- 长时间运行的事件监听器可能会占用系统资源,尤其是在没有正确管理的情况下。
- 测试挑战:
- 测试事件驱动的系统可能比测试基于直接调用的系统更加困难。
3、使用
- 定义事件: 创建一个事件类,继承自
ApplicationEvent
。
/**
* ApplicationEvent 的子类会被 ApplicationListener 监听并响应。
*/
public class CustomEvent extends ApplicationEvent {
private String message;
public CustomEvent(Object source, String message) {
super(source);
this.message = message;
}
public String getMessage() {
return message;
}
}
- 创建事件发布者: 在需要发布事件的组件中,注入
ApplicationEventPublisher
。
@Component
public class EventPublisherBean {
@Autowired
private ApplicationEventPublisher publisher;
// @Autowired
// ApplicationContext applicationContext;
public void publishEvent(String message) {
CustomEvent event = new CustomEvent(this, message);
publisher.publishEvent(event);
//也可以使用ApplicationContext发布事件,但通常不推荐
//applicationContext.publishEvent(event);
}
}
- 编写事件监听器: 创建一个事件监听器类,实现
ApplicationListener
接口或使用@EventListener
注解。
@Component
public class CustomEventListener implements ApplicationListener<CustomEvent> {
@Override
public void onApplicationEvent(CustomEvent event) {
String message = event.getMessage();
// 处理事件
System.out.println("Received event with message: " + message);
}
}
或者使用@EventListener
注解:
@Component
public class AnotherEventListener {
@EventListener
public void handleCustomEvent(CustomEvent event) {
// 处理事件
System.out.println("Handling event with message: " + event.getMessage());
}
}
- 配置异步事件监听(可选): 如果需要异步处理事件,可以使用
@Async
注解。
(测试时,因为懒方法上使用了@PostConstruct,结果发现执行不到当前@Async异步)
@Component
public class AsyncEventListener {
@Async
@EventListener
public void handleCustomEvent(CustomEvent event) {
// 异步处理事件
System.out.println("Asynchronously handling event with message: " + event.getMessage());
}
}
- 发布事件: 在适当的时机,使用注入的
ApplicationEventPublisher
发布事件。
// 在某个业务逻辑中
eventPublisherBean.publishEvent("Hello, World!");
- 启动Spring应用程序: 确保Spring应用程序上下文被正确初始化,以便注册事件发布者和监听器。
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(YourSpringBootApplication.class, args);
// 从容器中获取Bean并触发事件
EventPublisherBean eventPublisherBean = context.getBean(EventPublisherBean.class);
eventPublisherBean.publishEvent("Event from main method");
}
4、源码解读
执行顺序
主要方法
AbstractApplicationContext#publishEvent(org.springframework.context.ApplicationEvent)
/**
* 发布给定的事件到所有监听器。
* 如果事件不是{@link ApplicationEvent}的实例,它将被封装在一个{@link PayloadApplicationEvent}中。
* 如果提供了事件类型,则用于多播事件;否则,将从封装的事件中解析事件类型。
* 如果当前上下文尚未初始化多播器,则事件将被添加到早期事件列表中,待上下文初始化后进行多播。
* 如果当前上下文有父上下文,事件也将被发布到父上下文。
*
* @param event 要发布的事件,可以是任何对象,如果事件不是{@link ApplicationEvent}的实例,它将被封装。
* @param eventType 事件类型,可为null。如果提供,将用于多播事件;如果事件被封装,则从中解析事件类型。
* @since 4.2
*/
AbstractApplicationContext#publishEvent(java.lang.Object, org.springframework.core.ResolvableType)
/**
* 多播给定的事件到所有的监听器。
* 如果提供了事件类型,则使用提供的事件类型;否则,尝试解析默认的事件类型。
* 如果存在任务执行器,则使用它来异步执行监听器调用;否则,直接同步调用监听器。
*
* @param event 需要多播的事件对象。
* @param eventType 可选的事件类型,用于指定特定类型的事件监听器。
*/
SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)
/**
* 根据给定的事件和事件类型,获取匹配的应用程序监听器集合。
* 通过提前排除不匹配的监听器来优化性能。
*
* @param event 发生的事件,用于匹配监听器。允许基于缓存的匹配信息提前排除非匹配的监听器。
* @param eventType 事件的类型,用于精确匹配监听器。
* @return 匹配的监听器集合。
* @see org.springframework.context.ApplicationListener
*/
AbstractApplicationEventMulticaster#getApplicationListeners(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)
/**
* 根据给定的事件类型和源类型,实际检索应用监听器。
* <p>
* 此方法旨在高效地检索适用于特定事件和源的监听器集合。它首先从默认检索器中获取所有预注册的监听器,
* 然后根据事件类型和源类型过滤它们。此外,它还处理通过bean名称注册的监听器,这可能与直接注册的监听器重叠,
* 但可能包含额外的元数据。
*
* @param eventType 事件类型
* @param sourceType 事件源类型
* @param retriever 用于缓存目的的监听器检索器,如果需要填充
* @return 给定事件和源类型的预过滤应用监听器列表
*/
AbstractApplicationEventMulticaster#retrieveApplicationListeners(ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable ListenerRetriever retriever)
/**
* 判断给定的监听器是否支持给定的事件。
* <p>默认实现检测{@link SmartApplicationListener}和{@link GenericApplicationListener}接口。
* 对于标准的{@link ApplicationListener},会使用{@link GenericApplicationListenerAdapter}来检查目标监听器的泛型声明类型。
* @param listener 待检查的目标监听器
* @param eventType 待检查的事件类型
* @param sourceType 待检查的事件源类型
* @return 如果给定的监听器应包含在给定事件类型的候选监听器中,则返回true
*/
AbstractApplicationEventMulticaster#supportsEvent(org.springframework.context.ApplicationListener<?>, org.springframework.core.ResolvableType, java.lang.Class<?>)
/**
* 通过检查其泛型声明的事件类型,提前过滤豆定义的监听器。
* <p>
* 如果这个方法对给定的监听器作为初步检查返回 {@code true},则之后会通过
* {@link #supportsEvent(ApplicationListener, ResolvableType, Class)} 方法来获取和完全评估监听器实例。
* @param beanFactory 包含监听器bean的BeanFactory
* @param listenerBeanName BeanFactory中监听器bean的名字
* @param eventType 需要检查的事件类型
* @return 对于给定的事件类型,是否应该将给定的监听器包含在候选列表中
* @see #supportsEvent(Class, ResolvableType)
* @see #supportsEvent(ApplicationListener, ResolvableType, Class)
*/
AbstractApplicationEventMulticaster#supportsEvent(org.springframework.beans.factory.config.ConfigurableBeanFactory, java.lang.String, org.springframework.core.ResolvableType)
/**
* 调用给定的ApplicationListener处理给定的ApplicationEvent。
* <p>
* 此方法封装了调用监听器的实际逻辑,以便在存在错误处理程序时能够处理任何异常。
* 如果存在错误处理程序,任何在调用监听器过程中抛出的异常都会被错误处理程序处理。
* 如果不存在错误处理程序,则直接调用监听器,不进行异常处理。
*
* @param listener 要调用的应用程序事件监听器。
* @param event 要传递给监听器的应用程序事件。
* @since 4.1
*/
SimpleApplicationEventMulticaster#invokeListener(ApplicationListener<?> listener, ApplicationEvent event)
/**
* 调用对应的自定义监听器
* 调用应用程序监听器的onApplicationEvent方法。
*
* 此方法处理了调用监听器事件时可能发生的ClassCastException。如果发生此类异常,
* 且异常信息与事件类型不匹配,那么将记录一个调试消息,并抑制异常。
* 这种情况可能发生在使用lambda定义的监听器上,无法解析其泛型事件类型。
*
* @param listener 要调用的应用程序监听器。
* @param event 要传递给监听器的应用程序事件。
* @throws ClassCastException 如果监听器的事件类型与事件不匹配,且异常信息不符合特定模式,则抛出此异常。
*/
SimpleApplicationEventMulticaster#doInvokeListener(ApplicationListener listener, ApplicationEvent event)
ApplicationEvent
package org.springframework.context;
import java.util.EventObject;
/**
* 类将由所有应用程序事件扩展。抽象,因为直接发布通用事件没有意义。
* 抽象类 作为所有具体应用事件的基类。
* ApplicationEvent 的子类会被 ApplicationListener 监听并响应。
* ApplicationContext 提供了一个事件发布机制,允许任何实现了 ApplicationListener 接口的组件订阅并处理这些事件。
* 具体的事件类型通常由 ApplicationEvent 的子类来定义,例如 ContextRefreshedEvent 或 ContextStartedEvent,它们表示应用上下文被刷新或启动等特定的事件。
*
* @author Rod Johnson
* @author Juergen Hoeller
*/
public abstract class ApplicationEvent extends EventObject {
/** 使用Spring 1.2中的serialVersionUID实现互操作性 */
private static final long serialVersionUID = 7099057708183571937L;
/** 事件发生时的系统时间。 */
private final long timestamp;
/**
* 创建新的ApplicationEvent。
* @param source the object on which the event initially occurred (never {@code null})
*/
public ApplicationEvent(Object source) {
super(source);
this.timestamp = System.currentTimeMillis();
}
/**
* 返回事件发生时的系统时间(毫秒)。
*/
public final long getTimestamp() {
return this.timestamp;
}
}
AbstractApplicationContext
#publishEvent(java.lang.Object, org.springframework.core.ResolvableType)
/**
* 发布给定的事件到所有监听器。
* 如果事件不是{@link ApplicationEvent}的实例,它将被封装在一个{@link PayloadApplicationEvent}中。
* 如果提供了事件类型,则用于多播事件;否则,将从封装的事件中解析事件类型。
* 如果当前上下文尚未初始化多播器,则事件将被添加到早期事件列表中,待上下文初始化后进行多播。
* 如果当前上下文有父上下文,事件也将被发布到父上下文。
*
* @param event 要发布的事件,可以是任何对象,如果事件不是{@link ApplicationEvent}的实例,它将被封装。
* @param eventType 事件类型,可为null。如果提供,将用于多播事件;如果事件被封装,则从中解析事件类型。
* @since 4.2
*/
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
// 根据事件类型判断是否需要封装事件
// Decorate event as an ApplicationEvent if necessary
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
}
}
// 如果当前上下文尚未初始化多播器,则将事件添加到早期事件列表中;
// 否则,立即使用多播器广播事件。
// Multicast right now if possible - or lazily once the multicaster is initialized
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
// 如果当前上下文有父上下文,则也将发布事件到父上下文。
// Publish event via parent context as well...
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}
SimpleApplicationEventMulticaster
#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)
/** org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType) */
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
Executor executor = this.getTaskExecutor(); //事件处理的线程池(没有设置异步处理的线程池则由发布的线程处理)
Iterator var5 = this.getApplicationListeners(event, type).iterator(); //根据事件类型寻找已经注册的感兴趣的监听器
while(var5.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var5.next();
if (executor != null) {
executor.execute(() -> { //交由监听器处理事件(异步)
this.invokeListener(listener, event);
});
} else {
this.invokeListener(listener, event);
}
}
}
#doInvokeListener
/** org.springframework.context.event.SimpleApplicationEventMulticaster#doInvokeListener */
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
listener.onApplicationEvent(event); //监听器继承ApplicationListener重写的处理方法
} catch (ClassCastException var6) {
String msg = var6.getMessage();
if (msg != null && !this.matchesClassCastMessage(msg, event.getClass())) {
throw var6;
}
Log logger = LogFactory.getLog(this.getClass());
if (logger.isTraceEnabled()) {
logger.trace("Non-matching event type for listener: " + listener, var6);
}
}
}
文章来源:https://blog.csdn.net/qq_41999771/article/details/140710686
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!
标签: