使用 Spring Event 解耦代码

Spring Event 是 Spring 框架的一个重要功能,它增强了不同组件之间的通信。该功能在灵活性方面超越了传统的方法调用,并且基于松耦合架构,减少了组件依赖性并简化了测试、维护和故障排除。

Spring Event 的好处

Spring Events 的主要优势在于其发布-订阅功能,允许动态添加或删除订阅者。这种事件驱动架构在组件之间有效地传输数据而不相互依赖,从而实现独立更改而不互相影响。此外,它允许跨多个组件同时进行逻辑调用。

另一个主要好处是它遵循开放-封闭原则:组件对扩展开放,但对修改封闭。

例如,假设我们需要处理客户订单,并根据某些条件发出不同的服务调用来提交订单信息或向客户发送电子邮件。通过直接方法调用,我们每次都需要对订单服务进行更改。同时,其他服务的变更,也会影响订单服务。

但是,通过使用事件系统,我们可以通过使用事件关闭订单服务以进行修改,并通过实现新的侦听器轻松扩展行为。

Spring Event 的核心组件

Spring Events 的核心是三个组件:事件、发布者和监听器。 ApplicationEvent 是一个简单的 POJO(普通 Java 对象),它在发布者和侦听器之间传输数据。发布者创建此事件对象并使用 ApplicationEventPublisher Bean 来发布它。侦听器可以通过多种方式实现,例如注释或通过实现 ApplicationListener 接口。 Spring 处理这些侦听器的注册,从而无需手动设置。

例子

为了说明前面提到的订单流的例子。假设我们收到客户的订单,我们通知外部系统已收到订单,并向用户发送电子邮件确认已收到订单。如果我们想通过传统的服务调用来实现这种情况,我们的代码将具有类似于下面的结构。

@Service
class OrderService(
    private val orderRepository: OrderRepository,
    private val externalService: ExternalService,
    private val emailService: EmailService
) {

    @Transactional
    suspend fun create(order: Order) {
        val createdOrder = orderRepository.save(order)
        externalService.registerOrder(createdOrder)
        emailService.send(order)
    }
}

现在,说明如何用 Spring Events 结构替换此代码。

首先,让我们创建一个 OrderCreatedEvent。

data class OrderCreatedEvent(
    val order: Order
)

然后,我们在 OrderService 中发布这个事件。

@Service  
class OrderService(  
    private val orderRepository: OrderRepository,  
    private val applicationEventPublisher: ApplicationEventPublisher  
) {  

 @Transactional
    suspend fun create(order: Order) {  
        val createdOrder = orderRepository.save(order)  
        applicationEventPublisher.publishEvent(OrderCreatedEvent(createdOrder))  
    }  
}

 

然后,我们创建了外部服务调用和电子邮件发送的侦听器。

@Component  
class OrderRegistrationListener(private val externalService: ExternalService) {  
  
    @EventListener(OrderCreatedEvent::class)  
    suspend fun onApplicationEvent(event: OrderCreatedEvent) {  
        externalService.registerOrder(event.order as Order)
    }  
}

@Component  
class EmailListener(private val emailService: EmailService) {  
  
    @EventListener(OrderCreatedEvent::class)  
    suspend fun onApplicationEvent(event: OrderCreatedEvent) {  
       emailService.send(event.order as Order)
    }  
}

现在,我们已经创建了订单,发布了 OrderCreatedEvent,添加了监听器,并操作了我们的业务逻辑。

还有一种情况我们需要注意,OrderService中的create方法是事务性的。

交易事务绑定事件

Spring 还有一个专门为事务处理实现的监听器,称为 TransactionalEventListener 。它有以下几个事件切入点:

  • BEFORE_COMMIT,该事件将在事务提交之前处理。
  • AFTER_COMMIT,这是使用的默认阶段。当事务成功提交时将处理该事件。
  • AFTE_ROLLBACK,该事件将在事务回滚后处理。
  • AFTER_COMPLETION,该事件将在事务提交或回滚时处理。

在开发我们的示例中演示的反应式应用程序时,需要进行以下代码更新,如此处所述。

@Configuration
class TransactionalEventPublisherConfiguration {

    @Bean
    fun transactionalEventPublisher(applicationEventPublisher: ApplicationEventPublisher) = TransactionalEventPublisher(applicationEventPublisher)

}


@Service
class OrderService(
    private val orderRepository: OrderRepository,
    private val transactionalEventPublisher: TransactionalEventPublisher
) {

    @Transactional
    suspend fun create(order: Order) {
        val createdOrder = orderRepository.save(order)
        transactionalEventPublisher.publishEvent(OrderCreatedEvent(createdOrder)).awaitSingleOrNull()
    }
}

修改 listener

@Component
class EmailListener(private val emailService: EmailService) {

    @TransactionalEventListener
    suspend fun onApplicationEvent(event: OrderCreatedEvent) {
        emailService.send(event.order)
    }
}

@Component
class OrderRegistrationListener(private val externalService: ExternalService) {

    @TransactionalEventListener
    suspend fun onApplicationEvent(event: OrderCreatedEvent) {
        externalService.registerOrder(event.order)
    }
}

一切都很好,我们按照到目前为止的步骤将直接服务调用示例转换成了 Spring 事件结构。

让我们做一个小小的补充。在我们的例子中,下单后我们会调用外部服务并发送电子邮件。假设首先进行外部调用,然后发送电子邮件。这种顺序可以使用 spring.common 的 @Order 注解来实现。

@Component
class OrderRegistrationListener(private val externalService: ExternalService) {

    @Order(1)
    @TransactionalEventListener
    suspend fun onApplicationEvent(event: OrderCreatedEvent) {
        externalService.registerOrder(event.order)
    }
}


@Component
class EmailListener(private val emailService: EmailService) {

    @Order(2)
    @TransactionalEventListener
    suspend fun onApplicationEvent(event: OrderCreatedEvent) {
        emailService.send(event.order)
    }
}

过滤事件

EventListener 适配了一个条件属性,该属性接受 Spring Expression Language (SpEL) 表达式。如果表达式评估为布尔值 true,或者以下字符串之一:true、on、yes 或 1,则事件将被处理。
默认表达式是一个空字符串,这意味着事件总是被处理。

假设您只想对属于优先级组的用户执行电子邮件监听器。我们可以通过实现 if 语句来编写这个逻辑。然而,过度使用条件会导致代码更加复杂且难以维护。

假设在 Order 中有一个布尔类型的 userPriority 参数,如果该参数为 true,则会发送电子邮件。
为此,我们可以按如下方式更新电子邮件监听器。

@Component
class EmailListener(private val emailService: EmailService) {

    @Order(2)
    @TransactionalEventListener(condition = "event.payload.order.priorityUser eq true")
    suspend fun onApplicationEvent(event: OrderCreatedEvent) {
        emailService.send(event.order)
    }
}

 

 

 

 

请登录后发表评论

    没有回复内容