1. 概述

在本教程中,我们将深入探讨 Spring 中如何使用事件机制

虽然事件功能在 Spring 框架中经常被忽略,但它却是非常实用的功能之一。和 Spring 的很多特性一样,事件发布能力是由 ApplicationContext 提供的。

以下是使用 Spring 事件的一些基本准则:

  • 如果使用 Spring 4.2 之前的版本,事件类需要继承 ApplicationEvent
  • 从 Spring 4.2 开始,事件类 不再需要继承 ApplicationEvent
  • 发布者应注入 ApplicationEventPublisher 对象;
  • 监听者需实现 ApplicationListener 接口;

2. 自定义事件

Spring 允许我们创建并发布自定义事件,默认情况下这些事件是 同步执行 的。这带来了一些好处,例如监听者可以参与到发布者的事务上下文中。

2.1. 一个简单的应用事件

我们先创建一个简单的事件类,用于存储事件数据。

在这个例子中,事件类持有一个 String 类型的消息:

public class CustomSpringEvent extends ApplicationEvent {
    private String message;

    public CustomSpringEvent(Object source, String message) {
        super(source);
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}

2.2. 事件发布者

接下来创建事件的发布者。发布者构造事件对象,并将其发布给所有监听者。

为了发布事件,发布者只需注入 ApplicationEventPublisher 并调用 publishEvent() 方法即可:

@Component
public class CustomSpringEventPublisher {
    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    public void publishCustomEvent(final String message) {
        System.out.println("Publishing custom event. ");
        CustomSpringEvent customSpringEvent = new CustomSpringEvent(this, message);
        applicationEventPublisher.publishEvent(customSpringEvent);
    }
}

提示:Spring 4.2 后,ApplicationEventPublisher 接口新增了 publishEvent(Object event) 方法,允许传入任意对象作为事件,无需继承 ApplicationEvent

2.3. 事件监听者

最后创建事件监听者。

监听者只需是一个 Spring Bean 并实现 ApplicationListener 接口:

@Component
public class CustomSpringEventListener implements ApplicationListener<CustomSpringEvent> {
    @Override
    public void onApplicationEvent(CustomSpringEvent event) {
        System.out.println("Received spring custom event - " + event.getMessage());
    }
}

⚠️ 注意:监听方法是同步执行的,也就是说 publishCustomEvent() 方法会一直阻塞,直到所有监听者处理完毕。

3. 异步事件处理

有些场景下,我们并不希望事件同步处理,而是需要异步执行。

3.1. 使用 ApplicationEventMulticaster

可以通过配置一个带有线程池的 ApplicationEventMulticaster 来开启异步事件处理:

@Configuration
public class AsynchronousSpringEventsConfig {
    @Bean(name = "applicationEventMulticaster")
    public ApplicationEventMulticaster simpleApplicationEventMulticaster() {
        SimpleApplicationEventMulticaster eventMulticaster =
          new SimpleApplicationEventMulticaster();

        eventMulticaster.setTaskExecutor(new SimpleAsyncTaskExecutor());
        return eventMulticaster;
    }
}

✅ 此时所有事件监听者都会异步执行。

3.2. 使用 @Async

如果只想让某些监听者异步执行,可以使用 @Async 注解:

@EventListener
@Async
public void handleAsyncEvent(CustomSpringEvent event) {
    System.out.println("Handle event asynchronously: " + event.getMessage());
}

还可以指定使用特定的线程池:

@Async("nonDefaultExecutor")
void handleAsyncEvent(CustomSpringEvent event) {
    // run asynchronously by "nonDefaultExecutor"
}

📌 别忘了在配置类上加上 @EnableAsync

@Configuration
@EnableAsync
public class AppConfig {
}

4. Spring 内置事件

Spring 自身也会发布一些内置事件,比如:

  • ContextRefreshedEvent
  • ContextStartedEvent
  • RequestHandledEvent

这些事件可以帮助我们介入应用生命周期,执行自定义逻辑。

示例:监听上下文刷新事件

public class ContextRefreshedListener 
  implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent cse) {
        System.out.println("Handling context re-freshed event. ");
    }
}

5. 基于注解的事件监听

从 Spring 4.2 开始,事件监听者不再需要实现 ApplicationListener 接口,可以直接在任意 Spring Bean 的方法上使用 @EventListener 注解:

@Component
public class AnnotationDrivenEventListener {
    @EventListener
    public void handleContextStart(ContextStartedEvent cse) {
        System.out.println("Handling context started event.");
    }
}

默认是同步执行的,如果需要异步,只需加上 @Async 注解即可。

6. 泛型事件支持

Spring 还支持带有泛型信息的事件。

6.1. 泛型事件类

我们创建一个泛型事件类:

public class GenericSpringEvent<T> {
    private T what;
    protected boolean success;

    public GenericSpringEvent(T what, boolean success) {
        this.what = what;
        this.success = success;
    }
    // ... standard getters
}

6.2. 监听者

使用 @EventListener 和 SpEL 表达式进行条件监听:

@Component
public class AnnotationDrivenEventListener {
    @EventListener(condition = "#event.success")
    public void handleSuccessful(GenericSpringEvent<String> event) {
        System.out.println("Handling generic event (conditional).");
    }
}

6.3. 发布者

发布泛型事件时,由于类型擦除,建议使用子类明确泛型类型,例如:

class GenericStringSpringEvent extends GenericSpringEvent<String> { ... }

另外,如果在 @EventListener 方法中返回一个非空值,Spring 会自动将其作为新事件发布。

7. 事务绑定事件

Spring 4.2 引入了 @TransactionalEventListener 注解,允许监听者绑定到事务的不同阶段。

支持的事务阶段包括:

  • AFTER_COMMIT(默认):事务成功提交后触发
  • AFTER_ROLLBACK:事务回滚后触发
  • AFTER_COMPLETION:事务完成后触发(成功或失败)
  • BEFORE_COMMIT:事务提交前触发

示例:

@TransactionalEventListener
public void handleCustom(CustomSpringEvent event) {
    System.out.println("Handling event only when a transaction successfully completes.");
}

⚠️ 注意:*@TransactionalEventListener* 默认是同步执行的,不通过 ApplicationEventMulticaster 调度。

如果希望异步执行,可以结合使用 @Async

@Async
@TransactionalEventListener
void handleCustom(CustomSpringEvent event) { 
    System.out.println("Handling event only when a transaction successfully completes.");
}

⚠️ 踩坑提醒:使用 @Async 后,监听方法将无法访问原始事务上下文(如懒加载实体、事务回滚等),需谨慎使用。

8. 总结

本文介绍了 Spring 事件机制的基本使用方法,包括:

  • 自定义事件的创建与发布
  • 同步与异步事件处理
  • 注解驱动的事件监听
  • 泛型事件支持
  • 事务绑定事件处理

这些功能让 Spring 的事件机制变得非常灵活,适合用于解耦模块、实现观察者模式等场景。


原始标题:Spring Events | Baeldung