事件 简单来说就是发生了什么,然后争对这个发生的操作 怎么处理。比如 页面上有个 按钮,当点击的时候,应该怎么处理。看下下面的代码
$('#btn').on('click',function(){ console.log('btn click event') })
通过 js 我们知道,当点击按钮时,会打印 btn click event
这么一句话。
但不点击时,什么也不会发生。
从上可以分析得到
- 有
按钮
这么个东东, 发生的动作会作用在它身上, 我们称 这个 按钮为事件源
- 有
点击(click)
这么个动作,作用在 事件源 上, 可以将这一过程 抽象为事件对象
。发生的事件中要包含事件源,这样可以在对事件处理的操作中获取到事件源 - 有
console.log()
这么个操作,这表示发生了这个事件 应该怎么处理的操作,我们将这一过程抽象为事件监听
即某个 事件监听器(一个函数或者方法)
会一直监视着某个 事件源(比如按钮)
的某个 操作(比如点击)
当发生相应的动作后,这个监听器就会执行相应的操作。
需要注意的是:
- 事件对象中 包含事件源 ,也就说 事件源是事件对象的一个属性
- 监听器 需要注册到 事件源上
Java 中的事件
在 Java 中
- 事件对象 被抽象到
java.util.EventObject
, 这个对象中包含一个 Object 的属性,也即 事件源 (可扩展) - 事件监听器 被抽象到
java.util.EventListener
中 ,这是一个标识接口,需要自己定义监听方法 - 事件源 需要自己定义
在 Java 中 模拟 按钮点击事件 如下
事件对象:
public class ClickEventObject extends EventObject { /** * 这个msg 可随意扩展 */ private String msg; public ClickEventObject(Object source, String msg) { super(source); this.msg = msg; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } }
事件监听器
public interface ClickEventListener extends EventListener { /** * 事件处理 (我这里是接口) * @param eventObject 事件对象 */ void handleEvent(EventObject eventObject); }
事件源
public class Button { private ClickEventListener clickEventListener; /** * 注册 监听器 * 我这里只是用了单个 EventListener ,你可以改成 List<EventListener> 这样一个事件源就可以注册多个监听器了 * @param clickEventListener 点击事件监听器 */ public void registerListener(ClickEventListener clickEventListener) { this.clickEventListener = clickEventListener; } /** * 触发事件 */ public void triggerClick() { // this 表示事件源本身 这样也就是button ClickEventObject clickEventObject = new ClickEventObject(this,"点击事件处理"); this.clickEventListener.handleEvent(clickEventObject); } }
测试
public static void main(String[] args) { // 事件源 Button button = new Button(); // 注册监听器 button.registerListener(new ClickEventListener() { // 实例化 监听器处理 @Override public void handleEvent(EventObject eventObject) { if (eventObject instanceof ClickEventObject) { System.out.println(((ClickEventObject) eventObject).getMsg()); } } }); // 出发点击事件 button.triggerClick(); }
可以看到
- 事件对象只是关联事件源和事件监听器的中间对象,它其中包含 事件源
- 事件监听器需要注册到事件源上
Spring 中的事件
在 spring 的事件中 其实也是扩展了 java 中的 EventListener
和 EventObject
ApplicationEvent
直接继承了 EventObject
ApplicationListener
直接继承了 EventListener
,并且定义了 onApplicationEvent
方法,需要注意这里使用了泛型指定了特定的 EventObject
在 spring 中写一个 按钮 hover 的事件如下
事件对象
public class HoverEvent extends ApplicationEvent { /** * 自定义的消息 ,这个可以随意扩展 */ private String message; public HoverEvent(Object source, String message) { super(source); this.message = message; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }
事件监听器
@Component public class HoverListener implements ApplicationListener<HoverEvent> { /** * 事件处理 * @param hoverEvent hover事件 */ @Override public void onApplicationEvent(HoverEvent hoverEvent) { System.out.println(hoverEvent.getMessage()); } }
事件源
@Component public class Button { @Autowired ApplicationContext applicationContext; public void triggerHover(String message) { applicationContext.publishEvent(new HoverEvent(this,message)); } }
测试
public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config.class); Button button = applicationContext.getBean(Button.class); button.triggerHover("Hover事件处理"); }
其中 Config.class
是 spring 启动时的 配置类,这里直接将 事件源 和 事件监听器 交给 spring 管理
@ComponentScan("com.slarn.event.spring") @Configuration public class Config { }
从上面代码可以看到在 事件源 中,注入了 ApplicationContext
,触发 triggerHover
后 ,将操作交给了 applicationContext.publishEvent(EventObject)
方法
从这里可以猜测,applicationContext
是触发事件者,在 publishEvent
中必然有 调用 最终的事件处理方法,即最终会调用 HoverListener
的 onApplicationEvent(EventObject)
方法
跟踪代码可以发现 其调用栈如下:
org.springframework.context.ApplicationEventPublisher#publishEvent(org.springframework.context.ApplicationEvent) --> org.springframework.context.support.AbstractApplicationContext#publishEvent(java.lang.Object, org.springframework.core.ResolvableType) --> org.springframework.context.support.AbstractApplicationContext#getApplicationEventMulticaster --> org.springframework.context.event.ApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType) --> org.springframework.context.event.SimpleApplicationEventMulticaster#invokeListener -- > org.springframework.context.event.SimpleApplicationEventMulticaster#doInvokeListener
在 SimpleApplicationEventMulticaster#doInvokeListener 的方法中 可以看到 最终执行了 listener.onApplicationEvent(event);
方法,完成事件的回调。
到这里你可能会发现一个问题就是,之前说 事件监听器 是要注册到事件源上,但是在上面的 Button
中,却没有这个操作,这是怎么回事呢?是在哪里注册的呢?
带着这个问题,我们重新看下上面的调用栈 ,最终是执行了 listener.onApplicationEvent(event)
,那么这个 listener 是怎么来的呢?
往上找发现有如下代码
org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent()
@Override public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); Executor executor = getTaskExecutor(); for (ApplicationListener<?> listener : getApplicationListeners(event, type)) { if (executor != null) { executor.execute(() -> invokeListener(listener, event)); } else { invokeListener(listener, event); } } }
由此可以看到 getApplicationListeners(event, type )
可以获取到所有的监听器,也可以看出来 一个事件源上 可能有多个监听器。
这里就是 获取所有的监听器,然后遍历执行每个 listener 的 onApplicationEvent 方法
继续跟踪 getApplicationListeners(),发现他是 org.springframework.context.event.AbstractApplicationEventMulticaster#getApplicationListeners()
在这个地方调用的 然后会发现 AbstractApplicationEventMulticaster
其实是 ApplicationEventMulticaster
的实现类,打开这个 ApplicationEventMulticaster
瞄一眼,发现了什么
public interface ApplicationEventMulticaster { /** * Add a listener to be notified of all events. * @param listener the listener to add */ void addApplicationListener(ApplicationListener<?> listener); /** * Add a listener bean to be notified of all events. * @param listenerBeanName the name of the listener bean to add */ void addApplicationListenerBean(String listenerBeanName); /** * Remove a listener from the notification list. * @param listener the listener to remove */ void removeApplicationListener(ApplicationListener<?> listener); /** * Remove a listener bean from the notification list. * @param listenerBeanName the name of the listener bean to remove */ void removeApplicationListenerBean(String listenerBeanName); /** * Remove all listeners registered with this multicaster. * <p>After a remove call, the multicaster will perform no action * on event notification until new listeners are registered. */ void removeAllListeners(); /** * Multicast the given application event to appropriate listeners. * <p>Consider using {@link #multicastEvent(ApplicationEvent, ResolvableType)} * if possible as it provides better support for generics-based events. * @param event the event to multicast */ void multicastEvent(ApplicationEvent event); /** * Multicast the given application event to appropriate listeners. * <p>If the {@code eventType} is {@code null}, a default type is built * based on the {@code event} instance. * @param event the event to multicast * @param eventType the type of event (can be {@code null}) * @since 4.2 */ void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType); }
其中定义了一个 void addApplicationListener(ApplicationListener<?> listener);
方法,看到这里你会想到什么? 通过名字我们可以猜测 这里有可能就是 注册监听器 的地方,为了验证,我们直接在这方法的实现类上 打上一个断点
这个方法的实现类为
org.springframework.context.event.AbstractApplicationEventMulticaster#addApplicationListener
如果你对 spring bean 生命周期有一定了解的话,通过调试 跟踪调用栈 可以发现:
在 spring 初始化 bean [实例化,填充属性之后] 的时候(也就是执行 AbstractAutowireCapableBeanFactory#initializeBean()方法)
会在初始化方法调用之后 调用 applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
在这个方法中 获取所有的后置处理器(BeanPostProcessor),然后执行每个后置处理器的 postProcessAfterInitialization()
方法
其中有一个后置处理器为 ApplicationListenerDetector
,在他的 postProcessAfterInitialization()
方法中 有如下代码
最终在这个操作里面 把 类型为 ApplicationListener 的 listener 加入到 org.springframework.context.event.AbstractApplicationEventMulticaster.ListenerRetriever#applicationListeners 这个 Set 集合中。
这样在发布(publishEvent)的时候 从这里获取 所有的 listener 然后遍历调用 listener 的 onApplicationEvent
就和上面对应起来了
你可能会问 这个 ApplicationListenerDetector 这个后置处理器 上面为什么能够获取到,其实 这是 spring 内部 手动注册的,注册的代码在 ApplicationContext 刷新 refresh()方式时
org.springframework.context.support.AbstractApplicationContext#registerBeanPostProcessors --> org.springframework.context.support.PostProcessorRegistrationDelegate#registerBeanPostProcessors(org.springframework.beans.factory.config.ConfigurableListableBeanFactory, org.springframework.context.support.AbstractApplicationContext)
在这个最后一段代码为:
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
另外细心一下你会发现:
- applicationContext 的 publishEvent 方法 其实是 ApplicationEventPublisher 这个接口指定的方法,表示发布事件 (事件触发者),在 AbstractApplicationContext 中对这方法 进行了实现
- ApplicationEvent 实现 有 ApplicationContextEvent,ContextRefreshedEvent 这个里面你会发现 source 其实换成了 ApplicationContext,也就是说 ApplicationContext 就是事件源
总结一下:
- spring 在实例化 bean 之前 会手动注册一个后置处理器
ApplicationListenerDetector
- 在实例化 bean ,填充属性 之后的 初始化 bean 中 initializeBean 方法的
postProcessAfterInitialization
方法中会获取 所有的后置处理器 遍历执行处理,其中就包括上面的 ApplicationListenerDetector - 在
ApplicationListenerDetector
中 会判断 bean 是否是 ApplicationListener,如果是的话就 把这个 bean(listener) 加入到org.springframework.context.event.AbstractApplicationEventMulticaster.ListenerRetriever#applicationListeners
这个 Set 集合中 - AbstractApplicationEventMulticaster 这个类 是 AbstractApplicationContext 抽象类中的一个 属性(并且不为空,在 refresh()方法的
initApplicationEventMulticaster()
中初始化了一个SimpleApplicationEventMulticaster
的多播器),也就是说 ApplicationContext 有AbstractApplicationEventMulticaster
,即也能能获取到所有的 listeners - ApplicationContext 继承了 ApplicationEventPublisher,同时 AbstractApplicationContext 也实现了 ApplicationEventPublisher 的 publishEvent
- 在这个 publishEvent() 方法中就获取到 所有的 listeners 然后遍历 执行
listener.onApplicationEvent(event);
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于