Spring 事件机制详解:从基础使用到高级应用

一、使用步骤

1.1 创建继承 ApplicationEvent 的事件对象

事件对象是 Spring 事件机制中传递信息的载体,它可携带业务数据,方便监听器获取关键信息。在实际开发中,除了继承ApplicationEvent并重写构造方法,通常还会添加自定义字段及对应的 getter 方法。

java 复制代码
import org.springframework.context.ApplicationEvent;

// 自定义事件,携带业务数据
public class CustomEvent extends ApplicationEvent {
private String message; // 自定义字段:事件消息
private Long timestamp; // 自定义字段:事件发生时间

// 构造方法:接收事件源和业务数据
public CustomEvent(Object source, String message) {
super(source);
this.message = message;
this.timestamp = System.currentTimeMillis();
}

// getter方法:供监听器获取数据
public String getMessage() {
return message;
}

public Long getTimestamp() {
return timestamp;
}
}

1.2 创建监听器对象,用@EventListener注解标注处理方法

监听器负责对事件进行具体处理,通过@EventListener注解可指定监听的事件类型,还能通过condition属性添加触发条件,基于 SpEL 表达式灵活控制事件处理逻辑。一个监听器可以包含多个@EventListener方法,实现对不同事件或同一事件不同场景的处理。

java 复制代码
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component // 注册为Spring组件,使其被容器扫描
public class CustomEventListener {

// 监听CustomEvent事件,且仅当message包含"error"时触发
@EventListener(
value = CustomEvent.class,
condition = "#event.message.contains('error')"
)
public void handleErrorEvent(CustomEvent event) {
System.out.println("处理错误事件:" + event.getMessage() + ",发生时间:" + event.getTimestamp());
}

// 监听所有CustomEvent(无条件)
@EventListener(CustomEvent.class)
public void handleAllCustomEvent(CustomEvent event) {
System.out.println("处理所有自定义事件:" + event.getMessage());
}
}

1.3 抛出事件

通过ApplicationContext的publishEvent()方法发布事件,Spring 会自动通知所有监听该事件的监听器。在实际业务中,事件发布通常在 Service 层或 Controller 层进行,而非局限于测试类。

java 复制代码
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;

@Service // 业务服务类
public class EventPublisherService implements ApplicationContextAware {

private ApplicationContext applicationContext;

// 业务方法:执行操作后发布事件
public void doBusiness(String message) {
System.out.println("执行业务逻辑:" + message);
// 发布事件(携带业务数据)
applicationContext.publishEvent(new CustomEvent(this, message));
}

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}

二、原理

2.1 必要的组件

  • ApplicationEvent:所有自定义事件的基类,用于存储事件源(source)和时间戳(timestamp)等基础信息。

  • @EventListener:用于标记方法为事件处理器,相比传统的ApplicationListener接口实现,大大减少了代码侵入性。

  • EventListenerFactory:负责将@EventListener标注的方法转换为ApplicationListener对象,采用适配器模式,默认实现为DefaultEventListenerFactory。

  • EventListenerMethodProcessor:实现了SmartInitializingSingleton接口,在 Spring 容器启动时,它会扫描容器中所有@EventListener方法,通过EventListenerFactory创建监听器,并注册到容器中。

2.2 EventListenerFactory 和 EventListenerMethodProcessor 注入流程

graph TD A("SpringApplication.run()")-->|调用| B("createApplicationContext()") B-->|加载| C(AnnotationConfigServletWebServerApplicationContext.class) C -->|构造方法中加载| D("AnnotatedBeanDefinitionReader.class") D -->|构造方法中调用| E("AnnotationConfigUtils.registerAnnotationConfigProcessors()") E-->|向容器中添加|F(DefaultEventListenerFactory
EventListenerMethodProcessor)

2.3 事件发布与执行流程

  1. 调用applicationContext.publishEvent(event)时,事件会被传递给ApplicationEventMulticaster(默认SimpleApplicationEventMulticaster)。
  2. 多播器根据事件类型,找到所有匹配的ApplicationListener(包括通过@EventListener转换的监听器)。
  3. 多播器执行监听器的onApplicationEvent()方法,默认同步执行,也可配置为异步执行。

三、增强事件处理

3.1 自定义增强:继承 @EventListener + 实现 EventListenerFactory

若需对事件处理逻辑进行扩展,如添加日志记录、权限校验等功能,可通过自定义注解和实现EventListenerFactory来实现。

步骤 1:自定义注解(继承@EventListener的功能,并添加扩展属性):
java 复制代码
import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Indexed;
import java.lang.annotation.*;

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
@EventListener // 继承原有功能
public @interface LoggedEventListener {
// 复用@EventListener的value属性(指定事件类型)
@AliasFor(annotation = EventListener.class, attribute = "value")
Class<?>[] value() default {};

// 扩展属性:是否记录日志
boolean log() default true;
}
步骤 2:实现 EventListenerFactory(处理自定义注解):
java 复制代码
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.EventListenerFactory;
import org.springframework.context.event.GenericApplicationListener;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

@Component
public class LoggedEventListenerFactory implements EventListenerFactory, Ordered {

@Override
public boolean supportsMethod(Method method) {
// 仅处理@LoggedEventListener标注的方法
return AnnotationUtils.findAnnotation(method, LoggedEventListener.class) != null;
}

@Override
public GenericApplicationListener createApplicationListener(String beanName, Class<?> type, Method method) {
LoggedEventListener annotation = AnnotationUtils.findAnnotation(method, LoggedEventListener.class);
return new GenericApplicationListener() {
@Override
public boolean supportsEventType(ResolvableType eventType) {
// 支持注解指定的事件类型
Class<?>[] eventTypes = annotation.value();
return eventTypes.length == 0 || eventType.isAssignableFrom(eventTypes[0]);
}

@Override
public void onApplicationEvent(ApplicationEvent event) {
try {
// 扩展逻辑:记录日志
if (annotation.log()) {
System.out.println("事件处理日志:" + event.getClass().getSimpleName());
}
// 执行原方法(反射调用监听器方法)
method.invoke(type.getDeclaredConstructor().newInstance(), event);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
};
}

@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE; // 优先级高于默认工厂
}
}

使用示例:

java 复制代码
@Component
public class LoggedListener {
@LoggedEventListener(value = CustomEvent.class, log = true)
public void handle(CustomEvent event) {
System.out.println("处理带日志的事件:" + event.getMessage());
}
}

3.2 事务事件处理(@TransactionalEventListener)

@TransactionalEventListener用于解决事务内发布事件时,监听器执行与事务提交不同步导致的数据不一致问题。它支持在事务的BEFORE_COMMIT、AFTER_COMMIT、AFTER_ROLLBACK、AFTER_COMPLETION(无论成功失败)等阶段触发事件处理。

java 复制代码
import org.springframework.transaction.event.TransactionalEventListener;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.stereotype.Component;

@Component
public class TransactionalListener {

// 事务提交后执行(确保数据已持久化)
@TransactionalEventListener(
value = CustomEvent.class,
phase = TransactionPhase.AFTER_COMMIT
)
public void handleAfterCommit(CustomEvent event) {
System.out.println("事务提交后处理事件:" + event.getMessage());
}

// 事务回滚后执行
@TransactionalEventListener(
value = CustomEvent.class,
phase = TransactionPhase.AFTER_ROLLBACK
)
public void handleAfterRollback(CustomEvent event) {
System.out.println("事务回滚后处理事件:" + event.getMessage());
}
}

注意事项:

  • 事件发布者的方法必须被@Transactional标注,否则事务阶段配置将不生效,监听器会立即执行。
  • 若需异步执行事务事件,可结合@Async注解使用,但需在配置类上添加@EnableAsync注解。

四、进阶特性

4.1 异步事件处理

通过@EnableAsync开启异步功能,并在@EventListener或@TransactionalEventListener上添加@Async注解,可使事件处理在独立线程执行,避免阻塞主线程,提升系统性能。

java 复制代码
@Component
public class AsyncListener {
@Async
@EventListener(CustomEvent.class)
public void handleAsync(CustomEvent event) {
System.out.println("异步处理事件:" + Thread.currentThread().getName());
}
}

4.2 事件继承与多态

监听器可以监听父类事件,从而接收所有子类事件,利用面向对象的继承与多态特性,简化事件监听逻辑。

java 复制代码
// 父类事件
public class BaseEvent extends ApplicationEvent { ... }
// 子类事件
public class ChildEvent extends BaseEvent { ... }

// 监听父类事件,可接收ChildEvent
@EventListener(BaseEvent.class)
public void handleBaseEvent(BaseEvent event) { ... }

4.3 事件发布返回值

监听器方法可以返回值,多个监听器的返回值会被封装为List,通过applicationContext.publishEvent()的返回值获取,方便进行后续处理。

java 复制代码
// 监听器返回值
@EventListener(CustomEvent.class)
public String handleWithResult(CustomEvent event) {
return "处理结果:" + event.getMessage();
}

// 发布事件并获取返回值
List<Object> results = (List<Object>) applicationContext.publishEvent(new CustomEvent(this, "test"));

Spring 事件机制通过灵活的设计和丰富的扩展能力,为应用程序的解耦、异步处理和事务同步提供了强大支持。合理运用这些特性,能够有效提升系统的可维护性和性能。

相关推荐
Java天梯之路2 小时前
# Spring Boot 钩子全集实战(四):`SpringApplicationRunListener.environmentPrepared()` 详解
java·spring·面试
白鸽(二般)2 小时前
Spring 的配置文件没有小绿叶
java·后端·spring
qq_12498707533 小时前
基于Spring Boot的微信小程序的智慧商场系统的设计与实现
java·spring boot·spring·微信小程序·小程序·毕业设计·计算机毕业设计
醒过来摸鱼4 小时前
Spring Cloud Gateway
java·spring·spring cloud
后端小张4 小时前
【JAVA 进阶】Spring Boot自动配置详解
java·开发语言·人工智能·spring boot·后端·spring·spring cloud
彭于晏Yan4 小时前
Spring集成kafka
spring·kafka
IT 行者4 小时前
Spring Security Session 序列化策略分析
java·spring boot·后端·spring
IT 行者4 小时前
Spring Boot 4.0 整合Spring Security 7 后的统一异常处理指南
spring boot·后端·spring
短剑重铸之日15 小时前
SpringBoot声明式事务的源码解析
java·后端·spring·springboot