Spring事件发布和监听机制详解

在软件设计和开发过程中,事件驱动是一种非常主流的架构模式,它的基本组成见下图,可以看到存在一个事件中心,而各个服务可以执行事件发布、订阅和消费等基本过程。

   

事件驱动架构代表的是一种架构设计风格,实现方法和工具有很多。今天的内容将围绕Spring框架来介绍它的事件发布和监听机制。事实上,事件处理也是Spring内置的一个功能强大的扩展点,这种扩展点实现机制更加灵活,但也更加复杂。

对于所应该具备的技术组件而言,事件驱动架构与观察者模式比较类似,基本上都采用了如下图所示的组件结构。


作为类比,观察者模式中的主题就相当于是上图的事件(Event),观察者则对应事件监听器(EventListener)。而这里的事件发布器(EventPublisher)用来发布事件,并管理着一组事件监听器。一旦某个事件被发布,那么对它感兴趣的监听器就会监听到该事件,并触发事先定义好的处理逻辑。

Spring框架采用的也是与上图类似的设计方法。接下来,让我们看一下Spring中对整个事件驱动架构的抽象和实现过程。

Spring事件发布和监听机制

用户发布事件的ApplicationEventPublisher和用于监听事件的ApplicationListener是Spring框架中实现事件驱动编程的核心类。我们知道在ApplicationContext的整个生命周期中,每一个阶段或操作的开始和结束都可以触发一种事件,这些事件被封装成ApplicationEvent对象。通过这些接口,可以实现针对ApplicationContext中各种事件的处理。


一旦一个Bean实现了ApplicationListener接口,那么每当ApplicationEventPublisher发布事件时,这个Bean将自动触发监听器处理程序。ApplicationListener接口定义如下所示。

public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

void onApplicationEvent(E event);

}

有了事件的监听器,就有事件的发布器。在Spring中,事件发布器由ApplicationEventPublisher接口进行表示,该接口定义如下所示。

public interface ApplicationEventPublisher {

void publishEvent(ApplicationEvent event);

}

请注意,ApplicationContext接口本身就继承了这个ApplicationEventPublisher接口,意味着所有ApplicationContext的实现类都具有发布事件的能力。我们可以通过一个简单的代码示例来实现事件发布和消费的完整流程,如下所示。

public class EmailEvent extends ApplicationEvent{

}

public class SpringTest {

public static void main(String args[]){

ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");

//创建一个ApplicationEvent对象

EmailEvent event = new EmailEvent(…);

//主动触发该事件

context.publishEvent(event);

}

}

public class EmailNotifier implements ApplicationListener{

public void onApplicationEvent(ApplicationEvent event) {

 //监听EmailEvent事件

if (event instanceof EmailEvent) {

//处理EmailEvent事件

EmailEvent emailEvent = (EmailEvent)event;

} else {

}

}

}

在上述代码示例中,我们使用了一个自定义事件EmailEvent。事实上,Spring框架中已经内置了一批常见的事件,其中最常用的就是ContextRefreshedEvent。顾名思义,每当ApplicationContext被刷新时,Spring就会发布ContextRefreshedEvent事件。

我们在AbstractApplicationContext的refresh方法中看到有一个finishRefresh方法,该方法中包含了如下所示的核心代码。

protected void finishRefresh() {

// 发布ContextRefreshedEvent事件

publishEvent(new ContextRefreshedEvent(this));

}

可以看到这里通过这里publishEvent方法语句发布了一个ContextRefreshedEvent。当然,我们也可以直接使用refresh方法来触发该事件。


ContextRefreshedEvent是一个非常常用和重要的事件,我们在后续介绍Dubbo与Spring框架之间的整合过程时还会提到。

另外诸如ContextStartedEvent、ContextStoppedEvent、ContextClosedEvent等事件会在Spring容器的生命周期处于启动、停止以及关闭阶段自动触发。如果我们对这些事件感兴趣,就可以直接实现ApplicationListener接口从而对具体某一个事件进行响应。例如,如果我们想对ContextStoppedEvent事件进行响应,可以使用如下所示的实现方法。

@Component

public class TestApplicationListener implements ApplicationListener<ContextStoppedEvent>{

    @Override

    public void onApplicationEvent(ContextStoppedEvent contextStoppedEvent) {

        System.out.println(contextStoppedEvent);

    }

}

另外,我们还可以使用一个@EventListener注解来对整个事件监听的开发过程进行简化。使用该注解时,我们可以不需要显式实现ApplicationListener接口就可以消费事件。例如,如下代码完成了与前面代码同样的操作。

@Component

public class TestApplicationListener

@EventListener(classes={ ContextStoppedEvent.class})

public void listen(ContextStoppedEvent contextStoppedEvent){

System.out.println(contextStoppedEvent);

}

}

ApplicationListener实现原理

介绍完Spring事件发布和监听的基本流程之后,我们来进一步剖析它的实现原理。前面提到,ApplicationContext接口本身就继承了这个ApplicationEventPublisher接口,因此在AbstractApplicationContext中我们就会发现如下所示的publishEvent方法。

public void publishEvent(ApplicationEvent event) {

getApplicationEventMulticaster().multicastEvent(event);

if (this.parent != null) {

this.parent.publishEvent(event);

}

}

显然,对于一个事件而言,可以存在多个消费者。所以上述代码中首先获取了Spring中的事件多播器ApplicationEventMulticaster,并调用它的事件多播方法multicastEvent实现事件的多播发送。我们不难明白在ApplicationEventMulticaster中维护着一个ApplicationListener列表,并能实现对这些ApplicationListener发送事件。


SimpleApplicationEventMulticaster是ApplicationEventMulticaster的最终实现类,它的multicastEvent方法实现如下所示。

public void multicastEvent(final ApplicationEvent event) {

for (final ApplicationListener listener : getApplicationListeners(event)) {

//获取线程执行器

Executor executor = getTaskExecutor();

if (executor != null) {

//执行监听线程

executor.execute(new Runnable() {

public void run() { listener.onApplicationEvent(event);

}

});

}

else {

listener.onApplicationEvent(event);

}

}

}

这里通过JDK提供的Executor为每个ApplicationListener启动一个独立的线程,并在该线程中回调了onApplicationEvent方法。

我们已经明白了事件发布的独立过程,现在回到Spring容器来获取用于事件发布的ApplicationEventMulticaster以及各个用于事件监听的ApplicationListener的具体方式。让我们再次来到已经多次强调的AbstractApplicationContext中的refresh方法,这其中有两个步骤与事件相关,即initApplicationEventMulticaster和registerListeners。

public void refresh() throws BeansException, IllegalStateException {

try {

              ...

// 初始化自定义事件广播器

initApplicationEventMulticaster();

// 执行刷新

onRefresh();

// 注册监听器

registerListeners();

...

}

}

initApplicationEventMulticaster方法比较简单,先判断容器中是否实现了一个自定义的ApplicationEventMulticaster,如果有就直接使用;反之就会创建一个新的SimpleApplicationEventMulticaster。

另一个registerListeners方法则获取当前上下文中的所有ApplicationListener并添加到ApplicationEventMulticaster中。那么这些ApplicationListener是什么时候添加到当前上下文中的呢?显然,最合理的时机就是在Bean初始化之后。

至此,整个Spring的事件发布和监听机制已经介绍完毕。作为总结,我们可以通过一张图描述整个过程中所涉及的类层关系。


应该说,Spring中的事件处理机制虽然有点复杂,但功能丰富,架构设计也很优雅。上图中所展示的类层结构为开发人员自己实现一套自定义的事件驱动架构提供了很好的参考价值。

Spring事件处理机制在Dubbo框架中的应用

现在,我们已经理解了Spring中如何实现事件发布和监听的过程,接下来让我们看看它在开源框架中的实际应用。今天我们讨论框架仍然是Dubbo。

我们来到Dubbo服务端的启动类ServiceBean,该类的定义如下所示。

public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean, ApplicationContextAware, ApplicationListener<ContextRefreshedEvent>, BeanNameAware

显然,ServiceBean实现了Spring的ApplicationListener等接口,该接口的onApplicationEvent方法如下所示。代码X。

public void onApplicationEvent(ContextRefreshedEvent event) {

        if (isDelay() && !isExported() && !isUnexported()) {           

            export();

        }

}

可以看到Dubbo的ServiceBean类从ApplicationContext监听ContextRefreshedEvent事件。也就是说,只要Spring容器发布了ContextRefreshedEvent事件,Dubbo接收到该事件之后就会执行export方法,这个export方法会完成Dubbo服务的对外发布。

那么,Spring在什么时候会发布ContextRefreshedEvent事件呢?这个问题的答案,实际上在我们前面的讨论中已经明确了。一旦AbstractApplicationContext的refresh方法被调用,ContextRefreshedEvent事件就会被发布。作为总结,我们可以得到如下所示的执行流程图。


可以看到,Dubbo框架正是基于Spring框架所提供的事件发布和监听机制,完成了与Spring框架的无缝整合。

总结

通过今天内容的学习,我们掌握了Spring中事件处理的实现过程,这也为实现自定义的事件驱动架构提供了很好的基础,很多实现上的细节可供我们参考。在日常开发过程中,事件处理是一个常见的需求。在分布式环境下,我们可以通过消息中间件完成这个操作。而在单体系统内部,基于对本文中ApplicationEventPublisher和ApplicationListener这两个核心接口及其各个实现类的理解,我们可以抽象出更加轻量级的、符合具体业务场景的事件处理机制。

  • 20
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot的监听器是用于监听应用程序中特定事件的组件,当这些事件发生时,监听器会执行相应的逻辑。 下面是一个简单的使用Spring Boot监听器的示例: 1. 创建一个自定义的监听器类,实现Spring的ApplicationListener接口。例如: ```java public class MyApplicationListener implements ApplicationListener<ApplicationReadyEvent> { @Override public void onApplicationEvent(ApplicationReadyEvent event) { // 处理应用程序启动完成事件 System.out.println("应用程序已启动!"); } } ``` 2. 在Spring Boot应用程序的入口类中,通过注解@EnableAutoConfiguration或@SpringBootApplication启用自动配置,并通过@ComponentScan扫描自定义监听器类。例如: ```java @EnableAutoConfiguration @ComponentScan public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ``` 3. 运行应用程序时,自定义监听器的onApplicationEvent方法将会在应用程序启动完成时被调用。例如,当应用程序启动完成时,控制台将会打印出"应用程序已启动!"。 此外,Spring Boot还提供了其他类型的监听器,用于监听不同类型的事件,如应用程序启动前事件、应用程序关闭事件等。你可以根据需要实现不同的监听器,并注册到Spring Boot应用程序中。 总结:Spring Boot的监听器用于监听特定事件,并在事件发生时执行相应的逻辑。通过自定义监听器类和在入口类中注册监听器,我们可以方便地使用监听器来处理应用程序中的各种事件

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值