前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >一文读懂Spring事件机制

一文读懂Spring事件机制

作者头像
程序猿杜小头
发布2022-12-01 21:39:06
5140
发布2022-12-01 21:39:06
举报
文章被收录于专栏:程序猿杜小头程序猿杜小头

一文读懂Spring事件机制

Spring 事件机制是为同一ApplicationContext中多个Bean之间的通信而设计的,它是观察者模式的一个典型应用。

观察者模式在生活中随处可见,比如:在安装有交通信号灯的路口,行人需要实时观察信号灯的变化,从而判断是否需要前进、缓行或等待;而信号灯只负责发布信号变更事件,并不会干预行人,也就是说信号灯与行人之间是松耦合的关系。

为什么要搞清楚 Spring 事件机制呢?因为这玩意儿在 Spring 内部应用广泛,不搞清楚的话,看 Spring 相关源码会是一个拦路虎;此外,也可以在实际工作中解决相关问题,比如:笔者在工作中曾借助它来实现流程节点的自动流转,这些业务流程往往很长,但只有少数流程节点需要客户手动触发完成,其余节点需要自动流转。

1 从实战出发

聊到事件机制,那就不得不提事件处理模型三要素了,即事件源、事件和事件监听器。

1.1 定义事件

在 Spring 4.2 之前,所有事件均需要继承自ApplicationEvent,而 ApplicationEvent 又继承自java.util.EventObject;但在 Spring 4.2 之后,并不强制要求自定义事件继承 ApplicationEvent,因为 Spring 会将开发者自定义的事件类包装为一个PayloadApplicationEvent,其实,PayloadApplicationEvent 同样继承自 ApplicationEvent。

1.2 定义事件监听器

代码语言:javascript
复制
// 事件监听器,即行人
@Component
@Slf4j
public class CustomTrafficSignalListener implements ApplicationListener<TrafficSignalEvent> {
    @Override
    public void onApplicationEvent(TrafficSignalEvent trafficSignalEvent) {
        if (trafficSignalEvent instanceof RedSignalEvent) {
            log.info("红灯亮,请所有行人和车辆停止前行");
        } else if (trafficSignalEvent instanceof GreenSignalEvent) {
            log.info("绿灯亮,请所有行人和车辆前行");
        } else if (trafficSignalEvent instanceof YellowSignalEvent) {
            log.info("黄灯亮,请所有行人和车辆缓行");
        } else {
            throw new IllegalStateException("未知的信号灯事件");
        }
    }
}

Spring 4.2 提供了org.springframework.context.event.EventListener注解,不再需要实现org.springframework.context.ApplicationListener接口。相信大家在实际工作中更倾向于使用@EventListener的方式来定义事件监听器。

代码语言:javascript
复制
// 事件监听器,即行人
@Component
@Slf4j
public class FlexibleTrafficSignalListener {
    @EventListener
    public void onTrafficSignalEvent(TrafficSignalEvent event) {
        if (event instanceof RedSignalEvent) {
            log.info("红灯亮,请所有行人和车辆停止前行");
        } else if (event instanceof GreenSignalEvent) {
            log.info("绿灯亮,请所有行人和车辆前行");
        } else if (event instanceof YellowSignalEvent) {
            log.info("黄灯亮,请所有行人和车辆缓行");
        } else {
            throw new IllegalStateException("未知的信号灯事件");
        }
    }
}

1.3 定义事件源

代码语言:javascript
复制
// 交通信号灯,即事件源
@Component
public class TrafficSignal implements ApplicationEventPublisherAware {
    private ApplicationEventPublisher applicationEventPublisher;

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    public void doSignal() {
        TrafficSignalEvent redSignalEvent = new RedSignalEvent(this);
        applicationEventPublisher.publishEvent(redSignalEvent);
    }
}

1.4 运行结果

CustomTrafficSignalListener

代码语言:javascript
复制
2021-08-16 20:59:19.728  INFO 11908 --- [main] p.d.e.e.CustomTrafficSignalListener      : 红灯亮,请所有行人和车辆停止前行

FlexibleTrafficSignalListener

代码语言:javascript
复制
2021-08-16 20:59:22.135  INFO 17653 --- [main] p.d.e.e.FlexibleTrafficSignalListener    : 红灯亮,请所有行人和车辆停止前行

2 异步事件监听器

默认情况下,事件监听器基于 同步机制 来处理事件,这意味着ApplicationEventPublisherpublishEvent()方法会阻塞,直到所有事件监听器都处理完毕。如果你想指定某一事件监听器基于 异步机制 来处理事件,可以使用@Async注解。当然,也可以通过配置自定义的ApplicationEventMulticaster使得所有事件监听器都基于异步机制处理事件。值得一提的是,全局异步监听所涉及的这一自定义ApplicationEventMulticaster有个坑,即该Bean的名称必须是applicationEventMulticaster,否则不会生效,具体原因下面会分析。

2.1 配置自定义事件广播器

代码语言:javascript
复制
@Configuration
public class ApplicationEventMulticasterConfig {
    @Bean
    public ApplicationEventMulticaster applicationEventMulticaster() {
        ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("event-pool-%d").build();
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                0,
                16,
                0L,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(24),
                namedThreadFactory,
                new ThreadPoolExecutor.AbortPolicy()
        );
        SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster();
        simpleApplicationEventMulticaster.setTaskExecutor(threadPoolExecutor);
        return simpleApplicationEventMulticaster;
    }
}

2.2 运行结果

代码语言:javascript
复制
2021-08-16 22:05:50.053  INFO 13652 --- [event-pool-11] p.d.e.e.FlexibleTrafficSignalListener    : 红灯亮,请所有行人和车辆停止前行

3 由Spring所发布的预定义事件

Spring 提供了一些预定义的事件,如下图所示:

但有些事件在 ApplicationContext 创建之前就已触发,该如何观测它们呢?

3.1 定义事件监听器

代码语言:javascript
复制
@Slf4j
public class SpringBuiltInEventsListener implements ApplicationListener<ApplicationEvent> {
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        log.info("event = {}", event);
    }
}

3.2 向SpringApplication注册事件监听器

代码语言:javascript
复制
@SpringBootApplication(scanBasePackages = "pers.duxiaotou")
public class DuXiaoTouSpringAopApp {
    public static void main(String[] args) throws Throwable {
        SpringApplication springApplication = new SpringApplication(DuXiaoTouSpringAopApp.class);
        springApplication.addListeners(new SpringBuiltInEventsListener());
        springApplication.run(args);
    }
}

3.3 运行结果

代码语言:javascript
复制
2021-08-18 17:14:43.182  INFO 15624 --- [           main] p.d.e.e.SpringBuiltInEventsListener      : event = org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent[source=org.springframework.boot.SpringApplication@2a5b3fee]
2021-08-18 17:14:43.291  INFO 15624 --- [           main] p.d.e.e.SpringBuiltInEventsListener      : event = org.springframework.boot.context.event.ApplicationContextInitializedEvent[source=org.springframework.boot.SpringApplication@2a5b3fee]
2021-08-18 17:14:43.417  INFO 15624 --- [           main] p.d.e.e.SpringBuiltInEventsListener      : event = org.springframework.boot.context.event.ApplicationPreparedEvent[source=org.springframework.boot.SpringApplication@2a5b3fee]
2021-08-18 17:14:45.865  INFO 15624 --- [   event-pool-0] p.d.e.e.SpringBuiltInEventsListener      : event = org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent[source=org.springframework.boot.web.embedded.tomcat.TomcatWebServer@4ca6ee9b]
2021-08-18 17:14:45.872  INFO 15624 --- [   event-pool-0] p.d.e.e.SpringBuiltInEventsListener      : event = org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@54d18072, started on Wed Aug 18 17:14:43 CST 2021]
2021-08-18 17:14:45.873  INFO 15624 --- [   event-pool-0] p.d.e.e.SpringBuiltInEventsListener      : event = org.springframework.boot.context.event.ApplicationStartedEvent[source=org.springframework.boot.SpringApplication@2a5b3fee]
2021-08-18 17:14:45.873  INFO 15624 --- [   event-pool-0] p.d.e.e.SpringBuiltInEventsListener      : event = org.springframework.boot.availability.AvailabilityChangeEvent[source=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@54d18072, started on Wed Aug 18 17:14:43 CST 2021]
2021-08-18 17:14:45.875  INFO 15624 --- [   event-pool-1] p.d.e.e.SpringBuiltInEventsListener      : event = org.springframework.boot.context.event.ApplicationReadyEvent[source=org.springframework.boot.SpringApplication@2a5b3fee]
2021-08-18 17:14:45.875  INFO 15624 --- [   event-pool-1] p.d.e.e.SpringBuiltInEventsListener      : event = org.springframework.boot.availability.AvailabilityChangeEvent[source=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@54d18072, started on Wed Aug 18 17:14:43 CST 2021]

4 实现原理

在 Spring 事件机制中,ApplicationEventPublisher负责发布事件,其只定义了两个publishEvent()方法,如下:

代码语言:javascript
复制
public interface ApplicationEventPublisher {
 default void publishEvent(ApplicationEvent event) {
  publishEvent((Object) event);
 }
 void publishEvent(Object event);
}

ApplicationEventPublisher 的实现类几乎都以ApplicationContext结尾,这难道有什么玄机吗?Spring 事件机制是为同一ApplicationContext中多个Bean之间的通信而设计的,而 ApplicationContext 在 Spring 中扮演IoC容器的角色,负责Bean全生命周期管理,那发布事件当然交由ApplicationContext最合适了啊;其中最为重要的非AbstractApplicationContext莫属!废话不多说,上代码:

代码语言:javascript
复制
public abstract class AbstractApplicationContext
        extends DefaultResourceLoader
        implements ConfigurableApplicationContext {
    // 事件广播器的名称必须指定为applicationEventMulticaster,源码中就是这么限定的
    // 如果不按规矩来,那自定义的事件广播器屁用没有
    public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";

    // 事件广播器,负责向所有事件监听器广播事件
    private ApplicationEventMulticaster applicationEventMulticaster;

    private final Object startupShutdownMonitor = new Object();

    @Override
    public void publishEvent(ApplicationEvent event) {
        publishEvent(event, null);
    }
    @Override
    public void publishEvent(Object event) {
        publishEvent(event, null);
    }

    // 发布事件核心逻辑
    protected void publishEvent(Object event, ResolvableType eventType) {
        ApplicationEvent applicationEvent;
        if (event instanceof ApplicationEvent) {
            applicationEvent = (ApplicationEvent) event;
        } else {
            // 如果所发布的事件不是ApplicationEvent类型,那么默认将其包装为ApplicationEvent类型
            applicationEvent = new PayloadApplicationEvent<>(this, event);
            if (eventType == null) {
                eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
            }
        }
        // 通过事件广播器广播事件,广播给谁?事件监听器
        this.applicationEventMulticaster.multicastEvent(applicationEvent, eventType);
    }

    /**
     * 这个方法容易忽略,请重点关注以下两个方法,相信看了之后应该能解决你的疑惑···
     * 1) initApplicationEventMulticaster()
     * 2) registerListeners()
     */
    @Override
    public void refresh() {
        synchronized (this.startupShutdownMonitor) {
            // ...

            // 初始化事件广播器
            initApplicationEventMulticaster();
            // 注册事件监听器
            registerListeners();

            // ...
        }
    }

    // 初始化事件广播器
    protected void initApplicationEventMulticaster() {
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
            // 如果开发者自定义了名称为applicationEventMulticaster的事件广播器,则优先使用开发者自定义的
            this.applicationEventMulticaster = beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
        } else {
            // 如果开发者没有自定义或者定义了名称不是applicationEventMulticaster的事件广播器,则默认使用SimpleApplicationEventMulticaster
            this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
            beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
        }
    }

    // 注册事件监听器
    protected void registerListeners() {
        // 将事件监听器注册到事件广播器中
        // 但这里的事件监听器专指静态事件监听器,即META-INF下spring.factories配置文件中的事件监听器
        // 比如:spring-boot、jasypt-spring-boot等模块中均配置了静态的事件监听器
        for (ApplicationListener<?> listener : getApplicationListeners()) {
            this.applicationEventMulticaster.addApplicationListener(listener);
        }
        
        // 同样是将事件监听器注册到事件广播器中
        // 但这里主要指那些实现ApplicationListener接口的事件监听器哦
        String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
        for (String listenerBeanName : listenerBeanNames) {
            this.applicationEventMulticaster.addApplicationListenerBean(listenerBeanName);
        }
    }
}

从 AbstractApplicationContext 的源码来看,主要交代了两件事:

  1. AbstractApplicationContext 是实现事件发布的真正大佬,但发布事件并没有直接与事件监听器耦合,而是通过事件广播器来桥接;
  2. 当启动 Spring Boot 时会触发针对 ApplicationContext 的refresh()操作,而refresh()首先会初始化事件广播器,然后注册事件监听器;注意:registerListeners()中所涉及的getApplicationListeners()方法其实只是直接拿到事件监听器列表而已,真正从spring.factories配置文件中读取事件监听器的触发时机是在实例化SpringApplication的时候。眼尖的朋友可能会问:由@EventListener定义的事件监听器是如何注册到事件广播器中的呢?的确,这里不涉及基于注解模型的事件监听器的注册工作,这些事件监听器由EventListenerMethodProcessor来完成注册工作,最终会将其封装为ApplicationListenerMethodAdapter类型的监听器,但依然是ApplicationListener的子类。

接下来,自然要来认识一下ApplicationEventMulticaster大佬了。事件广播器不仅负责广播事件,同时主导Spring和开发者所定义的事件监听器的维护工作。

代码语言:javascript
复制
public interface ApplicationEventMulticaster {
    void addApplicationListener(ApplicationListener<?> listener);
    void addApplicationListenerBean(String listenerBeanName);
    void removeApplicationListener(ApplicationListener<?> listener);
    void removeApplicationListenerBean(String listenerBeanName);
    void removeApplicationListeners(Predicate<ApplicationListener<?>> predicate);
    void removeApplicationListenerBeans(Predicate<String> predicate);
    void removeAllListeners();
    void multicastEvent(ApplicationEvent event);
    void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);
}

Spring 事件机制默认采用SimpleApplicationEventMulticaster来作为事件广播器,其源码过于直白,这里就直接奉上源码不再解读了。

代码语言:javascript
复制
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
    // 线程池,用于实现异步监听器
    private Executor taskExecutor;

    private ErrorHandler errorHandler;

    @Override
    public void multicastEvent(ApplicationEvent event) {
        multicastEvent(event, resolveDefaultEventType(event));
    }

    @Override
    public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
        ResolvableType type = (eventType != null ? eventType : ResolvableType.forInstance(event);
        Executor executor = getTaskExecutor();
        // 事件广播器会遍历所有事件监听器,逐一向其发布事件
        for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
            if (executor != null) {
                executor.execute(() -> invokeListener(listener, event));
            } else {
                invokeListener(listener, event);
            }
        }
    }

    protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
        ErrorHandler errorHandler = getErrorHandler();
        if (errorHandler != null) {
            try {
                doInvokeListener(listener, event);
            }
            catch (Throwable err) {
                errorHandler.handleError(err);
            }
        } else {
            doInvokeListener(listener, event);
        }
    }

    private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
        try {
            listener.onApplicationEvent(event);
        } catch (ClassCastException ex) {
            throw ex;
        }
    }
}

5 总结

6 参考文档

  1. https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#context-functionality-events
  2. https://refactoring.guru/design-patterns/observer
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-08-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序猿杜小头 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一文读懂Spring事件机制
    • 1 从实战出发
      • 1.1 定义事件
      • 1.2 定义事件监听器
      • 1.3 定义事件源
      • 1.4 运行结果
    • 2 异步事件监听器
      • 2.1 配置自定义事件广播器
      • 2.2 运行结果
    • 3 由Spring所发布的预定义事件
      • 3.1 定义事件监听器
        • 3.2 向SpringApplication注册事件监听器
        • 3.3 运行结果
      • 4 实现原理
        • 5 总结
          • 6 参考文档
          相关产品与服务
          容器服务
          腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档