Spring系列——使用ApplicationEvent和Listener快速实现业务解耦

翻译自https://www.baeldung.com/spring-events

ApplicationEvent以及Listener是Spring为我们提供的一个事件监听、订阅的实现,内部实现原理是观察者设计模式,设计初衷也是为了系统业务逻辑之间的解耦,提高可扩展性以及可维护性。事件发布者并不需要考虑谁去监听,监听具体的实现内容是什么,发布者的工作只是为了发布事件而已。

1.自定义事件

1.1简单的应用事件

让我们创建一个简单的事件类 - 只是一个存储事件数据的占位符。在这种情况下,事件类包含String字符串:

public class CustomSpringEvent extends ApplicationEvent {
    private String message;
 
    public CustomSpringEvent(Object source, String message) {
        super(source);
        this.message = message;
    }
    public String getMessage() {
        return message;
    }
}
1.2发布者

现在让我们创建该事件的发布者。发布者构造事件对象并将其发布给正在收听的任何人。

要发布事件,发布者只需注入ApplicationEventPublisher并使用publishEvent() API:

@Component
public class CustomSpringEventPublisher {
    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;
 
    public void doStuffAndPublishAnEvent(final String message) {
        System.out.println("Publishing custom event. ");
        CustomSpringEvent customSpringEvent = new CustomSpringEvent(this, message);
        applicationEventPublisher.publishEvent(customSpringEvent);
    }
}

或者,发布者类可以实现ApplicationEventPublisherAware接口 - 这也将在应用程序启动时注入事件发布者。通常,只需向@Autowire注入发布者就更简单了。

1.3监听器

最后,让我们创建一个监听器。
监听器的唯一要求是成为bean并实现 ApplicationListener接口:

@Component
public class CustomSpringEventListener implements ApplicationListener<CustomSpringEvent> {
    @Override
    public void onApplicationEvent(CustomSpringEvent event) {
        System.out.println("Received spring custom event - " + event.getMessage());
    }
}

请注意我们的自定义侦听器如何使用泛型事件的泛型类型进行参数化 - 这使得onApplicationEvent()方法类型安全。这也避免了必须检查对象是否是特定事件类的实例并将其强制转换。

并且,正如已经讨论的那样 - 默认情况下,spring事件是同步的 - doStuffAndPublishAnEvent()方法会阻塞,直到所有侦听器都完成对事件的处理。

1.4创建异步事件

在某些情况下,同步发布事件并不是我们真正想要的 - 我们可能需要对事件进行异步处理。

您可以通过使用执行程序创建ApplicationEventMulticaster bean 来在配置中启用 它; 为了我们的目的,SimpleAsyncTaskExecutor运行良好:

@Configuration
public class AsynchronousSpringEventsConfig {
    @Bean(name = "applicationEventMulticaster")
    public ApplicationEventMulticaster simpleApplicationEventMulticaster() {
        SimpleApplicationEventMulticaster eventMulticaster 
          = new SimpleApplicationEventMulticaster();
         
        eventMulticaster.setTaskExecutor(new SimpleAsyncTaskExecutor());
        return eventMulticaster;
    }
}

这个事件,发布者和监听器实现与以前保持一致 - 但现在,监听器将在一个单独的线程中异步处理事件。

2.现有框架事件

2.1Spring 提供了以下的标准事件:
序号Spring 内置事件 & 描述
1ContextRefreshedEvent ApplicationContext 被初始化或刷新时,该事件被发布。这也可以在 ConfigurableApplicationContext 接口中使用 refresh() 方法来发生。
2ContextStartedEvent当使用 ConfigurableApplicationContext 接口中的 start() 方法启动 ApplicationContext 时,该事件被发布。你可以调查你的数据库,或者你可以在接受到这个事件后重启任何停止的应用程序。
3ContextStoppedEvent当使用 ConfigurableApplicationContext 接口中的 stop() 方法停止 ApplicationContext 时,发布这个事件。你可以在接受到这个事件后做必要的清理的工作。
4ContextClosedEvent当使用 ConfigurableApplicationContext 接口中的 close() 方法关闭 ApplicationContext 时,该事件被发布。一个已关闭的上下文到达生命周期末端;它不能被刷新或重启。
5RequestHandledEvent这是一个 web-specific 事件,告诉所有 bean HTTP 请求已经被服务。

这是一个监听上下文刷新的监听器的快速示例:

public class ContextRefreshedListener 
  implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent cse) {
        System.out.println("Handling context re-freshed event. ");
    }
}

3.Annotation-Driven 事件监听器

从Spring 4.2开始,事件监听器不需要是实现ApplicationListener接口的bean - 它可以通过@EventListener批注在托管bean的任何公共方法上注册:

@Component
public class AnnotationDrivenContextStartedListener {
    // @Async
    @EventListener
    public void handleContextStart(ContextStartedEvent cse) {
        System.out.println("Handling context started event.");
    }
}

和以前一样,方法签名声明它消耗的事件类型。和以前一样,这个监听器是同步调用的。但现在使其异步就像添加@Async注释一样简单 (不要忘记在应用程序中启用异步支持)。

4.泛型支持

4.1通用应用程序事件

让我们创建一个通用事件类型。在我们的示例中,事件类包含任何内容和成功状态指示符:

public class GenericSpringEvent<T> {
    private T what;
    protected boolean success;
 
    public GenericSpringEvent(T what, boolean success) {
        this.what = what;
        this.success = success;
    }
    // ... standard getters
}

注意GenericSpringEvent和CustomSpringEvent之间的区别。我们现在可以灵活地发布任何任意事件,并且不再需要从ApplicationEvent扩展。

4.2 监听器

现在让我们创建一个该事件的监听器。我们可以像之前一样实现ApplicationListener接口来定义监听器:

@Component
public class GenericSpringEventListener implements ApplicationListener<GenericSpringEvent<String>> {
    @Override
    public void onApplicationEvent(@NonNull GenericSpringEvent<String> event) {
        System.out.println("Received spring generic event - " + event.getWhat());
    }
}

但不幸的是,这个定义要求我们从 ApplicationEvent类继承GenericSpringEvent。因此,对于本教程,让我们使用前面讨论过的注释驱动的事件监听器。

通过在@EventListener注释上定义Boolean类型的EL表达式, 也可以使事件监听器成为条件。在这种情况下,只会为成功 的 String的GenericSpringEvent调用事件处理程序:

@Component
public class AnnotationDrivenEventListener {
    @EventListener(condition = "#event.success")
    public void handleSuccessful(GenericSpringEvent<String> event) {
        System.out.println("Handling generic event (conditional).");
    }
}
4.3 A publisher

事件发布者与上述类似。但是由于类型擦除,我们需要发布一个事件来解析我们要过滤的泛型参数。例如,GenericStringSpringEvent类扩展了GenericSpringEvent 。

还有另一种发布活动的方式。如果我们从使用@EventListener注释的方法返回非null值作为结果,Spring Framework将把结果作为新事件发送给我们。此外,作为事件处理的结果,我们可以通过将它们返回到集合中来发布多个新事件。

5.Transaction Bound Events

从Spring 4.2开始,框架提供了一个新的@TransactionalEventListener注释,它是@EventListener的扩展,允许将事件的侦听器绑定到事务的一个阶段。绑定可以进行以下事务阶段:

AFTER_COMMIT(默认值)用于在事务成功完成时触发事件
AFTER_ROLLBACK - 如果事务已回滚
AFTER_COMPLETION - 如果事务已完成(AFTER_COMMIT和 AFTER_ROLLBACK的别名)
BEFORE_COMMIT 用于在事务提交之前触发事件

以下是事务性事件监听器的快速示例:

@TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)
public void handleCustom(CustomSpringEvent event) {
    System.out.println("Handling event inside a transaction BEFORE COMMIT.");
}

代码请参考

我们在传统项目中往往各个业务逻辑之间耦合性较强,因为我们在service都是直接引用的关联service或者jpa来作为协作处理逻辑,然而这种方式在后期更新、维护性难度都是大大提高了。然而我们采用事件通知、事件监听形式来处理逻辑时耦合性则是可以降到最小。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值