Spring Boot应用程序事件教程 – reflectoring

如果要“监听”事件,我们可以在事件发生源处编写“监听器”来监听事件,但会将事件源与侦听器的逻辑紧密耦合。我们可以根据需要动态注册和注销某些事件的侦听器。对于同一事件,我们也可以有多个侦听器。本教程概述了如何发布和监听自定义事件,并解释了Spring Boot的内置事件。

事件与直接方法调用

事件和直接方法调用都适合于不同的情况。对于方法调用,这就像断言一样,无论发送和接收模块的状态如何,他们都需要知道此事件的发生。

另一方面,对于事件,我们只是说发生了一个事件,并且通知了哪些模块不是我们关心的问题。当我们想将处理传递给另一个线程时,最好使用事件(例如:在完成某些任务时发送电子邮件)。同样,事件对于测试驱动的开发非常有用。

事件用于在松耦合的组件之间交换信息。由于发布者和订阅者之间没有直接耦合,因此我们可以修改订阅者而不影响发布者,反之亦然。让我们看看如何在Spring Boot应用程序中创建,发布和收听自定义事件。

1. 创建一个 ApplicationEvent

我们可以使用Spring Framework的事件发布机制来发布应用程序事件。

让我们创建一个UserCreatedEvent通过扩展调用的自定义事件ApplicationEvent:

<b>class</b> UserCreatedEvent <b>extends</b> ApplicationEvent {
  <b>private</b> String name;

  UserCreatedEvent(Object source, String name) {
    <b>super</b>(source);
    <b>this</b>.name = name;
  }
  ...
}

source对象是事件发生时可以初始化和传递的参数,传递道super()方法。

从Spring 4.2开始,我们还可以将对象直接发布为事件,而无需扩展ApplicationEvent:

<b>class</b> UserRemovedEvent {
  <b>private</b> String name;

  UserRemovedEvent(String name) {
    <b>this</b>.name = name;
  }
  ...
}

2.发布一个 ApplicationEvent

我们使用ApplicationEventPublisher接口来发布事件:

@Component
<b>class</b> Publisher {
  
  <b>private</b> <b>final</b> ApplicationEventPublisher publisher;
    
    Publisher(ApplicationEventPublisher publisher) {
      <b>this</b>.publisher = publisher;
    }

  <b>void</b> publishEvent(<b>final</b> String name) {
    <font><i>// Publishing event created by extending ApplicationEvent</i></font><font>
    publisher.publishEvent(<b>new</b> UserCreatedEvent(<b>this</b>, name));
    </font><font><i>// Publishing an object as an event</i></font><font>
    publisher.publishEvent(<b>new</b> UserRemovedEvent(name));
  }
}
</font>

当我们发布的对象不是ApplicationEvent时,Spring会自动用PayloadApplicationEvent包装它

3. 监听事件

现在我们知道如何创建和发布自定义事件,让我们看看如何监听事件。一个事件可以有多个侦听器根据应用程序需求执行不同的工作。

有两种定义侦听器的方法。我们可以使用@EventListener注释或实现ApplicationListener接口。无论哪种情况,监听器类都必须由Spring管理

从Spring 4.1开始,现在可以简单地注释托管bean的方法,@EventListener以自动注册ApplicationListener与该方法的签名匹配的方法:

@Component
<b>class</b> UserRemovedListener {

  @EventListener
  ReturnedEvent handleUserRemovedEvent(UserRemovedEvent event) {
    <font><i>// handle UserRemovedEvent ...</i></font><font>
    <b>return</b> <b>new</b> ReturnedEvent();
  }

  @EventListener
  <b>void</b> handleReturnedEvent(ReturnedEvent event) {
        </font><font><i>// handle ReturnedEvent ...</i></font><font>
  }
  ...
}
</font>

启用注释驱动的配置时,不需要其他配置。我们的方法可以监听多个事件,或者如果我们想完全不使用任何参数来定义它,那么事件类型也可以在注释本身上指定。范例:@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})。

对于带有注释@EventListener的方法的返回类型如定义为非void,Spring会将结果作为新事件发布给我们。在上面的示例中,ReturnedEvent第一种方法返回的结果将被发布,然后由第二种方法处理。

如果指定SpEL,Spring仅在某些情况下允许触发我们的侦听器condition:

@Component
<b>class</b> UserRemovedListener {

  @EventListener(condition = <font>"#event.name eq 'reflectoring'"</font><font>)
  <b>void</b> handleConditionalListener(UserRemovedEvent event) {
    </font><font><i>// handle UserRemovedEvent</i></font><font>
  }
}
</font>

仅当表达式的计算结果为true,或包含以下字符串之一时:“true”, “on”, “yes”, 或“1”.方法参数通过其名称公开。条件表达式还公开了一个引用了raw ApplicationEvent(#root.event)和实际方法参数的“根”变量(#root.args)

在以上示例中,UserRemovedEvent仅当#event.name的值为时’reflectoring’,才会触发侦听器。

侦听事件的另一种方法是实现ApplicationListener接口:

@Component
<b>class</b> UserCreatedListener implements ApplicationListener<UserCreatedEvent> {

  @Override
  <b>public</b> <b>void</b> onApplicationEvent(UserCreatedEvent event) {
    <font><i>// handle UserCreatedEvent</i></font><font>
  }
}
</font>

只要侦听器对象在Spring应用程序上下文中注册,它就会接收事件。当Spring路由一个事件时,它使用侦听器的签名来确定它是否与事件匹配。

异步事件监听器

默认情况下,spring事件是同步的,这意味着发布者线程将阻塞,直到所有侦听器都完成对事件的处理为止。

要使事件侦听器以异步模式运行,我们要做的就是@Async在该侦听器上使用注释:

@Component
<b>class</b> AsyncListener {

  @Async
  @EventListener
  <b>void</b> handleAsyncEvent(String event) {
    <font><i>// handle event</i></font><font>
  }
}
</font>

为了使@Async注释生效,我们还必须注释一个@Configuration类,使用@EnableAsync注释SpringBootApplication类。

上面的代码示例还显示,我们可以将String用作事件。使用风险自负。最好使用特定于我们用例的数据类型,以免与其他事件冲突。

事务绑定事件

Spring允许我们将事件侦听器绑定到当前事务的某个阶段。当当前事务的结果对侦听器很重要时,这使事件可以更灵活地使用。

当我们使用注释我们的方法时@TransactionalEventListener,我们得到了一个扩展的事件监听器,该监听器知道事务:

@Component
<b>class</b> UserRemovedListener {

  @TransactionalEventListener(phase=TransactionPhase.AFTER_COMPLETION)
  <b>void</b> handleAfterUserRemoved(UserRemovedEvent event) {
    <font><i>// handle UserRemovedEvent</i></font><font>
  }
}
</font>

UserRemovedListener 仅在当前事务完成时才调用。

我们可以将侦听器绑定到事务的以下阶段:

  • AFTER_COMMIT:成功提交事务后,将处理该事件。如果事件侦听器仅在当前事务成功时才运行,则可以使用此方法。
  • AFTER_COMPLETION:在事务提交或回滚时将处理该事件。例如,我们可以使用它在事务完成后执行清理。
  • AFTER_ROLLBACK:交易回滚后,将处理该事件。
  • BEFORE_COMMIT:事件将在事务提交之前处理。例如,我们可以使用它来将事务性O / R映射会话刷新到数据库

Spring Boot的应用程序事件

以上是Spring事件,Spring Boot提供了几个预定义ApplicationEvent的,这些预定义绑定到SpringApplication生命周期。

在ApplicationContext创建之前会触发一些事件,因此我们无法将这些事件注册为@Bean。我们可以通过手动添加侦听器来注册这些事件的侦听器:

@SpringBootApplication
<b>public</b> <b>class</b> EventsDemoApplication {

  <b>public</b> <b>static</b> <b>void</b> main(String[] args) {
    SpringApplication springApplication = 
        <b>new</b> SpringApplication(EventsDemoApplication.<b>class</b>);
    springApplication.addListeners(<b>new</b> SpringBuiltInEventsListener());
    springApplication.run(args);
  }

}

通过将META-INF/spring.factories文件添加到我们的项目中,我们还可以注册侦听器,而不管如何创建应用的。并通过以下org.springframework.context.ApplicationListener键引用侦听器:

org.springframework.context.ApplicationListener= com.reflectoring.eventdemo.SpringBuiltInEventsListener

<b>class</b> SpringBuiltInEventsListener 
    implements ApplicationListener<SpringApplicationEvent>{

  @Override
  <b>public</b> <b>void</b> onApplicationEvent(SpringApplicationEvent event) {
    <font><i>// handle event</i></font><font>
  }
}
</font>

一旦确保正确注册了事件监听器,我们就可以监听所有Spring Boot的SpringApplicationEvents。让我们按照它们应用程序启动期间的执行顺序来看看:

ApplicationStartingEvent

ApplicationStartingEvent在运行开始时但在任何处理之前都会触发,除了侦听器和初始化程序的注册外。

ApplicationEnvironmentPreparedEvent

当Environment在上下文中是可用的,一个ApplicationEnvironmentPreparedEvent被触发,由于此时Environment将准备就绪,因此我们可以在其他bean使用它之前对其进行检查和修改。

ApplicationContextInitializedEvent

ApplicationContext已准备就绪时,一个ApplicationContextInitializedEvent触发,ApplicationContextInitializers被称为尚未加载bean定义。在bean初始化到Spring容器之前,我们可以使用它执行任务。

ApplicationPreparedEvent

当ApllicationContext准备就绪时,一个ApplicationPreparedEvent时会触发,但不会刷新。

在准备好的Environment和bean定义将被加载。

ContextRefreshedEvent

当ApplicationContext刷新时,ContextRefreshedEvent会触发。

ContextRefreshedEvent是直接来自Spring,而不是Spring Boot,并不继承扩展SpringApplicationEvent。

WebServerInitializedEvent

如果我们使用的是Web服务器,WebServerInitializedEvent则在Web服务器准备就绪后会触发a。ServletWebServerInitializedEvent和ReactiveWebServerInitializedEvent分别是servlet和反应式变量。

WebServerInitializedEvent不是继承扩展SpringApplicationEvent。

ApplicationStartedEvent

上下文已被刷新之后,一个ApplicationStartedEvent触发,但在任何Spring boot应用程序和命令行运行都被调用前。

ApplicationReadyEvent

一个ApplicationReadyEvent触发时就表示该应用程序已准备好服务请求。

建议此时不要修改内部状态,因为所有初始化步骤都将完成。

ApplicationFailedEvent

一个ApplicationFailedEvent如果有异常,应用程序无法启动点火。在启动期间的任何时间都可能发生这种情况。我们可以使用它来执行一些任务,例如执行脚本或在启动失败时发出通知。

结论

事件被设计为在同一应用程序上下文中在Spring bean之间进行简单的通信。从Spring 4.2开始,基础结构已得到显着改进,并提供了基于注释的模型以及发布任意事件的功能。

您可以 在GitHub
找到示例代码。

原文 

https://www.jdon.com/53971

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » Spring Boot应用程序事件教程 – reflectoring

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址