有同学提出让老师多讲一点Spring容器中的事件机制。

spring 事件事件监听 spring事件异步监听_spring


主要的代码就在上图的1、2、3当中了。如何使用以及观察者模式,老师都有谈到。但是有一个同学提出如何实现一个异步监听的时候,老师找到如下事件发布的源码

@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);
		}
	}
}

老师的结论为:现在executor属性为空,所以需要设置一个executor就可以执行异步监听了。事实是不是真的这样呢?

那么怎么设置这个executor呢,老师说了几种,我不认为有啥用,方法有很多种,我这里采用一种简单的方式。
因为在容器刷新的时候,会执行如下代码

/**
 * Name of the ApplicationEventMulticaster bean in the factory.
 * If none is supplied, a default SimpleApplicationEventMulticaster is used.
 * @see org.springframework.context.event.ApplicationEventMulticaster
 * @see org.springframework.context.event.SimpleApplicationEventMulticaster
 */
public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
/**
 * Initialize the ApplicationEventMulticaster.
 * Uses SimpleApplicationEventMulticaster if none defined in the context.
 * @see org.springframework.context.event.SimpleApplicationEventMulticaster
 */
protected void initApplicationEventMulticaster() {
	ConfigurableListableBeanFactory beanFactory = getBeanFactory();
	if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
		this.applicationEventMulticaster =
				beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
		if (logger.isTraceEnabled()) {
			logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
		}
	}
	else {
		this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
		beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
		if (logger.isTraceEnabled()) {
			logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
					"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
		}
	}
}

如果已经存在名称为applicationEventMulticaster、类型为ApplicationEventMulticaster的Bean,Spring就不会创建和注册默认的了。所以我们自己定义一个这样的bean,同时设置好executor属性即可,这里使用Spring中已有的SimpleAsyncTaskExecutor。

@Bean
public ApplicationEventMulticaster applicationEventMulticaster(){
    SimpleApplicationEventMulticaster applicationEventPublisher = new SimpleApplicationEventMulticaster();
    applicationEventPublisher.setTaskExecutor(new SimpleAsyncTaskExecutor());
    return applicationEventPublisher;
}

接下来创建一个事件和监听器,专门用于接收异步事件。

package org.springwork.demo.event;

import org.springframework.context.ApplicationEvent;


public class AsyncEvent extends ApplicationEvent {

    public AsyncEvent(Object source) {
        super(source);
    }
}
package org.springwork.demo.event;

import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class AsyncEventListener implements ApplicationListener<AsyncEvent> {
    @Override
    public void onApplicationEvent(AsyncEvent event) {
        System.out.println("AsyncEvent - " + Thread.currentThread().getName());
    }
}

然后在容器启动之后发布一个事件

spring 事件事件监听 spring事件异步监听_java_02


从结果可以看出,此时确实实现了异步监听。是不是很完美?等等~!哪里好像有问题!如果要在项目中要使用同步事件呢?在一个真实项目当中,不可能所有事件都是异步的吧?下面创建一个同步事件和同步事件监听器。其实与上面并没有差别,只是目标是同步监听而已、

package org.springwork.demo.event;

import org.springframework.context.ApplicationEvent;

public class SynEvent 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 SynEvent(Object source) {
        super(source);
    }
}
package org.springwork.demo.event;

import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class SynEventListener implements ApplicationListener<SynEvent> {
    @Override
    public void onApplicationEvent(SynEvent event) {
        System.out.println("SynEvent - " + Thread.currentThread().getName());
    }
}

结果如下

spring 事件事件监听 spring事件异步监听_spring 事件事件监听_03


这下是不是悲催了,所有事件都是异步监听的,没法区分同步事件监听和异步事件监听了!

所以老师说的异步监听实现完全是片面的,在实际项目中不可能有用的!那么怎么样实现异步监听呢?还是得看官网文档

spring 事件事件监听 spring事件异步监听_spring 事件事件监听_04


左边目录按照分为四个部分,分别从注解模式、异步事件、事件排序和泛型事件四个方面详细介绍了事件机制的使用方法。这里只关注其中的注解模式和异步事件,其他参考文档即可。

从4.2开始,事件机制开始支持注解模式了(@EventListener)。如下所示

@EventListener
public void processBlockedListEvent(BlockedListEvent event) {
    // notify appropriate parties via notificationAddress...
}

在监听方法上添加@EventListener注解,方法参数对应为接收事件的类型。
而要实现异步事件非常简单,添加一个@Async注解即可。

@EventListener
@Async
public void processBlockedListEvent(BlockedListEvent event) {
    // BlockedListEvent is processed in a separate thread
}

使用异步事件有一些局限,比如异常处理、返回值问题。具体参考官方文档。

回到我们前面的案例当中,配置如下所示:

@Async
@EventListener
public void onSimpleEvent(AsyncEvent asyncEvent) {
    System.out.println("AsyncEvent - " + Thread.currentThread().getName());
}

@EventListener
public void onSyncEvent(SynEvent synEvent) {
    System.out.println("synEvent - " + Thread.currentThread().getName());
}

在学习一个新的知识点的时候,必须学会看文档、类的注释、方法注释,比如上面的@Async的作用也不仅仅是异步事件监听。以下为这个注解上面的注释,非常详细。

spring 事件事件监听 spring事件异步监听_Async_05


但是单纯这么配置,还是不够的,还需要在配置类加一个@EnableAsync开启异步模式。加上了这个注解还不行,还需要添加一些辅助的Bean,这些在注释当中相当详细。

spring 事件事件监听 spring事件异步监听_事件监听_06


参考官方文档相关章节将会收获更多。Spring将异步执行和任务调度都进行了抽象。比如Spring在JDK的基础上,将资源从URL抽象为Resource,Spring之所以成为Java的事实标准,就是它扩展了JDK,进行了更好的抽象。有点扯远了。其实这里只是想说明一下以上两个注解可不仅仅是为了异步事件监听的,只不过恰好异步事件监听也属于这个抽象的范围而已。

最终配置如下(记得删除原来的事件发布器中的taskExecutor属性或者直接使用Spring默认的事件发布器,上面已经分析了,通过在事件发布器中添加taskExecutor属性是将所有事件都按照异步执行的,在实际项目中根本不可用)

@Bean
public Executor asyncExecutor() {
    return new SimpleAsyncTaskExecutor();
}

@Bean
public AsyncUncaughtExceptionHandler asyncUncaughtExceptionHandler() {
    return (ex, method, params) -> System.out.println(ex.getMessage() + " happens on " + method + "," + ex);
}

最后的执行效果如下

spring 事件事件监听 spring事件异步监听_事件监听_07


这样实现了同时存在同步监听和异步监听了。至于其中的原理,看下 EnableAsync注解就明白了。

spring 事件事件监听 spring事件异步监听_spring 事件事件监听_08


引入一个ProxyAsyncConfiguration配置类

spring 事件事件监听 spring事件异步监听_Async_09


然后引入一个后置处理器

spring 事件事件监听 spring事件异步监听_Async_10


这个后置处理器会在遍历Bean的时候,根据是否有@Async注解为Bean生成代理(也就是所谓的AOP),生成对应的代理,其实就是针对原来的方法进行了一层包装。关于AOP可以参考官方文档,介绍的非常详细,其中关于编译期增强、运行时增强,以及静态增强、动态增强等知识相当关键。理解AOP绝不是一两个接口的问题。只有知其然才能所以然。此处不会详细探讨AOP了。继续上面的内容,通过后置处理器为合适的类生成代理。其中最关键的就是AsyncAnnotationAdvisor这个增强器。

spring 事件事件监听 spring事件异步监听_事件监听_11


而每个Advisor都是由切面pointcut和拦截器或者Advice组成的,前者进行过滤,只有合适的类或者方法才需要增强(如果要针对属性进行增强,可以考虑隐介增强),其中拦截器或Advice就是具体的增强逻辑了、在这里AsyncAnnotationAdvisor = AnnotationAsyncExecutionInterceptor+AnnotationMatchingPointcut

spring 事件事件监听 spring事件异步监听_spring 事件事件监听_12


最后增强逻辑如下

spring 事件事件监听 spring事件异步监听_spring 事件事件监听_13


好啦!以上就是实现异步监听的方法和原理。