Spring 框架 4.2 中改进的应用程序事件

工程 | Stéphane Nicoll | 2015 年 2 月 11 日 | ...

应用程序事件从 Spring 框架的最初版本开始就可用,作为松耦合组件交换信息的途径。应用程序事件最著名的用法之一如下所示

@Component
public class MyListener 
        implements ApplicationListener<ContextRefreshedEvent> {
  
    public void onApplicationEvent(ContextRefreshedEvent event) {
        ...
    }
}

这允许 MyListener 在上下文已刷新时收到通知,并且可以使用它在应用程序上下文完全启动时运行任意代码。

在 Spring Framework 4.2 中,我们重新审视了事件基础架构的三个主要方面,我将在本文中进行解释。

泛型支持

现在可以在事件类型中使用嵌套泛型信息来定义您的 ApplicationListener 实现,例如

public class MyListener 
        implements ApplicationListener<MyEvent<Order>> { ... }

分发事件时,将使用侦听器的签名来确定它是否与传入的事件匹配。

由于类型擦除,您需要发布一个解析您要过滤的泛型参数的事件,例如 MyOrderEvent extends MyEvent<Order>。可能还有其他解决方法,如果社区认为值得,我们很乐意重新审视签名匹配算法。

基于注解的事件侦听器

最大的新特性是支持基于注解的事件侦听器,类似于我们最近在 Spring Framework 4.1 中对 JMS 和 AMQP 端点 所做的工作。简而言之,现在可以简单地用 @EventListener 注解托管 Bean 的方法,以自动注册与方法签名匹配的 ApplicationListener。我们上面的示例可以改写如下

@Component
public class MyListener {
  
    @EventListener
    public void handleContextRefresh(ContextRefreshedEvent event) {
        ...
    }
}

@EventListener 是一个核心注解,以类似于 @Autowired 和其他注解的方式透明地处理:使用 java 配置不需要额外的配置,并且现有的 <context:annotation-driven/> 元素启用了对它的完全支持。

方法签名定义了您感兴趣的事件类型。还可以定义一个 SpEL 表达式,该表达式应匹配以处理事件。例如,考虑以下事件

public class OrderCreatedEvent implements CreationEvent<Order> { ... }

    private boolean awesome;
   
    public boolean isAwesome() { return this.awesome; }
    ....
}

以下示例展示了一个事件侦听器,该侦听器仅对 Order很棒的 CreationEvent(即,如果 awesome 标志为 true)被调用

@Component
public class MyComponent {
  
  @EventListener(condition = "#creationEvent.awesome")
  public void handleOrderCreatedEvent(CreationEvent<Order> creationEvent) {
    ... 
  }

}

从上面的示例中可以看到,如果可以发现此类信息,则方法参数通过其名称公开。条件表达式还公开了一个“根”变量,其中包含原始 ApplicationEvent#root.event)和实际的方法参数(#root.args)。

发布事件

您可以为用 @EventListener 注解的任何方法定义非 void 返回类型。如果您将非 null 值作为处理特定事件的结果返回,我们将为您将其作为新事件发送。

您可能已经注意到我们的 OrderCreatedEvent 没有扩展自 ApplicationEvent;我们认为是时候为您提供发布任何任意事件的灵活性,而不是强迫您扩展自 ApplicationEventApplicationEventPublisher 接口已扩展以允许您发布任何对象;当该对象不是 ApplicationEvent 时,我们将将其包装在 PayloadApplicationEvent 中。如果您想使用常规 ApplicationListener 实现来侦听此类任意事件,请记住这一点。

以下示例显示了如何使用 ApplicationEventPublisher 发送 OrderCreatedEvent

@Component
public class MyComponent {

    private final ApplicationEventPublisher publisher;
    
    @Autowired
    public MyComponent(ApplicationEventPublisher publisher) { ... }
    
    public void createOrder(Order order) {
        // ....
        this.publisher.publishEvent(new OrderCreatedEvent(order)); 
    }

}

事务绑定事件

另一个受欢迎的改进是能够将事件的侦听器绑定到事务的某个阶段。典型的示例是在事务成功完成时处理事件:这允许在当前事务的结果实际上对侦听器很重要时,更灵活地使用事件。

Spring Framework 当前的结构方式是上下文不知道事务支持,并且我们显然不想偏离这个非常合理的原则,因此我们构建了一个开放的基础架构,允许注册其他组件并影响事件侦听器创建的方式。

事务模块实现了一个 EventListenerFactory,它查找新的 @TransactionalEventListener 注解。当此注解存在时,会注册一个了解事务的扩展事件侦听器,而不是默认侦听器。

让我们重用上面的示例,并以这样一种方式重写它,即只有在生产者正在运行的事务成功完成时,才会处理订单创建事件

@Component
public class MyComponent {
  
  @TransactionalEventListener(condition = "#creationEvent.awesome")
  public void handleOrderCreatedEvent(CreationEvent<Order> creationEvent) { 
    ...
  }

}

没什么好看的,对吧?@TransactionalEventListener 一个常规的 @EventListener,并且还公开了 TransactionPhase,默认为 AFTER_COMMIT。您还可以挂钩事务的其他阶段(BEFORE_COMMITAFTER_ROLLBACKAFTER_COMPLETION,它只是 AFTER_COMMITAFTER_ROLLBACK 的别名)。

默认情况下,如果没有运行事务,则根本不会发送事件,因为我们显然无法遵守请求的阶段,但是 @TransactionalEventListener 中有一个 fallbackExecution 属性,它告诉 Spring 如果没有事务,则立即调用侦听器。

试一试!

如果您想在 4.2 的第一个里程碑版本发布之前试用,请通过我们的 快照存储库 获取一个每晚的 SNAPSHOT 构建。您还可以使用 start.spring.io 使用最新的 Spring Boot 快照构建创建示例项目,或者如果您非常懒惰,可以将此内容复制粘贴到您的 shell 中

$ curl https://start.spring.io/starter.tgz -d artifactId=events-demo \
    -d baseDir=events-demo -d bootVersion=1.2.2.BUILD-SNAPSHOT | tar -xzvf -

并将项目更新为使用 Spring Framework 4.2.0.BUILD-SNAPSHOT

<properties>
  ...
  <spring.version>4.2.0.BUILD-SNAPSHOT</spring.version>
</properties>

与往常一样,我们欢迎社区反馈,请尝试这些功能,并让我们知道您是否遇到任何问题。

获取 Spring 新闻通讯

与 Spring 新闻通讯保持联系

订阅

走在前面

VMware 提供培训和认证,以加快您的进步。

了解更多

获取支持

Tanzu Spring 在一个简单的订阅中提供对 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

即将举行的活动

查看 Spring 社区中所有即将举行的活动。

查看全部