事件机制
概念
- 事件驱动模型:当事件被触发的时候,将事件加入一个事件队列,然后通过主程序不断轮询事件队列,处理目标函数。常见的事件驱动如鼠标点击事件、IO事件等,观察者设计模式就是事件驱动的一个很好实现。
- 消息驱动模型/发布订阅模型:本质上说,事件驱动和消息驱动相当,只是各自应用在不同的场景下。事件模式耦合高,同模块内好用;消息模式耦合低,跨模块好用。
Spring的事件
在Spring中提供了几种标准事件:
- ContextRefreshEvent:当ApplicationContext容器初始化完成或者被刷新的时候,就会发布该事件。
- ContextStartedEvent:当ApplicationContext启动的时候发布事件,即调用
ConfigurableApplicationContext
接口的start
方法的时候。 - ContextStopeedEvent:当ApplicationContext容器停止的时候发布事件,即调用
ConfigurableApplicationContext
接口的close
方法的时候。 - ContextCloseEvent:当ApplicationContext容器关闭的时候发布事件,即调用
ConfigurableApplicationContext
接口的close
方法的时候,关闭指的是所有的单例Bean都被销毁 - 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
可以看到事件发布后进入了监听器的代码中,且是在同一个线程中运行的。