在实际的业务开发中,我们经常使用MQ来进行业务的解耦合,比如电商场景下,下单是一个主流程,下单完成后发出下单成功的MQ,优惠券、京豆等业务系统对MQ进行消费,这样能够将下单核心流程与非核心流程进行业务解耦。
但除了跨应用的分布式系统需要使用MQ进行解耦之外,单一应用在某些业务场景下,对业务进行解耦也是一种常见的场景。
Spring Event作为Spring框架的一部分,为开发者提供了一种轻量级的事件驱动机制,能够有效地实现应用内的业务解耦。本文将深入探讨Spring Event的优势、实现方法及其在实际项目中的应用。
发布-订阅模式(Publish-Subscribe Pattern)是一种消息传递模式,在Java中通常用于实现松耦合的系统架构。在这种模式中,消息的发送者(发布者)和消息的接收者(订阅者)之间没有直接的联系,而是通过一个消息代理(通常称为事件总线或消息中间件)来进行通信。在Spring Boot中,发布-订阅模式通常是通过Spring的事件机制来实现的,允许组件之间进行松耦合的通信,即Spring Event。它主要包括以下几个核心部分:事件(Event)、事件发布者(Event Publisher)和事件监听器(Event Listener)。
这里以导航场景举例进行实现步骤的举例,当发生交通事故时,地图上需要显示交通事故图标以提醒用户,也需要发送通知给其他用户提醒其小心驾驶。
首先,定义一个事件类,继承自ApplicationEvent
。这个类用于封装要传递的信息。
import org.springframework.context.ApplicationEvent;
public class TrafficEvent extends ApplicationEvent {
/**
* source the object on which the event initially occurred or with
* which the event is associated
*/
private String message;
public TrafficEvent(Object source,String message) {
super(source);
this.message = message;
}
public String getMessage() {
return message;
}
}
创建一个Spring组件,用于发布事件。可以通过ApplicationEventPublisher
来发布事件。
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
@RequiredArgsConstructor
@Slf4j
@Component
public class TrafficPublish {
private final ApplicationEventPublisher publisher;
public void publish(String message){
TrafficEvent trafficEvent = new TrafficEvent(this,message);
publisher.publishEvent(trafficEvent);
}
}
创建一个事件监听器,使用@EventListener
注解来监听事件,也可以通过实现ApplicationListener
接口来进行监听。
@EventListener
注解import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class MapRenderListener {
@EventListener
public void onApplicationEvent1(TrafficEvent trafficEvent){
log.info("小程序地图渲染程序收到一条消息:source:{},time:{}",trafficEvent.getSource(),trafficEvent.getTimestamp());
log.info("消息内容:{}",trafficEvent.getMessage());
}
@EventListener
public void onApplicationEvent2(TrafficEvent trafficEvent){
log.info("App地图渲染程序收到一条消息:source:{},time:{}",trafficEvent.getSource(),trafficEvent.getTimestamp());
log.info("消息内容:{}",trafficEvent.getMessage());
}
}
ApplicationListener
接口import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class NoticeTrafficListener implements ApplicationListener<TrafficEvent> {
@Override
public void onApplicationEvent(TrafficEvent trafficEvent) {
log.info("导航应用收到一条消息:source:{},time:{}",trafficEvent.getSource(),trafficEvent.getTimestamp());
log.info("消息内容:{}",trafficEvent.getMessage());
}
}
在Spring Boot应用中使用发布者来发布事件,然后监听器会自动接收到事件。这里我们使用一个Controller 通过接收请求来发布事件。
@Controller
@CrossOrigin
public class ControllerTest {
@Autowired
private TrafficPublish trafficPublish;
@RequestMapping("/publish")
@ResponseBody
public String publish(String message){
trafficPublish.publish(message);
return "publish";
}
}
通过上面的四个步骤,日志打印(这里的日志打印使用了笔者自己封装的组件,可在方法执行前后对出入参及耗时进行打印)如下图所示:
publishEvent
方法是阻塞的通过打印的日志我们也可以看到ApplicationEventPublisher.publishEvent
方法是阻塞的,当ApplicationEventPublisher.publishEvent
方法发布一个事件后,会同步调用所有的事件监听者任务执行逻辑,事件的发布和所有监听者的任务调用都是在一个线程里面,为了防止发布消息的阻塞或者监听消息处理的阻塞,可以在监听消息内,使用独立的线程池进行执行。
Spring Framework 提供了一些内置事件,用于帮助开发者在应用程序上下文中处理常见的生命周期事件。这些内置事件包括:
ContextRefreshedEvent:当应用程序上下文初始化或刷新时发布该事件。这可以用于在应用程序完全启动后执行某些逻辑。
ContextStartedEvent:当应用程序上下文启动时发布该事件。通常用于在上下文启动后执行某些操作。
ContextStoppedEvent:当应用程序上下文停止时发布该事件。可以用于在应用程序停止时执行清理工作。
ContextClosedEvent:当应用程序上下文关闭时发布该事件。适合用于释放资源或执行关闭前的清理操作。
RequestHandledEvent(在 Spring MVC 中):当一个 HTTP 请求被处理后发布该事件。适合用于监控和统计请求处理情况。
ApplicationEvent
中的source继承 ApplicationEvent
后要重新父类的构造方法,并且source
字段不能为空,否则会抛出异常,并且source
字段只能在构造函数中进行传递。
监听器可以针对自己感兴趣的事件类型进行监听,如示例代码中的TrafficEvent
,如果想监听所有类型的事件,则可监听ApplicationEvent
。
因篇幅问题不能全部显示,请点此查看更多更全内容
怀疑对方AI换脸可以让对方摁鼻子 真人摁下去鼻子会变形
女子野生动物园下车狼悄悄靠近 后车司机按喇叭提醒
睡前玩8分钟手机身体兴奋1小时 还可能让你“变丑”
惊蛰为啥吃梨?倒春寒来不来就看惊蛰
男子高速犯困开智能驾驶出事故 60万刚买的奔驰严重损毁