定义一个实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserInfo {
private Integer age;
private String name;
}
复制代码
定义一个事件,需要继承ApplicationEvent
@Getter
@Setter
public class CustomEvent extends ApplicationEvent {
private UserInfo userInfo;
public CustomEvent(UserInfo userInfo) {
super(userInfo);
this.userInfo = userInfo;
}
}
复制代码
定义一个监听器,需要实现ApplicationListener
接口
@Component
public class CustomListener implements ApplicationListener {
@Override
public void onApplicationEvent(@NonNull CustomEvent event) {
System.out.println("CustomListener接收到事件~");
System.out.println(JSONObject.toJSONString(event.getUserInfo()));
}
}
复制代码
测试
@SpringBootTest
public class ListenerTest {
@Resource
private ApplicationContext applicationContext;
@Test
public void test() {
UserInfo userInfo = new UserInfo(1, "1");
// 发布事件
applicationContext.publishEvent(new CustomEvent(userInfo));
}
}
复制代码
可见,监听器成功监听并处理了我们发布的事件~
通过上面的案例,我们了解到,发布事件是调用的publishEvent
方法,所以我们直接从publishEvent
开始看~
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
// 如果event是ApplicationEvent实例,则向上转型
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent>) applicationEvent).getResolvableType();
}
}
// 早期发布的事件,此时listener还未注册,所以先将event存起来
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
// 获取事件发布器,并进行发布
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
// 如果父context不为空,则要通过父context再发布一次
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}
复制代码
publishEvent
方法,核心逻辑在于获取时间发布器进行发布,即下面这行代码
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
getApplicationEventMulticaster
只是简单的获取applicationEventMulticaster
,并做一下参数校验,没什么好说的。
@Nullable
private ApplicationEventMulticaster applicationEventMulticaster;
ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException {
if (this.applicationEventMulticaster == null) {
throw new IllegalStateException("ApplicationEventMulticaster not initialized - " +
"call 'refresh' before multicasting events via the context: " + this);
}
return this.applicationEventMulticaster;
}
复制代码
重点在于,applicationEventMulticaster
是怎么初始化的?
熟悉Spring
源码的小伙伴们,对下面这张图肯定不会陌生~
没错,正是Spring
初始化过程中非常重要的refresh
方法 ,在refresh
方法中,会调用initApplicationEventMulticaster
方法初始化事件多播器
initApplicationEventMulticaster
源码如下:
public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
protected void initApplicationEventMulticaster() {
// 获取bean工厂
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
// 检查IOC容器中是否已经有 beanName = applicationEventMulticaster的bean了
// 如果存在则直接使用已有的就行
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 {
// 如果不存在,则new一个SimpleApplicationEventMulticaster,并注册到IOC容器中
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
// 作为单例Bean注册到IOC容器中
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() + "]");
}
}
}
复制代码
initApplicationEventMulticaster
中逻辑很简单
IOC
容器中是否已经有beanName = applicationEventMulticaster
的bean了,则直接使用new
一个SimpleApplicationEventMulticaster
作为事件多播器
,并注册到IOC
容器中.这里比较重点,我们可以自定义
applicationEventMulticaster
,这样事件发布就会走我们自定义的发布器了关于这里,文末会给出案例
ok,了解到事件多播器的获取初始化逻辑之后,我们接着继续来看事件的发布
// 获取事件发布器,并进行事件发布
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
复制代码
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
// 解析event的类型
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
// 获取线程池
Executor executor = getTaskExecutor();
// 通过event、eventType匹配到符合的监听器
for (ApplicationListener> listener : getApplicationListeners(event, type)) {
// 如果线程池不为null,则通过线程池提交任务异步发布
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
// 否则,同步发布
invokeListener(listener, event);
}
}
}
复制代码
multicastEvent
的逻辑也很简单,我们简单总结一下
event
和eventType
匹配到所有符合的事件监听器进行发布同步
划重点了!
Spring的ApplicationListener
观察者模式,默认是同步的!!!
还记得上面我们分析的,事件多播器初始化逻辑吗?
默认是new SimpleApplicationEventMulticaster()
,并不会设置taskExecutor
文末会给出异步发布模式,这里就不过多介绍了~
final Map retrieverCache = new ConcurrentHashMap<>(64);
protected Collection> getApplicationListeners(
ApplicationEvent event, ResolvableType eventType) {
// 获取event Class
Object source = event.getSource();
Class> sourceType = (source != null ? source.getClass() : null);
// 将事件class和类型封装在ListenerCacheKey中,并将其作为缓存的key
// 并且重写了equals方法,会去比较event的Class和类型
ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
CachedListenerRetriever newRetriever = null;
// 从缓存中获取,如果没有则新建CachedListenerRetriever
// CachedListenerRetriever里会存放着符合该event的所有监听器
CachedListenerRetriever existingRetriever = this.retrieverCache.get(cacheKey);
if (existingRetriever == null) {
if (this.beanClassLoader == null ||
(ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
newRetriever = new CachedListenerRetriever();
// putIfAbsent 如果缓存中不存在对应key才会进行put
// 会返回老的value值,所以第一次会返回null
existingRetriever = this.retrieverCache.putIfAbsent(cacheKey, newRetriever);
if (existingRetriever != null) {
newRetriever = null; // no need to populate it in retrieveApplicationListeners
}
}
}
if (existingRetriever != null) {
// 如果缓存中已经存在,则直接取出该事件对应的监听器返回
Collection> result = existingRetriever.getApplicationListeners();
if (result != null) {
return result;
}
}
// 解析event对应的监听器
return retrieveApplicationListeners(eventType, sourceType, newRetriever);
}
复制代码
上面的源码还没有涉及到匹配逻辑,只是做的一层缓存(ConcurrentHashMap
)优化~
首先,会将event
的Class
和Type
封装在ListenerCacheKey
中,作为缓存的key
ListenerCacheKey
中也重写了equalse
方法,会比较eventType
和sourceType
private final ResolvableType eventType;
@Nullable
private final Class> sourceType;
public ListenerCacheKey(ResolvableType eventType, @Nullable Class> sourceType) {
Assert.notNull(eventType, "Event type must not be null");
this.eventType = eventType;
this.sourceType = sourceType;
}
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (!(other instanceof ListenerCacheKey)) {
return false;
}
ListenerCacheKey otherKey = (ListenerCacheKey) other;
return (this.eventType.equals(otherKey.eventType) &&
ObjectUtils.nullSafeEquals(this.sourceType, otherKey.sourceType));
}
复制代码
其次就比较简单了,如果缓存中有就用缓存的,缓存中没有就新建一个CachedListenerRetriever
,并放入缓存里
这里有个小细节,往缓存里添加数据用的是putIfAbsent
方法,也就是key
不存在才会添加数据,并且返回的是oldValue
也就是说,当事件第一次发布时,缓存里没有,putIfAbsent
会返回null
,即existingRetriever = null
则,下面的if
判断,第一次是false
,还是会走retrieveApplicationListeners
existingRetriever = this.retrieverCache.putIfAbsent(cacheKey, newRetriever);
if (existingRetriever != null) {
// 如果缓存中已经存在,则直接取出该事件对应的监听器返回
Collection> result = existingRetriever.getApplicationListeners();
if (result != null) {
return result;
}
}
// 解析event对应的监听器
return retrieveApplicationListeners(eventType, sourceType, newRetriever);
复制代码
private final DefaultListenerRetriever defaultRetriever = new DefaultListenerRetriever();
private Collection> retrieveApplicationListeners(
ResolvableType eventType, @Nullable Class> sourceType, @Nullable CachedListenerRetriever retriever) {
// 存放event对应的所有监听器实例
List> allListeners = new ArrayList<>();
// retriever != null说明是第一次event匹配,需要解析
Set> filteredListeners = (retriever != null ? new LinkedHashSet<>() : null);
// 存放event对应的监听器beanName
Set filteredListenerBeans = (retriever != null ? new LinkedHashSet<>() : null);
Set> listeners;
Set listenerBeans;
// defaultRetriever存放了所有的监听器实例及beanName
synchronized (this.defaultRetriever) {
listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
}
// 遍历所有监听器实例,检索出event对应的监听器实例
for (ApplicationListener> listener : listeners) {
// 是否能作为当前event的监听器
if (supportsEvent(listener, eventType, sourceType)) {
if (retriever != null) {
filteredListeners.add(listener);
}
allListeners.add(listener);
}
}
// 遍历所有监听器beanName,检索出event对应的监听器beanName
if (!listenerBeans.isEmpty()) {
// 获取bean工厂
ConfigurableBeanFactory beanFactory = getBeanFactory();
for (String listenerBeanName : listenerBeans) {
try {
// 是否能作为当前event的监听器
if (supportsEvent(beanFactory, listenerBeanName, eventType)) {
// 获取监听器bean
ApplicationListener> listener =
beanFactory.getBean(listenerBeanName, ApplicationListener.class);
// 如果listener不在改event对应的监听器集合中,但又符合该event
if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
if (retriever != null) {
// 这块if-else没太懂,有没有大佬指点!!!
if (beanFactory.isSingleton(listenerBeanName)) {
filteredListeners.add(listener);
}
else {
filteredListenerBeans.add(listenerBeanName);
}
}
allListeners.add(listener);
}
}
else {
Object listener = beanFactory.getSingleton(listenerBeanName);
if (retriever != null) {
filteredListeners.remove(listener);
}
allListeners.remove(listener);
}
}
catch (NoSuchBeanDefinitionException ex) {
}
}
}
// 排序,根据@Order注解,或者Ordered接口定义的优先级排序,值越小优先级越大
AnnotationAwareOrderComparator.sort(allListeners);
if (retriever != null) {
// 将event对应的监听器缓存起来
if (filteredListenerBeans.isEmpty()) {
retriever.applicationListeners = new LinkedHashSet<>(allListeners);
retriever.applicationListenerBeans = filteredListenerBeans;
}
else {
retriever.applicationListeners = filteredListeners;
retriever.applicationListenerBeans = filteredListenerBeans;
}
}
return allListeners;
}
复制代码
上面源码一句话可以概括,获取到所有listener
遍历,根据eventType, sourceType
匹配到符合当前event
的监听器
但其中会有些小细节
defaultRetriever
存放了所有的监听器实例及beanName
retriever != null
说明event
是第一次来解析对应的监听器获取event
对应监听器分析完了,接下来就回过头来分析通知监听器的源码了~
protected void invokeListener(ApplicationListener> listener, ApplicationEvent event) {
// 获取error的处理器
ErrorHandler errorHandler = getErrorHandler();
// 如果存在errorHandler,则对doInvokeListener进行try-catch
if (errorHandler != null) {
try {
// 通知监听器
doInvokeListener(listener, event);
}
catch (Throwable err) {
errorHandler.handleError(err);
}
}
else {
doInvokeListener(listener, event);
}
}
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
// 调用对应监听器实现的onApplicationEvent方法
listener.onApplicationEvent(event);
}
catch (ClassCastException ex) {
String msg = ex.getMessage();
if (msg == null || matchesClassCastMessage(msg, event.getClass()) ||
(event instanceof PayloadApplicationEvent &&
matchesClassCastMessage(msg, ((PayloadApplicationEvent) event).getPayload().getClass()))) {
Log loggerToUse = this.lazyLogger;
if (loggerToUse == null) {
loggerToUse = LogFactory.getLog(getClass());
this.lazyLogger = loggerToUse;
}
if (loggerToUse.isTraceEnabled()) {
loggerToUse.trace("Non-matching event type for listener: " + listener, ex);
}
}
else {
throw ex;
}
}
}
复制代码
invokeListener
的内容也比较简单,就是调用对应监听器实现onApplicationEvent
方法,实现通知效果~
在上面探究applicationEventMulticaster
初始化源码时,我提到了,Spring
的观察者模式默认是同步的,如下图所示
@Component
public class CustomListener implements ApplicationListener {
@Override
public void onApplicationEvent(@NonNull CustomEvent event) {
System.out.println(Thread.currentThread().getName() + " CustomListener接收到事件~");
System.out.println(JSONObject.toJSONString(event.getUserInfo()));
}
}
复制代码
但是其底层也是支持通过线程池进行异步通知
那么该如何做呢?
在上面的applicationEventMulticaster
初始化源码中,我们也能看到,如果容器中有beanName = applicationEventMulticaster
的bean,就会将其作为事件多播器
,所以我们可以自定义一个applicationEventMulticaster
@Configuration
public class SpringConfig {
@Bean(name = "applicationEventMulticaster")
public ApplicationEventMulticaster applicationEventMulticaster() {
SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster();
simpleApplicationEventMulticaster.setTaskExecutor(Executors.newFixedThreadPool(10, new DefaultThreadFactory("custom")));
return simpleApplicationEventMulticaster;
}
}
复制代码
此时再次测试事件发布,可以看到打印的当前线程不是main
线程了,而是使用我们自定义的线程池中的线程
如果,我们项目中定义了许多的监听器,那么为了避免在处理事件的时候出现异常,我们难免都会去进行**try-catch
,会写出很多冗余代码。**
那么怎么解决呢?
其实,在上面分析invokeListener
源码的时候我们也能直接看到,可以支持统一的ErrorHandler
,而且对代码没有侵入性~
protected void invokeListener(ApplicationListener> listener, ApplicationEvent event) {
// 获取error的处理器
ErrorHandler errorHandler = getErrorHandler();
// 如果存在errorHandler,则对doInvokeListener进行try-catch
if (errorHandler != null) {
try {
// 通知监听器
doInvokeListener(listener, event);
}
catch (Throwable err) {
errorHandler.handleError(err);
}
}
else {
doInvokeListener(listener, event);
}
}
复制代码
自定义ErrorHandler
@Slf4j
public class CustomErrorHandler implements ErrorHandler {
@Override
public void handleError(@NonNull Throwable throwable) {
System.out.println("自定义error处理");
log.error("CustomErrorHandler", throwable);
}
}
复制代码
@Bean(name = "applicationEventMulticaster")
public ApplicationEventMulticaster applicationEventMulticaster() {
SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster();
simpleApplicationEventMulticaster.setTaskExecutor(Executors.newFixedThreadPool(10, new DefaultThreadFactory("custom")));
// 设置自定义errorHandler
simpleApplicationEventMulticaster.setErrorHandler(new CustomErrorHandler());
return simpleApplicationEventMulticaster;
}
复制代码
@Component
public class CustomListener implements ApplicationListener {
@Override
public void onApplicationEvent(@NonNull CustomEvent event) {
// 手动模拟异常
int i = 1 / 0;
System.out.println(Thread.currentThread().getName() + " CustomListener接收到事件~");
System.out.println(JSONObject.toJSONString(event.getUserInfo()));
}
}
复制代码
测试
在refesh
方法中,会调用registerListeners
注册监听器,在上面的retrieveApplicationListeners
源码分析中,我们也说到了,会从defaultRetriever
中拿到所有listener
而此时的registerListeners
就是将listener
缓存到defaultRetriever
此外,还有publishEvent
中提到的earlyApplicationEvents
listener
还未缓存,就开始调用publishEvent
了,这时先将event
缓存到earlyEventsToProcess
中,待缓存完毕后进行事件发布~
protected void registerListeners() {
// 缓存listener实例
for (ApplicationListener> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
// 缓存listener beanName
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String listenerBeanName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
// 发布早期事件(listener还未缓存,就开始调用publishEvent了,这时先缓存到earlyEventsToProcess中)
Set earlyEventsToProcess = this.earlyApplicationEvents;
// 发布早期事件,并将this.earlyApplicationEvents = null;
this.earlyApplicationEvents = null;
if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}
@Override
public void addApplicationListener(ApplicationListener> listener) {
synchronized (this.defaultRetriever) {
Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
if (singletonTarget instanceof ApplicationListener) {
this.defaultRetriever.applicationListeners.remove(singletonTarget);
}
this.defaultRetriever.applicationListeners.add(listener);
this.retrieverCache.clear();
}
}
@Override
public void addApplicationListenerBean(String listenerBeanName) {
synchronized (this.defaultRetriever) {
this.defaultRetriever.applicationListenerBeans.add(listenerBeanName);
this.retrieverCache.clear();
}
}
复制代码
上面registerListeners
会调用getApplicationListeners
获取所有listener
,哪这里是怎么获取的呢?
答案就是: Spring
中存在ApplicationListenerDetector
实现了BeanPostProcessor
,如果bean instanceof ApplicationListener
,且是单例bean,则会添加到AbstractApplicationContext类的applicationListeners
这个集合中
getApplicationListeners
也是直接获取的applicationListeners
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);
if (Boolean.TRUE.equals(flag)) {
// singleton bean (top-level or inner): register on the fly
this.applicationContext.addApplicationListener((ApplicationListener>) bean);
}
else if (Boolean.FALSE.equals(flag)) {
if (logger.isWarnEnabled() && !this.applicationContext.containsBean(beanName)) {
// inner bean with other scope - can't reliably process events
logger.warn("Inner bean '" + beanName + "' implements ApplicationListener interface " +
"but is not reachable for event multicasting by its containing ApplicationContext " +
"because it does not have singleton scope. Only top-level listener beans are allowed " +
"to be of non-singleton scope.");
}
this.singletonNames.remove(beanName);
}
}
return bean;
}