SpringBoot 提供了各种各样的时间监听器( ApplicationListener 的子类 ),用来订阅SpringBoot在运行阶段的各种事件,整体的这种方式实现的逻辑图如下图:

    image.png

    这个接口是应用的事件的监听器,基于观察者模式实现,从Spring3.0开始,当监听器在Spring上下文注册后, 在Spring的某些阶段出现发出事件的时候,将会执行指定的方法。

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

    这里的消息指的是 ApplicationEvent, Spring 提供了ApplicationEvent 类,并内置一些Spring的应用事件,如果需要定义自定义的事件,则需要通过继承 ApplicationEvent实现。

    SpringBoot 内置的SpringApplicationEvent如下图所示。

    image.png

    SpringBoot 的事件发送时具有一定顺序,会按照SpringBoot的启动顺序发送:

    image.png

    通过继承ApplicationListener<E> 可以实现自定义的事件处理器,下面的代码中展示了监听ApplicationEvent事件的功能

    1. /**
    2. * 自定义的事件监听器,监听事件为 ApplicationEvent
    3. */
    4. @Slf4j
    5. @Component
    6. public class CustomerApplicationListener implements ApplicationListener<ApplicationEvent> {
    7. @Override
    8. public void onApplicationEvent(ApplicationEvent e) {
    9. log.info("接收到 Spring事件 => {}", e.getClass().getSimpleName());
    10. }
    11. }
    12. // 启动应用后,可以在控制台看到应用的相关事件,控制台打印结果如下
    13. 接收到 Spring事件 => ServletWebServerInitial
    14. 接收到 Spring事件 => ContextRefreshedEvent
    15. 接收到 Spring事件 => ApplicationStartedEvent
    16. 接收到 Spring事件 => AvailabilityChangeEvent
    17. 接收到 Spring事件 => ApplicationReadyEvent
    18. 接收到 Spring事件 => AvailabilityChangeEvent

    事实上,我们也可以通过自定义事件的方式,实现自己的相关的业务逻辑。比如下面的代码中展示了在Spring应用启动完成后会发送一个自定义的消息, 同样的此消息必须继承自ApplicationEvent 下面的代码中定义了CustomerEvent。

    1. @Getter
    2. @Setter
    3. public class OrderPayEvent extends ApplicationEvent {
    4. private final String content;
    5. private final Long time;
    6. public MyCustomerEvent(String content) {
    7. super("自定义的事件源");
    8. this.content = content;
    9. this.time = System.currentTimeMillis();
    10. }
    11. }

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

    默认情况下,接收事件的发送和处理在同一个线程,但是可以通过 **@Async@EnableAsync** 启用异步处理。

    1. // 通过SpringBoot上下文发送事件
    2. context.publishEvent(new OrderPayEvent("这是自定义的应用启动完成消息"));
    3. /**
    4. * 自定义的事件监听器,监听事件为 OrderPayEvent
    5. *
    6. * @apiNote 如果需要异步处理能力的话,添加此注解,注意SpringBoot应用需要添加@EnableAsync注解,否则该注解不生效
    7. */
    8. @Slf4j
    9. @Component
    10. public class CustomerEventListener implements ApplicationListener<OrderPayEvent> {
    11. @Async
    12. @Override
    13. public void onApplicationEvent(OrderPayEvent e) {
    14. log.info("接收到 自定义的事件 => {}", e.getClass().getSimpleName());
    15. log.info("事件内容 => {}", e.getContent());
    16. log.info("接收到消息的线程 ==> {}", Thread.currentThread().getName());
    17. }
    18. }
    19. // 控制台输出结果
    20. 发送消息的线程:main
    21. 接收到 自定义的事件 => OrderPayEvent
    22. 事件内容 => 这是自定义的应用启动完成消息
    23. 接收到消息的线程 ==> task-1

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

    1. public interface ApplicationEventMulticaster {
    2. // 新增事件监听器
    3. void addApplicationListener(ApplicationListener<?> listener);
    4. // 新增事件监听器
    5. void addApplicationListenerBean(String listenerBeanName);
    6. // 移除事件监听器
    7. void removeApplicationListener(ApplicationListener<?> listener);
    8. // 移除事件监听器
    9. void removeApplicationListenerBean(String listenerBeanName);
    10. // 移除全部事件监听器
    11. void removeAllListeners();
    12. // 发送应用事件
    13. void multicastEvent(ApplicationEvent event);
    14. // 发送应用事件
    15. void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);
    16. }

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

    1. protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
    2. Assert.notNull(event, "Event must not be null");
    3. // Decorate event as an ApplicationEvent if necessary
    4. ApplicationEvent applicationEvent;
    5. if (event instanceof ApplicationEvent) {
    6. applicationEvent = (ApplicationEvent) event;
    7. }
    8. else {
    9. applicationEvent = new PayloadApplicationEvent<>(this, event);
    10. if (eventType == null) {
    11. eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
    12. }
    13. }
    14. // Multicast right now if possible - or lazily once the multicaster is initialized
    15. if (this.earlyApplicationEvents != null) {
    16. this.earlyApplicationEvents.add(applicationEvent);
    17. }
    18. else {
    19. // 此处获取到 ApplicationEventMulticaster 实例后发送消息
    20. getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
    21. }
    22. // Publish event via parent context as well...
    23. if (this.parent != null) {
    24. if (this.parent instanceof AbstractApplicationContext) {
    25. ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
    26. }
    27. else {
    28. this.parent.publishEvent(event);
    29. }
    30. }
    31. }

    • 拓展阅读
    1. 经典架构设计-消息驱动架构