Spring、SpringBoot 常用核心特性 之 事件驱动
Spring、SpringBoot 常用核心特性 之 事件驱动
事件驱动:即跟随当前时间点上出现的时间,调动可用资源,执行相关任务,使不断出现的问题得以解决,防止事务堆积。
如:注册账号时会收到验证码,火车发车前收到异性,预订酒店后收到短信通知。如:浏览器中点击按钮请求后台,鼠标点击变化内容,键盘输入显示数据,服务接受请求后分发请求等。
在解决上述问题时,应用程序是由“时间”驱动运行的,这类程序在编写时往往可以采用相同的模型实现,我们可以将这种编程模型称为事件驱动模型。
(PS:事件驱动模型是一种抽象模型,用于对外部驱动系统业务逻辑这类应用程序进行建模)。
一、事件驱动模型
1、实现思路
事件驱动模型有很多种体现形式,如简单的事件触发机制、单线程异步任务、多线程异步任务等,但是各种技术中实现事件驱动模型的思路基本相同。事件驱动模型包括四个(三个)基本要素:事件、事件消费方、事件生产方、[事件管理器]
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5CIitOY9-1667113836888)(assets/image-20221030083220585.png)]](https://img-blog.csdnimg.cn/7368f1eb94a949d89fdc2db35c860448.png)
- 事件:描述发生的事情。比如说浏览器页面点击事件,鼠标、键盘输入事件,spring 请求处理完成、spring 容器刷新完毕等。
- 事件生产方(事件源):事件的生产方。任何一个事件都必须有一个事件源。比如input、button、spring中请求处理完毕的事件源就是 DispacherServlet、spring 容器刷新完毕的事件源 就是ApplicationContext
- 事件管理器(时间广播器):派发事件。事件和事件监听的桥梁、负责把事件通知给事件监听器(可在事件源中实现)
- 事件消费方(事件监听器):处理事件。监听事件的发生、可以再监听器中做一些处理
2、解决的问题
基于事件驱动的的应用程序可以实时响应所关心的时间,实现实时检测、响应外部动作,这是事件驱动模型的基本功能和作用。在一些复杂的系统中,事件驱动还可以很好地发挥一下作用
①实现组件之间的松耦合、解耦
在复杂系统中,往往存在多个组件相互耦合的情况,如果将组件之间的耦合关系抽象成“时间(Event)”,让事件担任组件之间的通信任务,就能降低、解除组件之间的耦合关系。
事件驱动模型,实际上是将组件之间的耦合关系转移到了“事件(Event)”上,但是对于某个领域而言事件(Event)一般具有通用性并且不会频繁变更实现逻辑,所以事件驱动模型可以很好的实现组件之间的解耦。
② 实现异步任务
一些业务场景中,顺序、阻塞式地执行任务会遇到一些比较耗时的中间步骤,但是不希望整个流程都停下来等待这些中间步骤完成,而是触发一个异步操作然后继续执行当前任务,在收到异步操作处理完成的消息之后再执行相关的处理。
使用事件驱动模型实现异步任务的一般思路是:当遇到耗时较大、没有同步执行要求的操作时,针对这个操作触发一个事件,将这个事件加入到任务队列中,直到有一个进程(线程)能够获取并执行这个任务,才开始执行这个任务
③ 跟踪状态变化
在存储实体模型的业务中通常需要修改实体模型的数据,对于部分业务场景需要存储、使用实体模型的历史变更记录,例如什么时间对实体数据做了什么修改。
对于这类需求,事件驱动模型也可以提供很好的解决方案,我们可以认为每次修改实体数据都是一次事件,那么在修改实体数据后将这个事件存储到事件队列中即可实现跟踪状态变化的需求。
④ 限流、消峰等
二、观察者模型、发布订阅模式
1、观察者模型
观察者模式是一种对象行为模式。它定义对象间的一种一对多的依赖关系(被观察者维护观察者列表)
当一个对象的状态发生改变时,列表中所有观察者都会接收到状态改变的通知
观察者把自己注册到被观察者持有的列表中
当被观察者发布通知,也就是有事件触发时,由被观察者轮询调用观察者的处理代码
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cdrmDEtW-1667113836889)(assets/image-20221030085615180.png)]](https://img-blog.csdnimg.cn/f85787987748469c9768f3a94e035df4.png)
(PS:1、目标需要知道观察者存在;2、目标和观察者之间是依赖关系)
2、发布、订阅模型
发布订阅模式其实是对象间的一对多的依赖关系(利用消息管理器)
当一个对象的状态发生改变时,所有依赖于它的对象都得到状态改变的通知
订阅者通过调度中心订阅自己关注的事件
当发布者发布事件到调度中心,也就是该事件触发时,由调度中心统一调度订阅者的处理代码
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-afKFvYXS-1667113836889)(assets/image-20221030090739361.png)]](https://img-blog.csdnimg.cn/20f3ba1d14d64edfabf9c8c6a63da4d8.png)
(PS:1、发布者不知道订阅者的存在;2、存在消息管理器,彼此之间不知道对方的存在)
3、使用场景区别
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u33MSbOL-1667113836889)(assets/image-20221030091319918.png)]](https://img-blog.csdnimg.cn/2faddfe7b38d4b3b867496341bd21c03.png)
三、Spring 中事件驱动应用
1、Demo01
/*** 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;@AutowiredApplicationContext applicationContext;@Overridepublic void run(ApplicationArguments args) throws Exception {applicationContext.publishEvent(new ApplicationEvent(this) {});}
}
@Slf4j
@Component
public class Demo01Listener implements ApplicationListener<ApplicationEvent> {@Overridepublic void onApplicationEvent(ApplicationEvent event) {log.info("[onApplicationEvent]:{}", event.toString());}
}
2、Demo02
/*** 自定义事件 Demo02Event 继承 ApplicationEvent* 实现对执行类型事件进行监听*/
@SpringBootApplication
public class Demo02App implements ApplicationRunner {public static void main(String[] args) {SpringApplication.run(Demo02App.class, args);}@Resourceprivate ApplicationContext applicationContext;@Overridepublic void run(ApplicationArguments args) throws Exception {applicationContext.publishEvent(new Demo02Evnet(this));}
}
public class Demo02Evnet extends ApplicationEvent {public Demo02Evnet(Object source) {super(source);}@Overridepublic String toString() {return "Demo02Evnet{" +"source=" + source +'}';}
}
@Slf4j
@Component
public class Demo02Listener implements ApplicationListener<Demo02Evnet> {@Overridepublic void onApplicationEvent(Demo02Evnet event) {log.info("[onApplicationEvent]:{}", event.toString());}
}
3、Demo03
/*** 1、自定义事件 Demo03Event 添加业务参数* 2、忽略事件源 根据实际业务情况而定 减少参数* 3、使用 @EventListener 替换 implements ApplicationListener 增加监听者的可扩展性*/
@SpringBootApplication
public class Demo03App implements ApplicationRunner {public static void main(String[] args) {SpringApplication.run(Demo03App.class, args);}@Autowiredprivate ApplicationContext applicationContext;@Overridepublic 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;}@Overridepublic String toString() {return "Demo03Evnet{" +"orderId='" + orderId + '\'' +'}';}
}
@Slf4j
@Component
public class Demo03Listener {@EventListenerpublic void listener1(ApplicationEvent event) {log.info("[listener1]:{}", event.toString());}@EventListenerpublic void listener2(Demo03Evnet event) {log.info("[listener2]:{}", event.toString());}
}
4、Demo04
/*** `@EvnentListener`用法讲解* 1、监听自定义时间、* 2、注解中制定监听事件类型,可指定多个监听事件类型* 3、注解中使用condition 根据特定条件进行监听* 4、根据特定条件进行监听 对事件进行修改后返回**/
@SpringBootApplication
public class Demo04App implements ApplicationRunner {public static void main(String[] args) {SpringApplication.run(Demo04App.class, args);}@Autowiredprivate ApplicationContext applicationContext;@Overridepublic 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;}@Overridepublic 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());}
}
5、Demo05
/*** 异步监听* 1、@Order 指定执行顺序 在同步的情况下生效* 2、@Async 异步执行 需要@EnableAsync 开启异步*/
@Slf4j
@EnableAsync
@SpringBootApplication
public class Demo05App implements ApplicationRunner {public static void main(String[] args) {SpringApplication.run(Demo05App.class, args);}@Autowiredprivate ApplicationContext applicationContext;@Overridepublic 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;}@Overridepublic String toString() {return "Demo04Evnet{" +"orderId='" + orderId + '\'' +'}';}
}
@Slf4j
@Component
public class Demo05Listener {@Order(10)@Async@EventListenerpublic void listener1(Demo05Evnet event) throws InterruptedException {log.info("[listener1]:{}", event.toString());Thread.sleep(2000);}@Order(2)@Async@EventListenerpublic void listener2(Demo05Evnet event) throws InterruptedException {log.info("[listener2]:{}", event.toString());Thread.sleep(2000);}
}
6、Demo06
/*** 自定义事件 不继承 ApplicationEvent [根据特定情况自行设计,由仅依赖 ApplicationEvent 转变为 依赖自定义事件类]*/
@Slf4j
@SpringBootApplication
public class Demo06App implements ApplicationRunner {public static void main(String[] args) {SpringApplication.run(Demo06App.class, args);}@Autowiredprivate ApplicationContext applicationContext;@Overridepublic void run(ApplicationArguments args) throws Exception {applicationContext.publishEvent(new Demo06Evnet( "order-001"));log.info("发布事件完毕");}@EventListenerpublic 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;}@Overridepublic String toString() {return "Demo06Evnet{" +"orderId='" + orderId + '\'' +'}';}
}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
