1、SpringFramework 对于层次性事件的处理策略是什么?是如何实现的?
1.1 分析
SpringFrameword 的层次性事件基于 BeanFactory 的层次性完成,即 BeanFactory 的父子结构;其中 BeanFactory 的 HierarchicalBeanFactory 子接口的 getParentBeanFactory 方法表示获取父Bean工厂。
ApplicationContext 实现了 HierarchicalBeanFactory,则拥有了父子结构;继承了 ApplicationEventPublsher,则拥有了发布事件的能力;而 ApplicationContext 的第一个抽象类 AbstractApplicationContext 组合了 ApplicationEventMulticaster,则拥有了事件派发的能力。
所以我们可以从 AbstractApplicationContext 的源码分析,SpringFramework 对于层次性事件是如何处理的。
上源码:
/**
* Publish the given event to all listeners.
* @param event the event to publish (may be an {@link ApplicationEvent}
* or a payload object to be turned into a {@link PayloadApplicationEvent})
* @param eventType the resolved event type, if known
* @since 4.2
*/
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
// Decorate event as an ApplicationEvent if necessary
//...... 先忽略
// Publish event via parent context as well...
// 判断是否有父容器,如果有,通知父容器发布事件
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}
所以 SpringFramework 对于层次性事件,是直接判断是否存在父容器,如果存在则调用父容器的 publishEvent() 方法来发布事件;所以如果是子容器发布事件,会通知到父容器;但是如果是父容器发布事件,和子容器就没有一丁点关系了。
1.2 实战
看完源码,还是得来个例子来实战一下。
自定义事件:
public class TestApplicationEvent extends ApplicationEvent {
/**
* Create a new {@code ApplicationEvent}.
* @param source the object on which the event initially occurred or with
* which the event is associated (never {@code null})
*/
public TestApplicationEvent(Object source) {
super(source);
}
}
自定义监听器:
@AllArgsConstructor
public class TestApplicationListener implements ApplicationListener<TestApplicationEvent> {
private String name;
/**
* Handle an application event.
* @param event the event to respond to
*/
@Override
public void onApplicationEvent(TestApplicationEvent event) {
System.out.println(name+" 监听到的事件:"+event.toString());
}
}
测试应用:
public class HierarchicalEventApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext();
AnnotationConfigApplicationContext son = new AnnotationConfigApplicationContext();
son.setParent(parent);
parent.addApplicationListener(new TestApplicationListener("父容器监听器"));
son.addApplicationListener(new TestApplicationListener("子容器监听器"));
// 需父容器先 refresh 再到子容器
parent.refresh();
son.refresh();
// 父子容器的监听器都接收到
son.publishEvent(new TestApplicationEvent("子容器发布的事件"));
// 只有父容器的监听器接收到
parent.publishEvent(new TestApplicationEvent("父容器发布的事件"));
}
}
打印结果:
子容器监听器 监听到的事件:com.github.howinfun.demo.ioc.event_listener.TestApplicationEvent[source=子容器发布的事件]
父容器监听器 监听到的事件:com.github.howinfun.demo.ioc.event_listener.TestApplicationEvent[source=子容器发布的事件]
父容器监听器 监听到的事件:com.github.howinfun.demo.ioc.event_listener.TestApplicationEvent[source=父容器发布的事件]
2、payload 事件与普通事件有什么区别?SpringFramework 在底层是如何兼容它的?
2.1 分析&源码
我们看到上面的事件发布例子,可以看到,如果有新增的事件,我们都需要创建自定义类去继承 ApplicationEvent
(抽象类),然后接着才能继续编写对应的 ApplicationListener。但是这样会非常的麻烦,如果我们只需关注事件里面的内容就好了,即 source
值。
那么是否能像 JDK 提供的 ArrayList 一样,可以让开发者指定范型,那么就只需要一个 ApplicationEvent 就可以了。
刚才,SpringFramework 在 4.2 版本中提供了:PayloadApplicationEvent<T>
。它继承了 ApplicationEvent ,并且实现了 ResolvableTypeProvider
接口,可以提供当前范型的查询方法。
详情看下面源码:
public class PayloadApplicationEvent<T> extends ApplicationEvent implements ResolvableTypeProvider {
private final T payload;
/**
* Create a new PayloadApplicationEvent.
* @param source the object on which the event initially occurred (never {@code null})
* @param payload the payload object (never {@code null})
*/
public PayloadApplicationEvent(Object source, T payload) {
super(source);
Assert.notNull(payload, "Payload must not be null");
this.payload = payload;
}
@Override
public ResolvableType getResolvableType() {
return ResolvableType.forClassWithGenerics(getClass(), ResolvableType.forInstance(getPayload()));
}
/**
* Return the payload of the event.
*/
public T getPayload() {
return this.payload;
}
}
我们看完源码再实战一波,最后再看看 payload 事件和普通事件的区别。
2.2 实战例子
先看看 PayloadApplicationEvent 如何使用:
自定义 Listener:
监听内容类型为Integer的事件
public class IntegerPayloadApplicationEventListener implements ApplicationListener<PayloadApplicationEvent<Integer>> {
/**
* Handle an application event.
* @param event the event to respond to
*/
@Override
public void onApplicationEvent(PayloadApplicationEvent<Integer> event) {
System.out.println("接收到数字型Payload事件,数字为:"+event.getPayload());
}
}
应用:
public class Application {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.addApplicationListener(new IntegerPayloadApplicationEventListener());
applicationContext.refresh();
// 发布Integer类型的事件内容
applicationContext.publishEvent(12334);
applicationContext.publishEvent(56578);
applicationContext.close();
}
}
我们可以发现,如果是利用 PayloadApplicationEvent 来做事件,我们发布事件时,只需要直接传入事件内容,而无需再新建一个 PayloadApplicationEvent;只需要保证发布的内容的类型和自定义 ApplicationListener<PayloadApplicationEvent<T>>
监听中的 T 泛型对应上即可。
那 ApplicationEventPublisher 是如何支持 PayloadApplicationEvent 的呢,其实在 AbstractApplicationContext#publishEvent() 方法中就可以看到,只不过我们上面放的源码忽略掉而已,下面将忽略的代码再放出来:
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
// 这里是兼容 PayloadApplicationEvent 的开始
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
}
}
// Multicast right now if possible - or lazily once the multicaster is initialized
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
我们可以看到,调用publishEvent方法后,会先判断事件是否为 ApplicationEvent 类型,如果不是的话会封装成 PayloadApplicationEvent,所以我们才不需要自己传入 PayloadApplicationEvent。
区别:
- 使用 PayloadApplicationEvent,我们不再需要创建很多自定义的 ApplicationEvent;只需要直接发布事件内容
- 但还是需要根据事件泛型去创建不同的 ApplicationListener 去监听不同泛型的 PayloadApplicationEvent。
兼容
至于 SpringFramework 底层是如何兼容的,相信看完上面的源码大家就清楚了,这里不再做分析。
3、最后的彩蛋
上面说到,我们使用 PayloadApplicationEvent 时需要指定泛型,那假设我们不指定,会发生什么。下面我们直接走源码分析一下 ApplicationListener 是如何兼容 PayloadApplicationEvent 的,主要分析的是获取适配的所有 ApplicationListener。
入口可以直接去到:org.springframework.context.event.AbstractApplicationEventMulticaster#getApplicationListeners
发布事件后,会通过派发器(ApplicationEventMulticaster)去派发事件,派发器会根据当前事件去找到所有匹配的 ApplicatioinListener:
- 判断本地缓存是否有对应监听器缓存的缓存:key:事件类型+容器类型,value:监听器列表
- 缓存有,直接返回,否则走查询逻辑
- 遍历当前容器的所有监听器
- 利用 supportsEvent 方法判读当前监听器是否支持当前事件
supportEvent底层是使用 org.springframework.core.ResolvableType#isAssignableFrom(org.springframework.core.ResolvableType) 方法来判断,那么我们直接看看这个isAssignableFrom的逻辑。
源码:
/**
* Determine whether this {@code ResolvableType} is assignable from the
* specified other type.
* <p>Attempts to follow the same rules as the Java compiler, considering
* whether both the {@link #resolve() resolved} {@code Class} is
* {@link Class#isAssignableFrom(Class) assignable from} the given type
* as well as whether all {@link #getGenerics() generics} are assignable.
* @param other the type to be checked against (as a {@code ResolvableType})
* @return {@code true} if the specified other type can be assigned to this
* {@code ResolvableType}; {@code false} otherwise
*/
public boolean isAssignableFrom(ResolvableType other) {
return isAssignableFrom(other, null);
}
我们简单看一下注视即可,我们可以看到,这个方法最终的原理是调用 Class#isAssignableFrom 这个方法。
而这个方法是判断当前类是否为其他类的父类或接口。那么到这我们已经很清楚了。如果 PayloadApplicationEvent 没有指定具体泛型,那么就是 Object,而 Object 是所有类的父类,那么监听器将会接收所有类型的事件(PayloadApplicationEvent<?>)。
实战
下面我们用例子来实战一下
我们基于上面的 IntegerPayloadApplicationEventListener 例子继续造东西就行了。
新增 ApplicationListener:
public class ObjectPayLoadApplicationEventListener implements ApplicationListener<PayloadApplicationEvent> {
/**
* Handle an application event.
* @param event the event to respond to
*/
@Override
public void onApplicationEvent(PayloadApplicationEvent event) {
System.out.println("ObjectPayLoadApplicationEventListener 接收到数字型Payload事件,数字为:"+event.getPayload());
}
}
应用添加上面监听器:
public class Application {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.addApplicationListener(new IntegerPayloadApplicationEventListener());
applicationContext.addApplicationListener(new ObjectPayLoadApplicationEventListener());
applicationContext.refresh();
/**
* 我们可以发现,如果是利用 PayloadApplicationEvent 来做事件,我们发布事件时,只需要直接传入事件内容,而无需再新建一个 PayloadApplicationEvent
* 只需要保证发布的内容的类型和自定义ApplicationListener<PayloadApplicationEvent<T>>监听中的 T 对应上即可。
*/
applicationContext.publishEvent(12334);
applicationContext.publishEvent(56578);
applicationContext.close();
}
}
打印结果:
IntegerPayloadApplicationEventListener 接收到数字型Payload事件,数字为:12334
ObjectPayLoadApplicationEventListener 接收到数字型Payload事件,数字为:12334
IntegerPayloadApplicationEventListener 接收到数字型Payload事件,数字为:56578
ObjectPayLoadApplicationEventListener 接收到数字型Payload事件,数字为:56578
最后我们可以发现,ObjectPayLoadApplicationEventListener 也同时收到了事件。
完结~