生活中的事件驱动:
银行存款事件,触发更新余额、用户星级提升、贷款广告推送
航班延误事件,触发广播通知客户、后续形成更新、酒店订单更新
事件驱动架构
事件驱动架构(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
如何在观察者和发布订阅中选择
观察者模式和发布-订阅模式都是用于解耦发布者和订阅者之间的通信,但它们在一些方面有所不同。下面是它们的基本概念和适用场景:
观察者模式:
观察者模式基于一个主题(通常称为主题或可观察对象),它维护一组观察者对象并在状态发生变化时通知它们。主题和观察者之间是一对多的关系,一个主题可以有多个观察者。当主题的状态发生变化时,所有观察者都会收到通知并进行相应的更新。
适用场景:
当主题状态的变化需要通知多个观察者,并且观察者的数量和身份可能会动态变化时,可以使用观察者模式。
当观察者需要订阅和取消订阅主题的通知时,观察者模式是一个很好的选择。
当希望在主题和观察者之间建立松散耦合关系,以便能够方便地添加、移除和修改观察者时,可以使用观察者模式。
发布-订阅模式:
发布-订阅模式是一种通过使用消息代理(称为发布者)来解耦发送者和接收者之间的通信。发布者将消息发送到一个或多个主题,而订阅者可以选择订阅感兴趣的主题并接收相应的消息。发布者和订阅者之间是一对多的关系。
适用场景:
当需要在不同组件或模块之间进行松散耦合的通信时,发布-订阅模式是一个不错的选择。
当发布者和订阅者之间的关系是动态的,并且它们可能随时加入或离开系统时,发布-订阅模式非常适用。
当希望扩展和修改通信机制,例如引入消息代理或中间件来处理消息时,发布-订阅模式是一个强大的工具。
总结:
观察者模式适用于一对多的关系,主题和观察者之间的通信比较直接。而发布-订阅模式适用于发布者和订阅者之间的松散耦合通信,通过消息代理来进行中介。