生活中的事件驱动:
银行存款事件,触发更新余额、用户星级提升、贷款广告推送
航班延误事件,触发广播通知客户、后续形成更新、酒店订单更新
事件驱动架构
事件驱动架构(Event-driven architecture,简称EDA),是指软件系统中发生了某些事件,导致软件的其他部分的操作或者执行。是一种软件设计的方法。
其中,事件是软件系统中某种显著的变化。
主要应用场景:
调试器,当程序运行系统当运行到了某个点时,发送事件,触发断点调试。这种关系称为反转控制 关系,即主观上时调试器控制运行系统调试,客观上是运行系统通知调试器,推送了断点调试。
整个流程:
事件生产方(Event Producer)发生事件(Event happends),并通过事件发射器(Event Emitter)向事件通道(Event Channel)发送这个事件
事件管理器(Event Manager)接收到通道中的事件,对事件进行管理或者进行事件传播(Event disseminates)
事件将会由事件接收者(Event Sinks)接收,并交给他所属的事件消费者(Event Consumer)进行事件评估并采取相关的动作(Event's evaluated and action are token)
类比过程,以mq为例:
mq服务端接收到消息,根据topic向消费者发送消息
消费者的mq接收端接收到消息,将会使用程序消费消息,并作出响应的操作
观察者模式
被观察者(Subject)持有观察者(Observer)的引用,当被观察者发生变化时,将会自动调用观察观察者对象的通知方法。
观察者模式中,Subject既充当事件生产方,又充当事件管理器。
AWT
awt就是观察者模式的典型运用场景:
复制 public class FooPanel extends JPanel implements ActionListener {
public FooPanel() {
super();
JButton btn = new JButton("Click Me!");
btn.addActionListener(this);
this.add(btn);
}
@Override
public void actionPerformed(ActionEvent ae) {
System.out.println("Button has been clicked");
}
}
上述代码中,组件类型JPanel
是Subject
,ActionListener
是Observer
,类型FooPanel
即使观察者又是被观察者。
被观察者通过btn.addActionListener(this);
代码将观察者的引用加入到内部中。
JDK Observer
Spring Event
Spring Event位于Spring的核心模块中,他主要通过核心类ApplicationEvent
以及ApplicationListener
接口提供功能。实现ApplicationListener
并将其放入到IoC容器中,当有ApplicationEvent
事件publish到ApplicationContext中时,就可以监听指定的事件并做出相应的操作。Spring Event基于典型的观察者模式实现。
集成ApplicationEvent
可以实现自己的事件,在Spring内部提供了一套标准的内置事件:
ServletRequestHandledEvent
示例代码:
复制 /**
* 自定义事件对象
*
* @author 杨顺翔
* @since 2022/08/27
*/
public class MyEvent extends ApplicationEvent {
private static final long serialVersionUID = 3223648309069468451L;
public MyEvent(String message) {
super(message);
}
}
复制 /**
* 实现ApplicationListener接口,定义事件监听器,注入到容器中,监控某类型事件
*
* @author 杨顺翔
* @since 2022/08/27
*/
@Component
public class MyEventListener implements ApplicationListener<MyEvent> {
@Override
public void onApplicationEvent(MyEvent contextStartedEvent) {
System.out.println("MyEventListener监听到事件: " + contextStartedEvent.toString());
}
}
复制 /**
* 事件发布类,也就是event provider,需要注入容器中ApplicationEventPublisher对象,并调用其 publish 方法发布一个事件
* 另外,页提供了一个Aware接口可以获取到事件发布对象
*
* @author 杨顺翔
* @since 2022/08/27
*/
@Component
public class MyEventPublisher implements ApplicationEventPublisherAware {
private ApplicationEventPublisher applicationEventPublisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
public void sayHello() {
System.out.println("hello");
MyEvent e = new MyEvent("hello");
applicationEventPublisher.publishEvent(e);
}
}
/**
* 此外,Spring内部提供了几个标准的事件
* (较高版本的Spring在定义Listener时,不用集成ApplicationListener了,可以直接给方法增加 @EventListener注解标记该方法是一个时间处理方法)
*
* @author 杨顺翔
* @since 2022/08/27
*/
@Component
public class StandardEventListener {
/**
* spring context 已启动事件处理
*/
@EventListener(ContextStartedEvent.class)
public void contextStated() {
System.out.println("context started");
}
/**
* 所有Bean已经刷新事件
*/
@EventListener
public void contextRefreshed(ContextRefreshedEvent contextRefreshedEvent) {
System.out.println("context refreshed " + contextRefreshedEvent.getSource());
}
/**
* 容器已经停止运行
*/
@EventListener(ContextStoppedEvent.class)
public void contextStopped() {
System.out.println("context stopped");
}
/**
* 容器已经完全关闭
*/
@EventListener(ContextClosedEvent.class)
public void contextClosed() {
System.out.println("context closed");
}
}
复制 /**
* Spring Event 事件处理,观察者模式
*/
@ComponentScan
@Configuration
public class SpringEventTest {
@Test
public void testContextStartedEvent() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringEventTest.class);
context.start();
// 业务的某个方法触发了某个事件
// 如果没有加@Async注解,时间处理就是同步的,他与观察者模式别无二致
MyEventPublisher myEventPublisher = context.getBean(MyEventPublisher.class);
myEventPublisher.sayHello();
// 停止容器
context.stop();
// 重新打开容器
context.start();
// 关闭容器
context.close();
}
}
发布订阅模式
将事件处理器单独的抽取出来,而不是像观察者模式一样合在一起,就是发布订阅模式。由事件管理器管理生产者与消费者,并控制事件的传输。
Google Guava Event Bus
使用Event Bus:
定义事件对象
复制 public class CustomEvent {
private String action;
public CustomEvent(String action) {
this.action = action;
}
public String getAction() {
return action;
}
public void setAction(String action) {
this.action = action;
}
}
定义事件监听器
复制 public class MyEventListener {
private static int eventHandled;
private static final Logger LOGGER = LoggerFactory.getLogger(MyEventListener.class);
/**
* 事件监听器(事件消费者),订阅string类型的事件
*/
@Subscribe
public void stringEvent(String event) {
LOGGER.info("发生string事件:" + event);
eventHandled++;
}
/**
* 监听CustomEvent类型的事件
*/
@Subscribe
public void customEvent(CustomEvent customEvent) {
LOGGER.info("发生CustomEvent事件:" + customEvent);
eventHandled++;
}
/**
* 使用DeadEvent监听没有事件消费者的事件
*/
@Subscribe
public void deadEvent(DeadEvent deadEvent) {
LOGGER.info("DeadEvent: " + deadEvent.getEvent());
}
}
使用事件监听器
复制 public class EventBusTest {
private MyEventListener myEventListener;
private EventBus eventBus;
/**
* 注册事件监听器
*/
@BeforeAll
public void setup() {
eventBus = new EventBus();
myEventListener = new MyEventListener();
// 注册事件监听器
eventBus.register(myEventListener);
}
/**
* 结束后自动解除注册事件监听器
*/
@AfterAll
public void tearDown() {
eventBus.unregister(myEventListener);
}
}
Spring AMQP
如何在观察者和发布订阅中选择
观察者模式和发布-订阅模式都是用于解耦发布者和订阅者之间的通信,但它们在一些方面有所不同。下面是它们的基本概念和适用场景:
观察者模式:
观察者模式基于一个主题(通常称为主题或可观察对象),它维护一组观察者对象并在状态发生变化时通知它们。主题和观察者之间是一对多的关系,一个主题可以有多个观察者。当主题的状态发生变化时,所有观察者都会收到通知并进行相应的更新。
适用场景:
当主题状态的变化需要通知多个观察者,并且观察者的数量和身份可能会动态变化时,可以使用观察者模式。
当观察者需要订阅和取消订阅主题的通知时,观察者模式是一个很好的选择。
当希望在主题和观察者之间建立松散耦合关系,以便能够方便地添加、移除和修改观察者时,可以使用观察者模式。
发布-订阅模式:
发布-订阅模式是一种通过使用消息代理(称为发布者)来解耦发送者和接收者之间的通信。发布者将消息发送到一个或多个主题,而订阅者可以选择订阅感兴趣的主题并接收相应的消息。发布者和订阅者之间是一对多的关系。
适用场景:
当需要在不同组件或模块之间进行松散耦合的通信时,发布-订阅模式是一个不错的选择。
当发布者和订阅者之间的关系是动态的,并且它们可能随时加入或离开系统时,发布-订阅模式非常适用。
当希望扩展和修改通信机制,例如引入消息代理或中间件来处理消息时,发布-订阅模式是一个强大的工具。
总结:
观察者模式适用于一对多的关系,主题和观察者之间的通信比较直接。而发布-订阅模式适用于发布者和订阅者之间的松散耦合通信,通过消息代理来进行中介。