1 "你日渐平庸,甘于平庸,将继续平庸。"------《以自己喜欢的方式过一生》
"总是有人要赢的,那为什么不能是我呢?"------科比·布莱恩特
"你那么憎恨那些人,和他们斗了那么久,最终却要变得和他们一样,人世间没有任何理想值得以这样的沉沦作为代价。"------马尔克斯《百年孤独》
"如果结果不如你所愿,就在尘埃落定前奋力一搏。"------《夏目友人帐》
"人有逆天之时,天无绝人之路。"------《醒世恒言》
"有些事不是看到了希望才去坚持,而是因为坚持才会看到希望。"------《十宗罪》
"维持现状意味着空耗你的努力和生命。"------纪伯伦
Spring * 事件驱动编程
推荐
一. 概念
事件驱动编程是面向事件的,不同于传统的基于顺序的编程,它旨在当事件发生时,再采用行动进行处理的一种编程范式。它使得程序以松耦合的方式进行粘合,实现部分解耦。
二. 主要解决的问题
1.代码的松耦合
将系统复杂的功能拆分为多个部分,每个部分抽象为一个组件,组件与组件之间通过良好定义的接口进行通信;那么他们之间通信的触发若采用引入(依赖)调用的方式,便使得类之间产生了强关系,属于功能耦合,若把他们之间调用的触发抽象为事件,就能降低、解除组件之间的耦合关系。
事件驱动模型,实际上是将组件之间的耦合关系转移到了"事件(Event)"上,但是对于某个领域而言事件(Event)一般具有通用性并且不会频繁变更实现逻辑,所以事件驱动模型可以很好地实现组件之间的解耦。
2.异步编程的需要(MQ 主要用于分布式)
业务场景中,顺序、阻塞式地执行任务会遇到一些比较耗时的中间步骤,但是不希望整个流程都停下来等待这些中间步骤完成,而是触发一个异步操作然后继续执行当前任务,在收到异步操作处理完成的消息之后再执行相关的处理。
使用事件驱动模型实现异步任务的一般思路是:当遇到耗时较大、没有同步执行要求的操作时,针对这个操作触发一个事件,将这个事件加入到任务队列中,直到有一个进程(线程)能够获取并执行这个任务,才开始执行这个任务。
这里大家可能会想到MQ,MQ不就是干这个的吗,对,它确实是做这个的,但是是想一下,我就是一个单机系统或者我是一个底层编程框架,为了实现解耦异步我就要引入MQ,是不是有点大材小用,强制上车的意思。所以:事件编程适用于小范围或者小场景需求,但在框架编程中大量使用。
3.状态模型(状态机,日志记录)
这个场景理解起来比较简单,但是处理这种场景的方式却很多。 比如我们需要对实体状态的变更进行监控,(有没有想到禅道-项目管理中的最新动态),对请求的接口做一些日志记录,但日志有可能需要异步。这里大家可能会联想到AOP,确实在状态监控的实现上考虑到的简单方式就是切面变成,这里是想告诉大家,除了切面,事件驱动也可以实现。各有优劣。
三. 设计模式?
可能大家在查询资料或者学习过程中,会看到,事件编程或者状态管理时,会碰到设计模式中的 "观察者模式",确实这个模式"很符合"这个场景,那问题来了,需要了解他吗? 我的建议是需要,为啥?懂了它,手写一个高大上的模式在项目上,岂不是更好,对于理解底层架构来说,也提供了思路,岂不更好。
观察者模式是使用频率较高的设计模式之一。 定义对象间一种一对多的依赖关系,使得当每一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。是一种对象行为型模式。
观察者模式的别名包括发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。 看到这些名称有没有想到技术上的一些概念?MQ MVVM HODOOP 监听器
一些概念:
-
Subject(目标):被观察者,它是指被观察的对象。
-
ConcreteSubject(具体目标):具体目标是目标类的子类,通常它包含经常发生改变的数据,当它的状态发生改变时,向它的各个观察者发出通知。
-
Observer(观察者):观察者将对观察目标的改变做出反应,观察者一般定义为接口,该接口声明了更新数据的方法
update(),因此又称为抽象观察者。
-
ConcreteObserver(具体观察者):在具体观察者中维护一个指向具体目标对象的引用,它存储具体观察者的有关状态,这些状态需要和具体目标的状态保持一致;它实现了在抽象观察者Observer
中定义的 update()方法。
图中涉及UML的关系要理解,图比较重要,看的懂才可以继续。:关联(实线实心箭头)泛化(继承 实线空心)实现(虚线空心)聚合(关联关系的一种,实现空心菱形)
四. JDK中的观察者模式(如何自行实现,不是从0开始)
观察者模式在 Java 语言中的地位非常重要。在 JDK 的 java.util 包中,提供了 Observable 类以及 Observer 接口,它们构成了 JDK 对观察者模式的支持(可以去查看下源码,写的比较严谨)。
这种方式不建议使用了,因为在jdk9之后废弃,且存在线程不安全问题,
**(推荐)**java.util.concurrent包中的ConcurrentHashMap和CopyOnWriteArrayList,以及java.util.EventListener相关的API来构建自己的观察者模式实现
五. Spring 的事件驱动编程(正文开始)
1. 概念
- 事件 :ApplicationEvent 是所有事件对象的父类。ApplicationEvent 继承自 jdk 的 EventObject, 所有的事件都需要继承 ApplicationEvent, 并且通过 source 得到事件源。
Spring 也为我们提供了很多内置事件,ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent、ContextClosedEvent、RequestHandledEvent。 - 事件监听:ApplicationListener,也就是观察者,继承自 jdk 的 EventListener,该类中只有一个方法
onApplicationEvent。当监听的事件发生后该方法会被执行。 - 事件源:ApplicationContext,ApplicationContext 是 Spring 中的核心容器,在事件监听中
ApplicationContext 可以作为事件的发布者,也就是事件源。因为 ApplicationContext 继承自
ApplicationEventPublisher。在 ApplicationEventPublisher
中定义了事件发布的方法:publishEvent(Object event) - 事件管理:ApplicationEventMulticaster,用于事件监听器的注册和事件的广播。监听器的注册就是通过它来实现的,它的作用是把Applicationcontext 发布的 Event 广播给它的监听器列表。
2. 核心类
- ApplicationEvent 事件类
- ApplicationListener 监听器泛型(事件类)
- @EventListener 注解同ApplicationListener 作用于方法上,参数是事件类
- ApplicationEventPublisher 自动发布事件的处理类,常用的,他是一个借口,自定义时可重写它
- ApplicationEventPublisherAware 接口类,用于业务处理中,它包含 setApplicationEventPublisher 的方法,用于指定ApplicationEventPublisher
- ApplicationEventMulticaster 用于事件监听器的注册和事件的广播,编程不涉及,但是需要了解下
3. 说一下 原理,懂了基本就OVER了,涉及源码,不懂暂时可以
1. 广播器 (类结构)
-
ApplicationEventMulticaster接口:提供了添加/移除监听器以及广播事件给监听器的行为。
-
AbstractApplicationEventMulticaster抽象类:提供了基础的监听器注册/移除以及查找能力。
-
SimpleApplicationEventMulticaster类:提供了事件广播功能。
2. 注册广播器和监听器
Spring容器初始化时, org.springframework.context.support.AbstractApplicationContext 在refresh()方法中,会进行广播器和监听器的注册。
- 初始化事件广播器(initApplicationEventMulticaster)
- 注册监听器(registerListeners)
- 事件广播
- publistEvent
- multicastEvent
4. 实操效果
-
定义事件
@EqualsAndHashCode(callSuper = true)
@Data
@Builder
public class DemoEvent extends ApplicationEvent {private String name; public DemoEvent(String source) { super(source); this.name = source; } public DemoEvent(Object source, Clock clock) { super(source, clock); }
}
-
定义事件监听
方式一:采用注解 适合业务编程
@Component
public class DemoEventListener {@EventListener(DemoEvent.class) public void demoListener(DemoEvent event){ System.out.println(event.getName()); }
}
方式二: 适合底层框架编程
public class DemoEventListenerIm implements ApplicationListener<DemoEvent> {@Override public void onApplicationEvent(DemoEvent event) { System.out.println(event.getName()); }
}
-
定义事件发布(可不写,框架编程需要)
@Component
public class DemoPublisher implements ApplicationEventPublisherAware {private ApplicationEventPublisher applicationEventPublisher; @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher = applicationEventPublisher; } public void publishEvent(DemoEvent event) { System.out.println("publish event"); applicationEventPublisher.publishEvent(event); }
}
-
测试
@SpringBootTest(classes = EventApplication.class)
@RunWith(SpringRunner.class)
public class TestApplication {@Autowired private ApplicationEventPublisher publisher; @Test public void demoEvenTest() { DemoEvent demoEvent = new DemoEvent("hello"); publisher.publishEvent(demoEvent); System.out.println("laile"); }
}
5. 其他注意点
-
事务监听器
@EnableTransactionManagement开启事务支持,@TransactionalEventListener标识事务监听器。
- 发布事件的操作必须在事务(@Transactional)内进行,否则监听器不会生效,也可以将fallbackExecution标志设置为true(@TransactionalEventListener(fallbackExecution = true))
- 可以配置在事务的哪个阶段来监听事务(默认在事务提交后监听),@TransactionalEventListener(phase = TransactionPhase.AFTER_COMPLETION)。
-
异步支持
@EnableAsync开启异步支持,@Async标识监听器异步处理。开启异步执行后,方法的异常不会抛出,只能在方法内部处理。
-
条件监听
@EventListener(condition = "#event.message.contains('important')")
用于按照条件处理数据,可用与区分数据处理
-
监听器顺序
@Order控制多个监听器的执行顺序,值越小,监听器越先执行。
到这里您基本就了解了事件编程思路。 下面我们聊一聊前端的事件编程。KOI技术-事件驱动编程(前端)