什么是事件发布机制
事件发布是经典的工作模式,其本质是流程到达特定节点,需要处理的任务。
打个比方,篮球赛进行到中场,就会发布一个暂停休息的事件,球员们接收到这个事件后,就自行去休息,喝水,布置战术了。
最简陋的事件发布机制,可以理解为观察者模式。
typescript
// 观察者接口
interface Observer {
void update(String msg);
}
// 被观察者(事件发布者)
class Subject {
// 维护订阅者列表
private List<Observer> observers = new ArrayList<>();
// 订阅
public void attach(Observer o) {
observers.add(o);
}
// 发布事件,遍历通知所有订阅者
public void notifyObservers(String msg) {
for (Observer o : observers) {
o.update(msg);
}
}
}
// 具体订阅者A
class UserA implements Observer {
@Override
public void update(String msg) {
System.out.println("用户A收到:" + msg);
}
}
// 具体订阅者B
class UserB implements Observer {
@Override
public void update(String msg) {
System.out.println("用户B收到:" + msg);
}
}
// 测试
public class Demo {
public static void main(String[] args) {
Subject subject = new Subject();
subject.attach(new UserA());
subject.attach(new UserB());
// 发布消息,自动遍历通知列表
subject.notifyObservers("订单已支付");
}
}
但是这样的代码有一个问题,耦合性强。
第一,观察者需要被添加到观察者列表。
第二,事件发布的时候,发布者需要主动调用通知方法。
一旦有了耦合,那么代码维护和添加就是很麻烦的问题。
spring采用的是升级版,观察者模式+事件总线。
- 发布者只管发事件,完全不知道谁在监听。
scala
// 事件:只是一个数据载体
public class OrderPayEvent extends ApplicationEvent {
public OrderPayEvent(Object source) {
super(source);
}
}
typescript
@Service
public class OrderService {
// 注入事件发布器
@Autowired
private ApplicationEventPublisher publisher;
public void pay() {
System.out.println("主流程:订单支付完成");
// 只发事件!!!不知道谁会处理
publisher.publishEvent(new OrderPayEvent(this));
}
}
- 总线统一管理所有监听器,负责转发。
- 监听者只注册自己,处理自己的逻辑。(这里的监听器注册,又是我们的老朋友IOC)
typescript
@Service
public class SmsService {
// 监听事件,自动被Spring管理
@EventListener
public void handle(OrderPayEvent event) {
System.out.println("监听者:发送短信");
}
}
@Service
public class LogService {
@EventListener
public void handle(OrderPayEvent event) {
System.out.println("监听者:记录日志");
}
}
发布者和监听者直接零依赖。
spring自带哪些事件
spring自有的事件集中于启动流程中。
- ApplicationContextRefreshedEvent:容器刷新完成触发。容器刷新完毕,Bean 全部加载完成,做全局资源初始化、配置校验
- ContextStartedEvent:容器启动触发。应用启动阶段,打印启动日志、初始化连接池
- ContextStoppedEvent:应用暂停运行,不再接收请求
- ContextClosedEvent:容器关闭销毁触发。容器关闭,优雅释放资源:关闭连接、保存临时数据、注销服务
- ApplicationStartedEvent:容器刷新完毕,应用启动流程走完,还未对外接收请求。应用启动阶段,打印启动日志、初始化连接池
- ApplicationReadyEvent:内嵌服务器启动就绪,端口监听完成,可以正式处理请求。项目完全就绪,执行初始化任务:加载缓存、定时任务注册、接口预热
- ApplicationFailedEvent:启动流程中途抛出异常,启动终止节点
以Context开头的事件,spring容器生命周期事件。
application开头是springboot应用生命周期事件。
自定义事件发布
首先,定义自己的事件。
scala
// 自定义业务事件
public class UserRegisterEvent extends ApplicationEvent {
// 自定义业务参数
private String username;
public UserRegisterEvent(Object source, String username) {
super(source);
this.username = username;
}
public String getUsername() {
return username;
}
}
然后,选择发布的时机。
typescript
@Service
public class UserService {
@Resource
private ApplicationEventPublisher publisher;
public void register(String username){
System.out.println("主逻辑:用户["+username+"]注册完成");
// 发布自定义事件
publisher.publishEvent(new UserRegisterEvent(this, username));
}
}
当然,要写一个监听者
csharp
@Service
public class UserEventListener {
// 监听事件,触发后执行自定义逻辑
@EventListener
public void handleRegister(UserRegisterEvent event){
String name = event.getUsername();
System.out.println("监听处理:给"+name+"发送欢迎短信");
System.out.println("监听处理:新增用户注册日志");
}
}
可以看到,publisher是框架中维护的单例对象。
事件发布流程:
- 业务publishEvent发起事件
- 发布器将事件交给单例ApplicationEventMulticaster
- 根据事件类型,从注册表
- 遍历执行逻辑,找到匹配的监听器
- 监听执行完毕,流程切回主线,是的publishEvent是个阻塞方法。