[译]谈谈SpringBoot 事宜机制

要“监听”事宜,我们总是可以将“监听器”作为事宜源中的另一个方式写入事宜,但这将使事宜源与监听器的逻辑慎密耦合。

对于现实事宜,我们比直接方式挪用更天真。我们可以凭据需要动态注册和注销某些事宜的侦听器。我们还可以为统一事宜设置多个侦听器。

本教程概述了若何公布和侦听自界说事宜,并注释了 Spring Boot 的内置事宜。

为什么我应该使用事宜而不是直接方式挪用?

事宜和直接方式挪用都适合于差别的情形。使用方式挪用,就像断言一样-无论发送和吸收模块的状态若何,他们都需要知道此事宜的发生。

对于事宜,另一方面,我们只知道发生了一个事宜,哪些模块会被通知并不是我们体贴的问题。当我们想要将某些营业处置传递给另一个线程时(例如:在某些义务完成时发送电子邮件),最好使用事宜。此外,事宜对于测试驱动的开发也很有用。

什么是应用程序事宜( Application Events)?

Spring 应用程序事宜允许我们发送和吸收特定应用程序事宜,我们可以凭据需要处置这些事宜。事宜用于在松散耦合的组件之间交流信息。由于公布者和订阅者之间没有直接耦合,因此可以在不影响公布者的情形下修改订阅者,反之亦然。

让我们看看若何在 Spring Boot 应用程序中建立、公布和侦听自界说事宜。

建立ApplicationEvent

我们可以使用 Spring Framework 的事宜公布机制公布应用程序事宜。

让我们通过扩展来建立挪用的自界说事宜:

class UserCreatedEvent extends ApplicationEvent {
  private String name;

  UserCreatedEvent(Object source, String name) {
    super(source);
    this.name = name;
  }
  ...
}

代码中super(source)中的source应该是最初发生事宜的工具或与事宜相关联的工具。

从Spring 4.2最先,我们还可以将工具公布为事宜,而无需扩展ApplicationEvent:

class UserRemovedEvent {
  private String name;

  UserRemovedEvent(String name) {
    this.name = name;
  }
  ...
}

公布一个ApplicationEvent

我们使用ApplicationEventPublisher接口公布事宜:

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

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

当我们公布的工具不是ApplicationEvent时,Spring会自动为我们将其包装在PayloadApplicationEvent中。

吸收应用程序事宜

现在,我们知道若何建立和公布自界说事宜,让我们看看若何侦听该事宜。事宜可以有多个侦听器而且凭据应用程序要求执行差别的事情。

有两种方式可以界说侦听器。我们可以使用注解(@EventListener)或实现接口(ApplicationListener)。在这两种情形下,侦听器类都必须由 Spring 治理。

注解

从Spring 4.1最先,可以使用@EventListener注解的方式,以自动注册与该方式署名匹配的ApplicationListener:

@Component
class UserRemovedListener {

  @EventListener
  ReturnedEvent handleUserRemovedEvent(UserRemovedEvent event) {
    // handle UserRemovedEvent ...
    return new ReturnedEvent();
  }

  @EventListener
  void handleReturnedEvent(ReturnedEvent event) {
        // handle ReturnedEvent ...
  }
  ...
}

启用注解驱动的设置时,不需要其他设置。我们的方式可以监听多个事宜,或者若是我们想完全不使用任何参数来界说它,那么事宜类型也可以在注解本身上指定。示例:@EventListener({ContextStartedEvent.class,ContextRefreshedEvent.class})。

对于使用@EventListener注解并界说为具有返回类型的方式,Spring会将效果作为新事宜公布给我们。在上面的示例中,第一个方式返回的ReturnedEvent将被公布,然后由第二个方式处置。

若是指定SpEL条件,Spring仅在某些情形下才允许触发我们的侦听器:

@Component
class UserRemovedListener {

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

仅当表达式的计算效果为true或以下字符串之一时才处置该事宜:“ true”,“ on”,“ yes”或“ 1”。方式参数通过其名称公然。条件表达式还公然了一个“ root”变量,该变量引用原始ApplicationEvent(#root.event)和现实方式参数(#root.args)

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

实现ApplicationListener接口

侦听事宜的另一种方式是实现ApplicationListener接口:

@Component
class UserCreatedListener implements ApplicationListener<UserCreatedEvent> {

  @Override
  public void onApplicationEvent(UserCreatedEvent event) {
    // handle UserCreatedEvent
  }
}

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

异步事宜侦听器

默认情形下,spring事宜是同步的,这意味着公布者线程将壅闭,直到所有侦听器都完成对事宜的处置为止。

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

@Component
class AsyncListener {

  @Async
  @EventListener
  void handleAsyncEvent(String event) {
    // handle event
  }
}

为了使@Async注解起作用,我们还必须使用@EnableAsync注解我们的@Configuration类之一或@SpringBootApplication类。

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

Transaction-绑定事宜

Spring允许我们将事宜侦听器绑定到当前事务的某个阶段。若是当前事务的效果对侦听器很主要时,这使事宜可以更天真地使用。

当我们使用@TransactionalEventListener注释方式时,我们将获得一个扩展的事宜侦听器,该侦听器可以领会事务:

用Taro做个微信小程序Todo, 小白工作记录

@Component
class UserRemovedListener {

  @TransactionalEventListener(phase=TransactionPhase.AFTER_COMPLETION)
  void handleAfterUserRemoved(UserRemovedEvent event) {
    // handle UserRemovedEvent
  }
}

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

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

AFTER_COMMIT:事务乐成提交后,将处置该事宜。若是事宜侦听器仅在当前事务乐成时才运行,则可以使用此方式。

AFTER_COMPLETION:事务提交或回滚时将处置该事宜。例如,我们可以使用它在事务完成后执行清算。

AFTER_ROLLBACK:事务回滚后将处置该事宜。

BEFORE_COMMIT:该事宜将在事务提交之前举行处置。例如,我们可以使用它来将事务性ORM会话刷新到数据库。

Spring Boot的 Application Events

Spring Boot提供了几个与SpringApplication生命周期相关的预界说ApplicationEvent。

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

@SpringBootApplication
public class EventsDemoApplication {

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

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

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

class SpringBuiltInEventsListener 
    implements ApplicationListener<SpringApplicationEvent>{

  @Override
  public void onApplicationEvent(SpringApplicationEvent event) {
    // handle event
  }
}

确定事宜监听器已准确注册后,便可以监听所有Spring Boot的SpringApplicationEvents。让我们根据它们在应用程序启动过程中的执行顺序来举行考察。

ApplicationStartingEvent

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

ApplicationEnvironmentPreparedEvent

当上下文中使用的环境可用时,将触发ApplicationEnvironmentPreparedEvent。

由于此时环境已准备就绪,因此我们可以在其他Bean使用它之前对其举行检查和修改。

ApplicationContextInitializedEvent

当ApplicationContext准备就绪而且挪用ApplicationContextInitializers但尚未加载bean界说时,将触发ApplicationContextInitializedEvent。

在bean初始化到Spring容器之前,我们可以使用它来执行义务。

ApplicationPreparedEvent

准备好ApllicationContext但未刷新时会触发ApplicationPreparedEvent。

该环境已准备就绪,可以使用,而且将加载Bean界说。

WebServerInitializedEvent

若是我们使用的是网络服务器,则在网络服务器准备就绪后会触发WebServerInitializedEvent。 ServletWebServerInitializedEvent和ReactiveWebServerInitializedEvent分别是servlet和反应式网络服务。

WebServerInitializedEvent不扩展SpringApplicationEvent。

ApplicationStartedEvent

在刷新上下文之后但在挪用任何应用程序和命令行运行程序之前,将触发ApplicationStartedEvent。

ApplicationReadyEvent

触发ApplicationReadyEvent来指示该应用程序已准备就绪,可以处置请求。

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

ApplicationFailedEvent

若是存在异常而且应用程序无法启动,则会触发ApplicationFailedEvent。在启动时代的任何时间都可能发生这种情形。

我们可以使用它来执行一些义务,例如执行剧本或在启动失败时发出通知。

结论

事宜是为在统一应用程序上下文内的Spring Bean之间举行简朴通讯而设计的。从Spring 4.2最先,基础结构已得到显着改善,并提供了基于注释的模子以及公布随便事宜的功效。

英文原文:https://reflectoring.io/spring-boot-application-events-explained/

关注笔者民众号,推送各种原创/优质技术文章 ⬇️

[译]谈谈SpringBoot 事宜机制

原创文章,作者:870t新闻网,如若转载,请注明出处:https://www.870t.com/archives/5395.html