1.Listener(监听器)如何使用
1.1 使用@AppEventListener注解往容器中添加监听器
1.1.1 创建三个我们的自定义事件
自定义事件1-ChangeEvent,简单继承ApplicationEvent类并实现序列化(Serializable)接口,实现序列化接口需要写序列化id(serialVersionUID)
public class ChangeEvent extends ApplicationEvent implements Serializable {
private static final long serialVersionUID = 0L;
public ChangeEvent(Object source) {
super(source);
}
}
自定义事件2-MessageEvent,简单继承ApplicationEvent类并实现序列化(Serializable)接口,实现序列化接口需要写序列化id(serialVersionUID)
public class MessageEvent extends ApplicationEvent implements Serializable {
private static final long serialVersionUID = 0L;
public MessageEvent(Object source) {
super(source);
}
}
自定义事件3-PayloadEvent,这种方式什么也不做,就使用普通的Java对象作为事件发布,这种方式在后面会被包装成为PayloadApplicationEvent对象
public class PayloadEvent {
}
1.1.2 创建监听器,对事件发布做出响应
使用注解的方式往容器中添加监听器对象,也就是发生对应的事件之后需要做出的响应。
@Component
public class AppEventListener {
public AppEventListener() {
System.out.println("AppEventListener");
}
@EventListener(MessageEvent.class)
public void listenMessage(MessageEvent messageEvent) {
System.out.println("---MessageEvent---" + messageEvent.getSource());
}
@EventListener(ChangeEvent.class)
public void listenChange(ChangeEvent changeEvent) {
System.out.println("---ChangeEvent---" + changeEvent.getSource());
}
@EventListener(PayloadApplicationEvent.class)
public void listenPayLoad(PayloadApplicationEvent<PayloadEvent> event) {
System.out.println("---payload---" + event.getPayload());
}
}
写好了事件,以及监听到事件之后我们的处理方法,那么我们如何发布事件?
1.2 如何发布事件
1.2.1 我们可以通过IOC容器直接发布事件
拿到容器,直接使用IOC容器的publishEvent方法发布事件,就能收到对应的响应
ApplicationContext context = new AnnotationConfigApplicationContext(MyListenerTest.class);
context.publishEvent(new MessageEvent("message"));
context.publishEvent(new ChangeEvent("change"));
context.publishEvent(new PayloadEvent());
1.2.2 我们可以往组件中注入ApplicationEventPublisher,通过这个它来进行发布事件
比如我们自定义一个组件AutowirePublisher,往这个组件中注入ApplicationEventPublisher组件,我们可以调用这个组件它的publishEvent方法进行发布事件。
@Component
public class AutowirePublisher {
@Autowired
ApplicationEventPublisher applicationEventPublisher;
public AutowirePublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
public void publisher() {
applicationEventPublisher.publishEvent(new MessageEvent("mesg"));
applicationEventPublisher.publishEvent(new ChangeEvent("chg"));
applicationEventPublisher.publishEvent(new PayloadEvent());
}
}
使用如下代码进行测试
ApplicationContext context = new AnnotationConfigApplicationContext(MyListenerTest.class);
final AutowirePublisher bean = context.getBean(AutowirePublisher.class);
bean.publisher(); //发布我们自定义的事件
1.2.3那么这个ApplicationEventPublisher是哪里来的?
其实这个组件在refresh的第三步prepareBeanFactory中就已经为我们导入了这个组件,以及一些其他的组件,需要用到时我们直接Autowire即可。
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
1.3 使用实现接口的方式创建监听器
1.3.1 实现ApplicationEvent接口创建监听事件
public class TestApplicationEvent extends ApplicationEvent implements Serializable {
private static final long serialVersionUID = 0L;
public TestApplicationEvent(Object source) {
super(source);
}
}
1.3.2 实现ApplicationListener接口创建监听器
@Component
public class TestApplicationListener implements ApplicationListener<TestApplicationEvent> {
@Override
public void onApplicationEvent(TestApplicationEvent event) {
System.out.println("---TestApplicationListener---" + event.getSource());
}
}
1.3.3 发布事件
applicationEventPublisher.publishEvent(new TestApplicationEvent("test"));
我们也能将自定义的事件发布。需要注意的是使用@AppEventListener和往容器中加入监听器组件这两种方式的执行过程中会有些许不同,在下面的流程中会详细讲述。
2.监听器的实现原理是什么?
2.1 加了@EventListener这个注解是的方法Spring是怎么进行处理的?
在AnnotationConfigApplicationContext的无参构造器中对Spring内置的几个BeanDefinition导入到容器当中。(详情可以看我这篇博客Spring源码内部4-6个组件的来源)
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
与监听器相关的两个组件就是上面的两个它们对应的beanClass为
org.springframework.context.event.EventListenerMethodProcessor
org.springframework.context.event.DefaultEventListenerFactory
2.1.1 我们来看EventListenerMethodProcessor这个类的继承关系
public class EventListenerMethodProcessor implements SmartInitializingSingleton, ApplicationContextAware, BeanFactoryPostProcessor
- 1.实现了ApplicationContextAware接口,用来给组件中注入容器对象
- 2.实现了BeanFactoryPostProcessor接口,这个接口会在invokeBeanFactoryPostProcessors中执行
- 3.实现了SmartInitializingSingleton接口,它会在preInstantiateSingletons中创建了全部的Bean之后执行,它会遍历每个实现了这个接口的Bean,执行afterSingletonsInstantiated方法
接下来我们来看它实现的BeanFactoryPostProcessor重写的方法实现了哪些功能?
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
//将所有实现了EventListenerFactory接口的Bean(事件监听器工厂)拿出来,调用list#sort方法进行排序
Map<String, EventListenerFactory> beans = beanFactory.getBeansOfType(EventListenerFactory.class, false, false);
List<EventListenerFactory> factories = new ArrayList<>(beans.values());
AnnotationAwareOrderComparator.sort(factories);
this.eventListenerFactories = factories;
}
它主要实现的功能是,将容器中所有的EventListenerFactory实现类都拿出来,并进行排序,其实也就没做什么。
接下来我们来看它实现的SmartInitializingSingleton接口所重写的方法所做的事有哪些?
@Override
public void afterSingletonsInstantiated() {
ConfigurableListableBeanFactory beanFactory = this.beanFactory;
Assert.state(this.beanFactory != null, "No ConfigurableListableBeanFactory set");
//将容器中所有的组件的BeanName都拿出来
String[] beanNames = beanFactory.getBeanNamesForType(Object.class);
for (String beanName : beanNames) {
//.........一些校验逻辑,这里忽略次要因素........
try {
type = AutoProxyUtils.determineTargetClass(beanFactory, beanName);
}
try {
//对Bean进行处理
processBean(beanName, type);
}
}
}
private void processBean(final String beanName, final Class<?> targetType) {
Map<Method, EventListener> annotatedMethods = null;
try {
/**
* 拿出来这个组件中的每个加了@EventListener注解的方法
*/
annotatedMethods = MethodIntrospector.selectMethods(targetType,
(MethodIntrospector.MetadataLookup<EventListener>) method ->
AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));
}
//............一些校验逻辑,这里忽略次要因素..................
ConfigurableApplicationContext context = this.applicationContext;
List<EventListenerFactory> factories = this.eventListenerFactories;
for (Method method : annotatedMethods.keySet()) {
for (EventListenerFactory factory : factories) {
if (factory.supportsMethod(method)) {
Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
/**
* 将beanName,beanType,method包装成为一个ApplicationListenerMethodAdapter(监听方法适配器)
* ApplicationListenerMethodAdapter这个类实现了ApplicationListener接口和Ordered接口
* 也就是说这个适配器对象本身也是监听器对象(实际上在容器中放的监听器就是适配器对象)
*
* 封装成ApplicationListenerMethodAdapter这个适配器对象,并调用init方法进行初始化
*/
ApplicationListener<?> applicationListener =
factory.createApplicationListener(beanName, targetType, methodToUse);
if (applicationListener instanceof ApplicationListenerMethodAdapter) {
((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
}
/**
* 往容器中的监听器列表以及多播器列表中同时添加ApplicationListenerMethodAdapter对象
*/
context.addApplicationListener(applicationListener);
break;
}
}
}
}
- 1.遍历容器中所有的组件,调用processBean进行处理
- 2.拿出来这个组件中加了@EventListener注解的方法
- 2.1遍历所有的EventListenerFactory(默认只有DefaultEventListenerFactory、TransactionalEventListenerFactory两个实现类,并且默认注入的组件只有DefaultEventListenerFactory)
- 2.2遍历加了@EventListener的方法,将beanName,beanType,以及method封装成为一个ApplicationListenerMethodAdapter对象(它也实现了ApplicationListener接口,也是说这个适配器对象也是监听器对象)并调用init方法进行初始化
- 2.3将这些封装好的ApplicationListenerMethodAdapter放到ApplicationContext容器当中的监听器列表以及多播器列表当中。放在多播器的(applicationEventMulticaster.defaultRetriever.applicationListeners)字段中
那么DefaultEventListenerFactory这个组件是用来干嘛的?从名字我们可以大概知道它是一个用来存放事件监听器的工厂。
2.2 使用实现ApplicationListener接口方式往容器中添加组件的方式是怎么进行处理的?
这就涉及到我们Spring容器刷新(refresh)的第10个步骤registerListeners
protected void registerListeners() {
// Register statically specified listeners first.
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let post-processors apply to them!
/**
* 这里拿到的是容器中组件(加了@Component注解的组件)的Listener
* 使用@EventListener注解的方法不是在这里添加的,是在所有的Bean初始化完成之后才添加到容器当中
*/
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String listenerBeanName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
// Publish early application events now that we finally have a multicaster...
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}
这个方法中用来注册一些监听器的组件的Name到列表中以及完成一些早期事件的发布,因为这一步执行的时候真实的Bean还没完成实例化和初始化等操作,这里只是将BeanName放到applicationListenerBeans这个列表中,真正的监听器对象是放到applicationListeners这个列表当中的。
那么问题来了,我们的监听器组件是在哪里被放到容器当中的?如何被放进去容器当中的呢?
2.2.1 关于ApplicationListenerDetector组件来源以及作用
这个组件是什么?它从哪里来?它其实在refresh的第三步,也就是prepareBeanFactory(beanFactory的准备工作)中往容器中加入了这个组件,也就是下面这行代码进行注册的-->beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
。
这个组件有什么作用?我们的监听器组件是通过这个组件的postProcessAfterInitialization方法中将创建好的监听器 组件 放到监听器列表(applicationListeners)中以及对应的多播器中。
2.2.2 关于ApplicationListenerDetector的核心部分源码
这个组件实现了MergedBeanDefinitionPostProcessor接口,因此它至少有以下的三个方法,下面分别解析
ApplicationListenerDetector#postProcessMergedBeanDefinition
这个方法主要是将实现了ApplicationListener接口的相关信息放在singletonNames列表中,用来在after方法的判断是否要将这个bean加入到容器当中
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
/**
* 如果bean实现了ApplicationListener接口,就把它放在singletonNames列表中
* key-->beanName
* value-->isSingleton(是否单例)
*/
if (ApplicationListener.class.isAssignableFrom(beanType)) {
this.singletonNames.put(beanName, beanDefinition.isSingleton());
}
}
来自BeanPostProcessor接口的before方法和after方法
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof ApplicationListener) {
// potentially not detected as a listener by getBeanNamesForType retrieval
Boolean flag = this.singletonNames.get(beanName);
/**
* 将创建好的监听器这个Bean,加到存放监听器对象的列表(applicationListeners)以及多播器当中
*/
if (Boolean.TRUE.equals(flag)) {
this.applicationContext.addApplicationListener((ApplicationListener<?>) bean);
}
else if (Boolean.FALSE.equals(flag)) {
this.singletonNames.remove(beanName);
}
}
return bean;
}
before方法啥也没做。after方法则主要判断这个监听器是否单例,如果是单例的就把它加入到监听器列表/多播器中。如果不是单例的,那么就从singletonNames这个列表当中移除。
3.发布事件的时候Spring为我们做了什么?
当我们使用Spring容器中的applicationEventPublisher组件发布事件时
applicationEventPublisher.publishEvent(new MessageEvent("mesg"));
ApplicationEventPublisher#publishEvent的主要逻辑如下
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
// Decorate event as an ApplicationEvent if necessary
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) { //如果实现的是ApplicationEvent接口
applicationEvent = (ApplicationEvent) event;
} else { //要是不是实现的ApplicationEvent接口,就将它封装成为一个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 {
/**
* 非懒加载的走到这里
* 第一个方法用来获取事件的多播器,第二个方法multicastEvent则用来发布事件
* @see SimpleApplicationEventMulticaster#multicastEvent(ApplicationEvent, ResolvableType)
* 在这个方法中可以使用异步/同步的方式去invokeListener
* 然后它会去调用
* @see SimpleApplicationEventMulticaster#doInvokeListener(ApplicationListener, ApplicationEvent)
* 它最终走的逻辑是ApplicationListenerMethodAdapter#processEvent,也就是最终在下面的方法中执行
* @see org.springframework.context.event.ApplicationListenerMethodAdapter#processEvent(ApplicationEvent)
*/
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
// 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);
}
}
}
我们可以发现,在publish方法中主要做的事首先是判断我们发布的事件是否实现了ApplicationEvent接口,如果是则转换成ApplicationEvent类型,如果不是则使用PayloadApplicationEvent将发布的事件进行包装一层。
如果是一些懒加载的事件,那么在这里会放到earlyApplicationEvents中,而如果不是懒加载事件,这里会直接获取多播器并且发布事件(multicastEvent)。
Spring容器中为我们创建的默认多播器是SimpleApplicationEventMulticaster(在refresh方法的第8个步骤initApplicationEventMulticaster中创建了这么一个默认的多播器),因此我们这里会走到SimpleApplicationEventMulticaster#multicastEvent方法中。
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
Executor executor = getTaskExecutor(); //获取一个Executor(线程的那个Executor)
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) { //默认情况下走到这里Executor为null,只有指定了异步发布时才会使用Executor进行发布
executor.execute(() -> invokeListener(listener, event));
}
else { //默认情况下是使用直接的方式去进行调用这个监听器的
invokeListener(listener, event);
}
}
}
在这个方法中会判断能否获取到Executor,如果有Executor,则说明需要进行异步调用,我们默认使用的是同步的方式。最终都是执行的invokeListener方法。
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) { //获取异常的处理器
try {
doInvokeListener(listener, event);
}
catch (Throwable err) {
errorHandler.handleError(err);
}
}
else { //真正用来执行监听器的地方
doInvokeListener(listener, event);
}
}
在invokeListener方法中它会判断是否是异常处理器,如果是则会走异常处理器的逻辑,而我们的监听器并不是异常处理器,直接走到doInvokeListener中,而这个方法的主要逻辑是listener.onApplicationEvent(event)
也就是调用的ApplicationListener#onApplicationEvent方法。
3.1 @EventListener注解的方式创建的监听器的逻辑
如果我们使用的是@EventListener注解的方式创建的监听器,那么Spring中的内置组件internalEventListenerProcessor已经将Listener的beanName、beanType、method包装成为了ApplicationListenerMethodAdapter,因此这里调用的也就是ApplicationListenerMethodAdapter#onApplicationEvent方法,而这个方法中的逻辑就是processEvent(event)
。下面是ApplicationListenerMethodAdapter#processEvent方法的逻辑。
public void processEvent(ApplicationEvent event) {
/**
* 解析事件需要用到的参数
*/
Object[] args = resolveArguments(event);
if (shouldHandle(event, args)) {
/**
* 通过调用doInvoke去执行Method#invoke方法(JDK的Method),最终就是在这里被调用的
*/
Object result = doInvoke(args);
/**
* 如果这个方法还有返回值,那么还需要对结果进行处理,一般result为null
*/
if (result != null) {
handleResult(result);
} else {
logger.trace("No result object given - no result to handle");
}
}
}
在这个方法中主要做的就是解析我们的事件的参数,并调用监听器绑定的方法,如果有返回值的话,还需要结果的返回值进行处理。
3.2 使用实现ApplicationLister接口的方式往容器中添加组件的方式
如果我们使用实现ApplicationLister接口的方式往容器中添加组件的方式去创建监听器,比如我们自定义的如下的监听器组件
@Component
public class TestApplicationListener implements ApplicationListener<TestApplicationEvent> {
@Override
public void onApplicationEvent(TestApplicationEvent event) {
System.out.println("---TestApplicationListener---" + event.getSource());
}
}
在执行listener.onApplicationEvent(event)
这个方法时会走到我们自定义的TestApplicationListener#onApplicationEvent方法中,而不是走ApplicationListenerMethodAdapter这个适配器类的逻辑。
网友评论