事件驱动:即跟随当前时间点上出现的时间,调动可用资源,执行相关任务,使不断出现的问题得以解决,防止事务堆积。
如:注册账号时会收到验证码,火车发车前收到异性,预订酒店后收到短信通知。如:浏览器中点击按钮请求后台,鼠标点击变化内容,键盘输入显示数据,服务接受请求后分发请求等。
在解决上述问题时,应用程序是由“时间”驱动运行的,这类程序在编写时往往可以采用相同的模型实现,我们可以将这种编程模型称为事件驱动模型。
(PS:事件驱动模型是一种抽象模型,用于对外部驱动系统业务逻辑这类应用程序进行建模)。
事件驱动模型有很多种体现形式,如简单的事件触发机制、单线程异步任务、多线程异步任务等,但是各种技术中实现事件驱动模型的思路基本相同。事件驱动模型包括四个(三个)基本要素:事件、事件消费方、事件生产方、[事件管理器]
基于事件驱动的的应用程序可以实时响应所关心的时间,实现实时检测、响应外部动作,这是事件驱动模型的基本功能和作用。在一些复杂的系统中,事件驱动还可以很好地发挥一下作用
①实现组件之间的松耦合、解耦
在复杂系统中,往往存在多个组件相互耦合的情况,如果将组件之间的耦合关系抽象成“时间(Event)”,让事件担任组件之间的通信任务,就能降低、解除组件之间的耦合关系。
事件驱动模型,实际上是将组件之间的耦合关系转移到了“事件(Event)”上,但是对于某个领域而言事件(Event)一般具有通用性并且不会频繁变更实现逻辑,所以事件驱动模型可以很好的实现组件之间的解耦。
② 实现异步任务
一些业务场景中,顺序、阻塞式地执行任务会遇到一些比较耗时的中间步骤,但是不希望整个流程都停下来等待这些中间步骤完成,而是触发一个异步操作然后继续执行当前任务,在收到异步操作处理完成的消息之后再执行相关的处理。
使用事件驱动模型实现异步任务的一般思路是:当遇到耗时较大、没有同步执行要求的操作时,针对这个操作触发一个事件,将这个事件加入到任务队列中,直到有一个进程(线程)能够获取并执行这个任务,才开始执行这个任务
③ 跟踪状态变化
在存储实体模型的业务中通常需要修改实体模型的数据,对于部分业务场景需要存储、使用实体模型的历史变更记录,例如什么时间对实体数据做了什么修改。
对于这类需求,事件驱动模型也可以提供很好的解决方案,我们可以认为每次修改实体数据都是一次事件,那么在修改实体数据后将这个事件存储到事件队列中即可实现跟踪状态变化的需求。
④ 限流、消峰等
观察者模式是一种对象行为模式。它定义对象间的一种一对多的依赖关系(被观察者维护观察者列表)
当一个对象的状态发生改变时,列表中所有观察者都会接收到状态改变的通知
观察者把自己注册到被观察者持有的列表中
当被观察者发布通知,也就是有事件触发时,由被观察者轮询调用观察者的处理代码
(PS:1、目标需要知道观察者存在;2、目标和观察者之间是依赖关系)
发布订阅模式其实是对象间的一对多的依赖关系(利用消息管理器)
当一个对象的状态发生改变时,所有依赖于它的对象都得到状态改变的通知
订阅者通过调度中心订阅自己关注的事件
当发布者发布事件到调度中心,也就是该事件触发时,由调度中心统一调度订阅者的处理代码
(PS:1、发布者不知道订阅者的存在;2、存在消息管理器,彼此之间不知道对方的存在)
/**
* 1、Spring 事件驱动最基础的使用 ApplicationEventPublisher[发布者]、ApplicationEvent[事件]、ApplicationListener[监听者]
* 2、ApplicationEventPublisher 子类 ApplicationContext
* 3、事件源、监听器 需要被 spring 管理
* 4、监听器 需要实现 ApplicationListener
* 5、可体现事件源和监听器之间的松耦合 仅以来spring、ApplicationEvent
*/
@Slf4j
@SpringBootApplication
public class Demo01App implements ApplicationRunner {
public static void main(String[] args) {
SpringApplication.run(Demo01App.class,args);
}
// @Autowired
// ApplicationEventPublisher applicationEventPublisher;
@Autowired
ApplicationContext applicationContext;
@Override
public void run(ApplicationArguments args) throws Exception {
applicationContext.publishEvent(new ApplicationEvent(this) {
});
}
}
@Slf4j
@Component
public class Demo01Listener implements ApplicationListener<ApplicationEvent> {
@Override
public void onApplicationEvent(ApplicationEvent event) {
log.info("[onApplicationEvent]:{}", event.toString());
}
}
/**
* 自定义事件 Demo02Event 继承 ApplicationEvent
* 实现对执行类型事件进行监听
*/
@SpringBootApplication
public class Demo02App implements ApplicationRunner {
public static void main(String[] args) {
SpringApplication.run(Demo02App.class, args);
}
@Resource
private ApplicationContext applicationContext;
@Override
public void run(ApplicationArguments args) throws Exception {
applicationContext.publishEvent(new Demo02Evnet(this));
}
}
public class Demo02Evnet extends ApplicationEvent {
public Demo02Evnet(Object source) {
super(source);
}
@Override
public String toString() {
return "Demo02Evnet{" +
"source=" + source +
'}';
}
}
@Slf4j
@Component
public class Demo02Listener implements ApplicationListener<Demo02Evnet> {
@Override
public void onApplicationEvent(Demo02Evnet event) {
log.info("[onApplicationEvent]:{}", event.toString());
}
}
/**
* 1、自定义事件 Demo03Event 添加业务参数
* 2、忽略事件源 根据实际业务情况而定 减少参数
* 3、使用 @EventListener 替换 implements ApplicationListener 增加监听者的可扩展性
*/
@SpringBootApplication
public class Demo03App implements ApplicationRunner {
public static void main(String[] args) {
SpringApplication.run(Demo03App.class, args);
}
@Autowired
private ApplicationContext applicationContext;
@Override
public void run(ApplicationArguments args) throws Exception {
String orderId = "order-001";
applicationContext.publishEvent(new Demo03Evnet(this,orderId));
}
}
public class Demo03Evnet extends ApplicationEvent {
private String orderId;
public Demo03Evnet(Object source, String orderId) {
super(source);
this.orderId = orderId;
}
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
@Override
public String toString() {
return "Demo03Evnet{" +
"orderId='" + orderId + '\'' +
'}';
}
}
@Slf4j
@Component
public class Demo03Listener {
@EventListener
public void listener1(ApplicationEvent event) {
log.info("[listener1]:{}", event.toString());
}
@EventListener
public void listener2(Demo03Evnet event) {
log.info("[listener2]:{}", event.toString());
}
}
/**
* `@EvnentListener`用法讲解
* 1、监听自定义时间、
* 2、注解中制定监听事件类型,可指定多个监听事件类型
* 3、注解中使用condition 根据特定条件进行监听
* 4、根据特定条件进行监听 对事件进行修改后返回
*
*/
@SpringBootApplication
public class Demo04App implements ApplicationRunner {
public static void main(String[] args) {
SpringApplication.run(Demo04App.class, args);
}
@Autowired
private ApplicationContext applicationContext;
@Override
public void run(ApplicationArguments args) throws Exception {
applicationContext.publishEvent(new Demo04Evnet(this,"order-001"));
applicationContext.publishEvent(new Demo04Evnet(this,"order-002"));
}
}
public class Demo04Evnet extends ApplicationEvent {
private String orderId;
public Demo04Evnet(Object source, String orderId) {
super(source);
this.orderId = orderId;
}
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
@Override
public String toString() {
return "Demo04Evnet{" +
"orderId='" + orderId + '\'' +
'}';
}
}
@Slf4j
@Component
public class Demo04Listener {
/**
* 参数类型 纪委 监听事件对象类型
*/
// @EventListener
// public void listener1(Demo04Evnet event) {
// log.info("[listener1]:{}", event.toString());
// }
/**
* 注解中指定监听事件类型,可指定多个监听事件类型
* 方法参数中可使用Object作为形参,但只能有一个形参
*/
// @EventListener({Demo04Evnet.class,ApplicationEvent.class})
// public void listener2(Object event) {
// log.info("[listener2]:{}", event.toString());
// }
/**
* 根据特定条件进行监听
*/
// @EventListener(condition = "#event.orderId == 'order-002'")
// public void listener3(Demo04Evnet event) {
// log.info("[listener3]:{}", event.toString());
// }
/**
* 根据特定条件进行监听 对事件进行修改后返回
* 特定业务场景下可使用此模式
*/
@EventListener(condition = "#event.orderId == 'order-001'")
public Demo04Evnet listener4(Demo04Evnet event) {
log.info("[listener4]:{}", event.toString());
event.setOrderId("order-003");
return event;
}
/**
* 监听修改后的事件
*/
@EventListener(condition = "#event.orderId == 'order-003'")
public void listener5(Demo04Evnet event) {
log.info("[listener5]:{}", event.toString());
}
}
/**
* 异步监听
* 1、@Order 指定执行顺序 在同步的情况下生效
* 2、@Async 异步执行 需要@EnableAsync 开启异步
*/
@Slf4j
@EnableAsync
@SpringBootApplication
public class Demo05App implements ApplicationRunner {
public static void main(String[] args) {
SpringApplication.run(Demo05App.class, args);
}
@Autowired
private ApplicationContext applicationContext;
@Override
public void run(ApplicationArguments args) throws Exception {
applicationContext.publishEvent(new Demo05Evnet(this,"order-001"));
// applicationContext.publishEvent(new Demo05Evnet(this,"order-002"));
log.info("发布事件完毕");
}
}
public class Demo05Evnet extends ApplicationEvent {
private String orderId;
public Demo05Evnet(Object source, String orderId) {
super(source);
this.orderId = orderId;
}
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
@Override
public String toString() {
return "Demo04Evnet{" +
"orderId='" + orderId + '\'' +
'}';
}
}
@Slf4j
@Component
public class Demo05Listener {
@Order(10)
@Async
@EventListener
public void listener1(Demo05Evnet event) throws InterruptedException {
log.info("[listener1]:{}", event.toString());
Thread.sleep(2000);
}
@Order(2)
@Async
@EventListener
public void listener2(Demo05Evnet event) throws InterruptedException {
log.info("[listener2]:{}", event.toString());
Thread.sleep(2000);
}
}
/**
* 自定义事件 不继承 ApplicationEvent [根据特定情况自行设计,由仅依赖 ApplicationEvent 转变为 依赖自定义事件类]
*/
@Slf4j
@SpringBootApplication
public class Demo06App implements ApplicationRunner {
public static void main(String[] args) {
SpringApplication.run(Demo06App.class, args);
}
@Autowired
private ApplicationContext applicationContext;
@Override
public void run(ApplicationArguments args) throws Exception {
applicationContext.publishEvent(new Demo06Evnet( "order-001"));
log.info("发布事件完毕");
}
@EventListener
public void demo6EventListener(Demo06Evnet evnet) {
log.info("[demo06EventListener {}]", evnet.toString());
}
}
public class Demo06Evnet {
private String orderId;
public Demo06Evnet( String orderId) {
this.orderId = orderId;
}
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
@Override
public String toString() {
return "Demo06Evnet{" +
"orderId='" + orderId + '\'' +
'}';
}
}