Spring ApplicationListener 详解

ApplicationEvent 和 ApplicationListener

ApplicationEvent

是个抽象类,里面只有一个构造函数和一个长整型的timestamp;它的实现类表示一类事件,可携带数据。

ApplicationListener

是一个接口,里面只有一个onApplicationEvent方法;这个接口是应用的事件的监听器,当监听器在Spring上下文注册后, 在Spring的某些阶段出现发出事件的时候,将会执行指定的方法。

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    /** 处理一个应用事件 */
    void onApplicationEvent(E event);
}

ApplicationContext 事件机制是观察者设计模式的实现,通过 ApplicationEvent 类和 ApplicationListener 接口,可以实现 ApplicationContext 事件处理;如果容器中存在 ApplicationListener 的 Bean,当 ApplicationContext 调用 publishEvent 时,对应的 Bean 将会自动触发。

SpringBoot 内置事件

SpringBoot 内置的 SpringApplicationEvent 有:
* ApplicationStartingEvent
* ApplicationEnvironmentPreparedEventEvent
* ApplicationContextInitializedEvent
* ApplicationPreparedEvent
* ApplicationStartedEvent
* ApplicationReadyEvent
* ApplicationFailedEvent

触发条件如下图:

自定义事件监听器

通过继承 ApplicationListener 可以实现自定义的事件监听器;
例如:

/**
 * 自定义的事件监听器,监听事件为 ApplicationEvent
 */
@Slf4j
@Component
public class CustomerApplicationListener implements ApplicationListener<ApplicationEvent> {
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        log.info("接收到 Spring 事件 => {}", event.getClass().getSimpleName());
    }
}

启动应用后,控制台打印结果如下:

自定义事件

需要定义自定义的事件,需要通过继承 ApplicationEvent实现;
例如:

@Getter
@Setter
public class CustomerEvent extends ApplicationEvent {

    private final String content;

    private final Long time;

    public CustomerEvent(String content) {
        super("自定义的事件源");
        this.content = content;
        this.time = System.currentTimeMillis();
    }
}

在需要的地方使用 SpringBoot 的上下文 ApplicationContext 发布事件, 那么相应的自定义消息处理器的就会接收到消息;

@RestController
@RequestMapping("/api/test")
public class TestController {

    @Autowired
    private ApplicationContext applicationContext;

    @GetMapping(value = "/test")
    public Map<String, Object> Test() {
        applicationContext.publishEvent(new CustomerEvent("这是自定义的事件"));
        return null;
    }
}

自定义 CustomerEvent 事件监听器:

/**
 * 自定义的事件监听器,监听事件为 CustomerEvent
 */
@Slf4j
@Component
public class CustomerApplicationListener implements ApplicationListener<CustomerEvent> {

    @Override
    public void onApplicationEvent(CustomerEvent event) {
        log.info("接收到 自定义的事件 => {}", event.getClass().getSimpleName());
        log.info("事件内容 => {}", event.getContent());
        log.info("接收到消息的线程 ==> {}", Thread.currentThread().getName());
    }
}

访问Api:http://localhost:8080/api/test/test,触发CustomerEvent;
控制台输出如下:

  • 默认情况下,接收事件的发送和处理在同一个线程,但是可以通过 @Async 和 @EnableAsync 启用异步处理。
  • SpringBoot 应用需要添加 @EnableAsync 注解,否则 @Async 注解不生效

    @Slf4j
    @Component
    @Async
    @EnableAsync
    public class CustomerApplicationListener implements ApplicationListener<CustomerEvent> {
    
    @Override
    public void onApplicationEvent(CustomerEvent event) {
        log.info("接收到 自定义的事件 => {}", event.getClass().getSimpleName());
        log.info("事件内容 => {}", event.getContent());
        log.info("接收到消息的线程 ==> {}", Thread.currentThread().getName());
    }
    }
    

事件监听器的注册和管理及事件的发送

本质上,SpringBoot 通过应用事件广播器 ApplicationEventMulticaster 的方式实现事件监听器的注册和管理以及事件的发送。

public interface ApplicationEventMulticaster {
    
    // 新增事件监听器
    void addApplicationListener(ApplicationListener<?> listener);

    // 新增事件监听器
    void addApplicationListenerBean(String listenerBeanName);

    // 移除事件监听器
    void removeApplicationListener(ApplicationListener<?> listener);

    // 移除事件监听器      
    void removeApplicationListenerBean(String listenerBeanName);

    // 移除全部事件监听器
    void removeAllListeners();

    // 发送应用事件
    void multicastEvent(ApplicationEvent event);

    // 发送应用事件
    void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);
}

通过查看 ApplicationContext 源码的方式可以看到其 publicEvent 的源码调用了 multicastEvent 方法发送应用事件。

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 {
        // 此处获取到 ApplicationEventMulticaster 实例后发送消息
        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);
        }
    }
}