Spring、SpringBoot 常用扩展特性 之 事件驱动

事件驱动:即跟随当前时间点上出现的事件,调动可用资源,执行相关任务,使不断出现的问题得以解决,防止事务堆积。
如:浏览器中点击按钮请求后台,鼠标点击变化内容,键盘输入显示数据,服务接收请求后分发请求等。
在解决上述问题时,应用程序是由“事件”驱动运行的,这类程序在编写时往往可以采用相同的模型实现,我们可以将这种编程模型称为事件驱动模型。
(PS:事件驱动模型其实是一种抽象模型,用于对由外部事件驱动系统业务逻辑这类应用程序进行建模。)

一、事件驱动模型

1. 实现的思路

事件驱动模型有很多种体现形式,如简单的事件触发机制、单线程异步任务、多线程异步任务等,但是各种技术中实现事件驱动模型的基本思路相同。
事件驱动模型包括四个(三个)基本要素:事件、事件消费方、事件生产方、[事件管理器]

https://images.cnblogs.com/cnblogs_com/dafengdeai/2267824/o_230129084143_%E4%BA%8B%E4%BB%B6%E9%A9%B1%E5%8A%A8%E6%A8%A1%E5%9E%8B.jpg

  • 事件: 描述发生的事情。比如说浏览器页面点击事件,鼠标、键盘输入事件,spring 请求处理完成、Spring 容器刷新完毕等。
  • 事件生产方(事件源): 事件的产生者。任何一个事件都必须有一个事件源。比如input、button,spring中请求处理完成的事件源就是 DispatcherServlet 、Spring 容器刷新完毕的事件源就是 ApplicationContext
  • 事件管理器(事件广播器): 派发事件。事件和事件监听器的桥梁、负责把事件通知给事件监听器 (可在事件源中实现)
  • 事件消费方(事件监听器): 处理事件。监听事件的发生、可以在监听器中做一些处理

2. 解决的问题

基于事件驱动的应用程序可以实时响应所关心的事件,实现实时检测、响应外部动作,这是事件驱动模型的基本功能和作用。在一些复杂的系统中,事件驱动模型还可很好地发挥以下作用

1. 实现组件之间的松耦合、解耦

在复杂系统中,往往存在多个组件相互耦合的情况,如果将组件之间的耦合关系抽象成“事件(Event)”,让事件担任组件之间的通信任务,就能降低、解除组件之间的耦合关系。
事件驱动模型,实际上是将组件之间的耦合关系转移到了“事件(Event)”上,但是对于某个领域而言事件(Event)一般具有通用性并且不会频繁变更实现逻辑,所以事件驱动模型可以很好地实现组件之间的解耦。

2. 实现异步任务

在一些业务场景中,顺序、阻塞式地执行任务会遇到一些比较耗时的中间步骤,但是不希望整个流程都停下来等待这些中间步骤完成,而是触发一个异步操作然后继续执行当前任务,在收到异步操作处理完成的消息之后再执行相关的处理。
使用事件驱动模型实现异步任务的一般思路是:当遇到耗时较大、没有同步执行要求的操作时,针对这个操作触发一个事件,将这个事件加入到任务队列中,直到有一个进程(线程)能够获取并执行这个任务,才开始执行这个任务。

3. 跟踪状态变化

在存储实体模型的业务中通常需要修改实体模型的数据,对于部分业务场景需要存储、使用实体模型的历史变更记录,例如什么时间对实体数据做了什么修改。
对于这类需求,事件驱动模型也可以提供很好的解决方案,我们可以认为每次修改实体数据都是一次事件,那么在修改实体数据后将这个事件存储到事件队列中即可实现跟踪状态变化的需求。

二、观察者模式、发布订阅模式

processon 图解连接

1. 观察者模式

观察者模式是一种对象行为模式。它定义对象间的一种一对多的依赖关系(被观察者维护观察者列表)
当一个对象的状态发生改变时,列表中所有观察者都会接收到状态改变的通知
观察者把自己注册到被观察者持有的列表中
当被观察者发布通知,也就是有事件触发时,由被观察者轮询调用观察者的处理代码

https://images.cnblogs.com/cnblogs_com/dafengdeai/2267824/o_230129084243_%E8%A7%82%E5%AF%9F%E8%80%85%E6%A8%A1%E5%BC%8F.jpg

(PS:如果需要实现异步任务,EventSource中会设计一个事件队列用于异步调度)

2. 发布、订阅模式

发布订阅模式其实是对象间的一对多的依赖关系(利用消息管理器)
当一个对象的状态发生改变时,所有依赖于它的对象都得到状态改变的通知
订阅者通过调度中心订阅自己关注的事件
当发布者发布事件到调度中心,也就是该事件触发时,由调度中心统一调度订阅者的处理代码

https://images.cnblogs.com/cnblogs_com/dafengdeai/2267824/o_230129084220_%E5%8F%91%E5%B8%83%E8%AE%A2%E9%98%85%E6%A8%A1%E5%BC%8F.jpg

(PS:如果需要实现异步任务,EventDispatcher(事件广播器)中会设计一个事件队列用于异步调度)

3. 使用场景区别

|观察者模式|发布、订阅模式|
|: ---- |: ---- |
|关注的是事件发生后观察者能够获取到足够的数据。 |关注的是事件发生后能够准确地触发相应的动作。|
|Event种类单一,但是Event会被多个EventListener关注,Event中携带的信息更多的是观察者需要的数据。 |Event种类多,每类Event只有单个EventListener关注,Event中携带的多是事件自身的一些信息(例如EventID之类),而很少携带其他数据。 |
|要求事件发生后EventSource能够通知所有EventListener,此时调度Event的逻辑很简单,不必设置独立的调度器执行调度任务。 |Event更像一个触发信号,不同的Event会触发不同的EventListener执行动作,此时调度Event的逻辑复杂,需要设置独立的调度器执行调度任务。 |

三、Spring中事件驱动应用

1. demo1

/**
 * 1. Spring事件驱动最基础的使用 ApplicationEventPublisher、ApplicationEvent、ApplicationListener
 * 2. ApplicationEventPublisher 子类 ApplicationContext
 * 3. 事件源、监听器 需要被spring管理
 * 4. 监听器 需要实现 ApplicationListener<ApplicationEvent>
 * 5. 可体现事件源和监听器之间的松耦合 仅依赖spring、ApplicationEvent
 * @author dafeng
 */
@Slf4j
@SpringBootApplication
public class Demo1App implements ApplicationRunner {

    public static void main(String[] args) {
        SpringApplication.run(Demo1App.class, args);
    }

    @Autowired
    ApplicationEventPublisher applicationEventPublisher;

//    @Autowired
//    ApplicationContext applicationContext;

    @Override
    public void run(ApplicationArguments args) {
        applicationEventPublisher.publishEvent(new ApplicationEvent(this) {
        });
    }
}
/**
 * @author dafeng
 */
@Slf4j
@Component
public class Demo1Listener implements ApplicationListener<ApplicationEvent> {
    @Override
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
        log.info("[onApplicationEvent]: {}", applicationEvent.toString());
    }
}

2. demo2

/**
 * 自定义事件 Demo2Event 继承 ApplicationEvent
 * 实现对指定类型事件进行监听
 * @author dafeng
 */
@Slf4j
@SpringBootApplication
public class Demo2App implements ApplicationRunner {

    public static void main(String[] args) {
        SpringApplication.run(Demo2App.class, args);
    }

    @Autowired
    ApplicationContext applicationContext;

    @Override
    public void run(ApplicationArguments args) {
        applicationContext.publishEvent(new Demo2Event(this));
    }
}
/**
 * @author dafeng
 */
public class Demo2Event extends ApplicationEvent {
    public Demo2Event(Object source) {
        super(source);
    }

    @Override
    public String toString() {
        return "Demo2Event{" + "source=" + source + '}';
    }
}
/**
 * @author dafeng
 */
@Slf4j
@Component
public class Demo2Listener implements ApplicationListener<Demo2Event> {
    @Override
    public void onApplicationEvent(Demo2Event demo2Event) {
        log.info("[onApplicationEvent]: {}", demo2Event.toString());
    }
}

3. demo3

/**
 * 1. 自定义事件 Demo3Event 添加业务参数
 * 2. 忽略事件源 根据实际业务情况而定 减少参数
 * 3. 使用 @EventListener 替换 implements ApplicationListener<Demo2Event>  增加监听者的可扩展性
 * @author dafeng
 */
@Slf4j
@SpringBootApplication
public class Demo3App implements ApplicationRunner {

    public static void main(String[] args) {
        SpringApplication.run(Demo3App.class, args);
    }

    @Autowired
    ApplicationContext applicationContext;

    @Override
    public void run(ApplicationArguments args) {
        String orderId = "order-001";
        applicationContext.publishEvent(new Demo3Event(this, orderId));
    }
}
/**
 * @author dafeng
 */
public class Demo3Event extends ApplicationEvent {
    private String orderId;

    public Demo3Event(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 "Demo3Event{" + "orderId='" + orderId + '\'' + ", source=" + source + '}';
    }
}
/**
 * @author dafeng
 */
@Slf4j
@Component
public class Demo3Listener {
    @EventListener
    public void listener001(ApplicationEvent event) {
        log.info("[listener001] >> {}", event.toString());
    }

    @EventListener
    public void listener002(Demo3Event event) {
        log.info("[listener002] >> {}", event.toString());
    }
}

demo4

/**
 * `@EventListener` 用法讲解
 * 1. 监听自定义事件
 * 2. 注解中指定监听事件类型,可指定多个监听事件类型
 * 3. 注解中使用condition 根据特定条件进行监听
 * 4. 根据特定条件进行监听 对事件进行修改后返回
 *
 * @author dafeng
 */
@Slf4j
@SpringBootApplication
public class Demo4App implements ApplicationRunner {

    public static void main(String[] args) {
        SpringApplication.run(Demo4App.class, args);
    }

    @Autowired
    ApplicationContext applicationContext;

    @Override
    public void run(ApplicationArguments args) {
        applicationContext.publishEvent(new Demo4Event("order-001"));
        applicationContext.publishEvent(new Demo4Event("order-002"));
    }
}
/**
 * @author dafeng
 */
public class Demo4Event extends ApplicationEvent {
    private String orderId;

    public Demo4Event(String orderId) {
        super(orderId);
        this.orderId = orderId;
    }

    public String getOrderId() {
        return orderId;
    }

    public void setOrderId(String orderId) {
        this.orderId = orderId;
    }

    @Override
    public String toString() {
        return "Demo4Event{" + "orderId='" + orderId + '\'' + ", source=" + source + '}';
    }
}

/**
 * @author dafeng
 */
@Slf4j
@Component
public class Demo4Listener {
    /**
     * 参数类型 即为 监听事件对象类型
     */
    @EventListener
    public void listener001(Demo4Event event) {
        log.info("[listener001] >> {}", event.toString());
    }

    /**
     * 注解中指定监听事件类型,可指定多个监听事件类型
     * 方法参数中可使用Object作为形参,但只能有一个形参 (Maximum one parameter is allowed)
     */
    @EventListener({Demo4Event.class, ApplicationEvent.class})
    public void listener002(Object event) {
        log.info("[listener002] >> {}", event.toString());
    }

    /**
     * 根据特定条件进行监听
     */
    @EventListener(condition = "#event.orderId == 'order-002'")
    public void listener003(Demo4Event event) {
        log.info("[listener003] >> {}", event.toString());
    }


    /**
     * 根据特定条件进行监听 对事件进行修改后返回
     * 特定业务场景下可使用此模式
     */
    @EventListener(condition = "#event.orderId == 'order-001'")
    public Demo4Event listener004(Demo4Event event) {
        log.info("[listener004] >> {}", event.toString());
        event.setOrderId("order-003");
        return event;
    }

    /**
     * 监听修改后的事件
     */
    @EventListener(condition = "#event.orderId == 'order-003'")
    public void listener005(Demo4Event event) {
        log.info("[listener005] >> {}", event.toString());
    }

}

demo5

/**
 * 异步监听
 * 1. @Order 指定执行顺序 在同步的情况下生效
 * 2. @Async 异步执行 需要@EnableAsync 开启异步
 * 
 * @author dafeng
 */
@Slf4j
//@EnableAsync
@SpringBootApplication
public class Demo5App implements ApplicationRunner {

    public static void main(String[] args) {
        SpringApplication.run(Demo5App.class, args);
    }

    @Autowired
    ApplicationContext applicationContext;

    @Override
    public void run(ApplicationArguments args) {

        applicationContext.publishEvent(new Demo5Event("order-001"));
//        applicationContext.publishEvent(new Demo5Event("order-002"));

        log.info("发布事件完成");
    }
}
/**
 * @author dafeng
 */
public class Demo5Event extends ApplicationEvent {
    private String orderId;

    public Demo5Event(String orderId) {
        super(orderId);
        this.orderId = orderId;
    }

    public String getOrderId() {
        return orderId;
    }

    public void setOrderId(String orderId) {
        this.orderId = orderId;
    }

    @Override
    public String toString() {
        return "Demo5Event{" + "orderId='" + orderId + "'" + ", source=" + source + '}';
    }
}
/**
 * @author dafeng
 */
@Slf4j
@Component
public class Demo5Listener {

//    @Async
    @Order(1)
    @EventListener
    public void listener001(Demo5Event event) {
        log.info("[listener001] >> {}", event.toString());
        ThreadUtil.sleep(2000);
    }

//    @Async
    @Order(2)
    @EventListener
    public void listener002(Demo5Event event) {
        log.info("[listener002] >> {}", event.toString());
        ThreadUtil.sleep(2000);
    }
}

demo6

/**
 * 自定义事件 不继承 ApplicationEvent [根据特定情况自行设计,由仅依赖 ApplicationEvent 转变为 依赖自定义事件类]
 *
 * @author dafeng
 */
@Slf4j
@SpringBootApplication
public class Demo6App implements ApplicationRunner {
    public static void main(String[] args) {
        SpringApplication.run(Demo6App.class, args);
    }

    @Autowired
    ApplicationContext applicationContext;

    @Override
    public void run(ApplicationArguments args) {
        applicationContext.publishEvent(new Demo6Event("order-001"));
    }

    @EventListener
    public void demo6EventListener(Demo6Event event){
        log.info("[demo6EventListener] >> {}", event.toString());
    }
}
/**
 * @author dafeng
 */
public class Demo6Event {
    private String orderId;

    public Demo6Event(String orderId) {
        this.orderId = orderId;
    }

    @Override
    public String toString() {
        return "Demo6Event{" + "orderId='" + orderId + '\'' + '}';
    }
}
posted @ 2023-01-29 16:50  AdmiralFisher  阅读(686)  评论(0)    收藏  举报