事件机制

概念

  1. 事件驱动模型:当事件被触发的时候,将事件加入一个事件队列,然后通过主程序不断轮询事件队列,处理目标函数。常见的事件驱动如鼠标点击事件、IO事件等,观察者设计模式就是事件驱动的一个很好实现。
  2. 消息驱动模型/发布订阅模型:本质上说,事件驱动和消息驱动相当,只是各自应用在不同的场景下。事件模式耦合高,同模块内好用;消息模式耦合低,跨模块好用。

Spring的事件

在Spring中提供了几种标准事件:

  1. ContextRefreshEvent:当ApplicationContext容器初始化完成或者被刷新的时候,就会发布该事件。
  2. ContextStartedEvent:当ApplicationContext启动的时候发布事件,即调用ConfigurableApplicationContext接口的start方法的时候。
  3. ContextStopeedEvent:当ApplicationContext容器停止的时候发布事件,即调用ConfigurableApplicationContext接口的close方法的时候。
  4. ContextCloseEvent:当ApplicationContext容器关闭的时候发布事件,即调用ConfigurableApplicationContext接口的close方法的时候,关闭指的是所有的单例Bean都被销毁
  5. RequestHandledEvent:只能用于DispathcerServlet的web应用,Spring处理用户请求结束后,系统会触发该事件。

实现机制

主要组成

事件对象:ApplicationEvent,继承自EventObject

 1 public class EventObject implements java.io.Serializable {
 2     /**
 3      * 发生事件的对象
 4      */
 5     protected transient Object  source;
 6 
 7     public EventObject(Object source) {
 8         if (source == null)
 9             throw new IllegalArgumentException("null source");
10 
11         this.source = source;
12     }
13 
14     public Object getSource() {
15         return source;
16     }
17 
18     public String toString() {
19         return getClass().getName() + "[source=" + source + "]";
20     }
21 }
22 public abstract class ApplicationEvent extends EventObject {
23     /** 事件发生的毫秒值 */
24     private final long timestamp;
25 
26     public ApplicationEvent(Object source) {
27         super(source);
28         this.timestamp = System.currentTimeMillis();
29     }
30 
31     public final long getTimestamp() {
32         return this.timestamp;
33     }
34 
35 }

 

事件监听器:ApplicationListener,是一个接口,继承自EventListener,实际中需要实现其onApplicationEvent(E event)方法。

 1 public interface EventListener {
 2 }
 3 @FunctionalInterface
 4 public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
 5 
 6     /**
 7      * 处理事件
 8      */
 9     void onApplicationEvent(E event);
10 
11 }

 

事件发布:ApplicationEventPublisher,是一个接口,包含publishEvent()方法,ApplicationContext继承了该接口,在AbstractApplicationContext中实现了事件的发布接口,其中使用了ApplicationEventMulticaster对象完成发布。ApplicationEventMulticaster负责管理监听器,以及向监听器发布事件。

 1 @FunctionalInterface
 2 public interface ApplicationEventPublisher {
 3 
 4     /**
 5      * 通知所有注册在此通知者中的所有匹配的监听者
 6      */
 7     default void publishEvent(ApplicationEvent event) {
 8         publishEvent((Object) event);
 9     }
10 
11     /**
12      * 通知所有注册在此通知者中的所有匹配的监听者(从Spring4.2开始有这个方法)
13      */
14     void publishEvent(Object event);
15 
16 }

 

 1 public abstract class AbstractApplicationContext extends DefaultResourceLoader
 2         implements ConfigurableApplicationContext {
 3         
 4     /** 发布事件的多播器 */
 5     @Nullable
 6     private ApplicationEventMulticaster applicationEventMulticaster;
 7         
 8     protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
 9         Assert.notNull(event, "Event must not be null");
10 
11         // 如有必要,将事件对象强转为 ApplicationEvent
12         ApplicationEvent applicationEvent;
13         if (event instanceof ApplicationEvent) {
14             // 如果被作为事件的类是ApplicationEvent类型的,强转为ApplicationEvent类型
15             applicationEvent = (ApplicationEvent) event;
16         }
17         else {
18             // 如果被作为事件的类不是ApplicationEvent类型,则将其包装为PayloadApplicationEvent类型
19             applicationEvent = new PayloadApplicationEvent<>(this, event);
20             if (eventType == null) {
21                 eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
22             }
23         }
24 
25         // 如果可能,现在就进行多播 - 或者在多播器初始化后懒惰地进行
26         if (this.earlyApplicationEvents != null) {
27             this.earlyApplicationEvents.add(applicationEvent);
28         }
29         else {
30             // 获取多播器发布事件
31             getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
32         }
33 
34         // 也通过父上下文发布事件...
35         if (this.parent != null) {
36             if (this.parent instanceof AbstractApplicationContext) {
37                 ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
38             }
39             else {
40                 this.parent.publishEvent(event);
41             }
42         }
43     }
44     
45     /**
46      * 新增一个接收上下文事件的ApplicationListener
47      * 注意,当上下文未激活时,任何注册上来的ApplicationListener都会在refresh的时候被应用
48      * 当上下文激活时,会和当前的事件多播器一起应用
49      */
50     @Override
51     public void addApplicationListener(ApplicationListener<?> listener) {
52         Assert.notNull(listener, "ApplicationListener must not be null");
53         if (this.applicationEventMulticaster != null) {
54             // 调用多播器的方法在多播器上注册监听器
55             this.applicationEventMulticaster.addApplicationListener(listener);
56         }
57         this.applicationListeners.add(listener);
58     }
59     
60 }

 

 1 /**
 2  * 实现这个接口,可以管理多个ApplicationListener对象,并向它们发布事件
 3  */
 4 public interface ApplicationEventMulticaster {
 5 
 6     /**
 7      * 添加一个侦听器接收所有事件的通知
 8      */
 9     void addApplicationListener(ApplicationListener<?> listener);
10 
11     /**
12      * 添加一个监听器bean接收所有事件的通知
13      */
14     void addApplicationListenerBean(String listenerBeanName);
15 
16     /**
17      * 从通知列表中删除一个侦听器。
18      */
19     void removeApplicationListener(ApplicationListener<?> listener);
20 
21     /**
22      * 从通知列表中删除一个侦听器bean。
23      */
24     void removeApplicationListenerBean(String listenerBeanName);
25 
26     /**
27      * 删除在此多播器中注册的所有侦听器。
28      * 在删除调用之后,多播器将不会对事件通知执行任何操作,直到注册新的侦听器。
29      */
30     void removeAllListeners();
31 
32     /**
33      * 将给定的应用程序事件多播到适当的侦听器
34      * 尽可能使用multicastEvent(ApplicationEvent, ResolvableType),因为他为基于泛型的事件提供了更好的支持
35      * @param event the event to multicast
36      */
37     void multicastEvent(ApplicationEvent event);
38 
39     /**
40      * 将给定的应用程序事件多播到适当的侦听器
41      * 如果eventType为null,则将基于事件实例构建默认的类型
42      */
43     void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);
44 
45 }

 

使用方法

定义一个事件类

 1 /**
 2  * 这里也可以不继承ApplicationEvent,那么当事件发布的时候,这个事件的对象会被包装成PayloadApplicationEvent类型,具体看前文讲的AbstractApplicationContext.publishEvent方法
 3  */
 4 public class MyEvent extends ApplicationEvent {
 5 
 6     private String name;
 7 
 8     /**
 9      * Create a new ApplicationEvent.
10      *
11      * @param source the object on which the event initially occurred (never {@code null})
12      */
13     public MyEvent(Object source, String name) {
14         super(source);
15         this.name = name;
16     }
17 
18     public String getName() {
19         return name;
20     }
21 
22     public void setName(String name) {
23         this.name = name;
24     }
25 }

 

定义一个事件监听器类

 1 @Component
 2 public class MyEventListener {
 3 
 4     /**
 5      * 使用EventListener注解,能够在单例bean初始化时,进入AbstractApplicationContext.addApplicationListener(ApplicationListener<?>)方法,
 6      * 将此监听方法包装成一个ApplicationListener注册到ApplicationEventMulticaster中
 7      */
 8     @EventListener(MyEvent.class)
 9     public void handleEvent(MyEvent event) {
10         System.out.println("处理MyEvent事件中,name: " + event.getName() + ", 线程名: " + Thread.currentThread().getName());
11     }
12 
13 }

 

发布事件

 1 @Service
 2 public class EventServiceImpl implements EventService {
 3     @Autowired
 4     private ApplicationContext context;
 5 
 6     @Override
 7     public void publishEvent(String name) {
 8         System.out.println("发布事件中...线程名: " + Thread.currentThread().getName());
 9         context.publishEvent(new MyEvent(this, name));
10     }
11 }

 

调用EventService.publishEvent(String)方法,输出如下:

发布事件中...线程名: http-nio-8081-exec-1
处理事件中,name: tongxin, 线程名: http-nio-8081-exec-1

可以看到事件发布后进入了监听器的代码中,且是在同一个线程中运行的。

posted @ 2022-08-05 13:33  蓝瓶的真好喝  阅读(172)  评论(0编辑  收藏  举报