SpringBoot源码学习——SpringBoot中的事件监听

1.简介

SpringBoot是建立在Spring基础之上,沿用了Spring的核心容器代码,是对Spring容器的一次封装和改造。因为加入了自动装配已经内嵌web容器等特性。所以在Spring容器的基础上,作为SpringBoot应用上下文的SpringApplication增加了一套事件监听并关联了Spring容器中原有的事件监听系统

2.SpringApplicationRunListeners

该类是对SpringBoot上下文中的SpringApplicationRunListener集合包装对象,每当某个阶段完成,SpringApplication会通过调用SpringApplicationRunListeners来向SpringApplicationRunListener广播事件。当然,该类不直接在SpringBoot应用上下文中,而是每次使用都通过SpringApplicationRunListeners listeners = getRunListeners(args);的形式获取

class SpringApplicationRunListeners {
    private final Log log;
    private final List<SpringApplicationRunListener> listeners;

    SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
        this.log = log;
        this.listeners = new ArrayList(listeners);
    }

    void starting() {
        Iterator var1 = this.listeners.iterator();

        while(var1.hasNext()) {
            SpringApplicationRunListener listener = (SpringApplicationRunListener)var1.next();
            listener.starting();
        }

    }
    ...

SpringApplicationRunListeners能做什么

通过上面的SpringApplicationRunListeners源码,我们可以发现,每当某个阶段完成,SpringApplication会通过调用SpringApplicationRunListeners来向SpringApplicationRunListener广播事件

SpringApplicationRunListeners哪里来的

private SpringApplicationRunListeners getRunListeners(String[] args) {
        Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
        return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
    }

在getRunListeners,先不谈其他元素,可以直接看到SpringApplicationRunListeners就是在getRunListeners中被创建的
我们可以看到SpringApplicationRunListeners组合了SpringApplicationRunListener以及将SpringApplicationRunListener作为构造参数。接下来了解SpringApplicationRunListener


SpringApplicationRunListener

什么是SpringApplicationRunListener

该类从名字上看就是一个监听器,我们可以认为它是SpringBoot应用的运行时监听器
一般的观察者模式(发布/订阅模式)中,有一个事件监听者,也会有事件发布者,同时还有一个事件对象。在这里,我们可以认为SpringApplicationRunListeners充当了事件发布者的角色,而我们的SpringApplicationRunListener就是订阅者.不过这里并没有显式的事件对象,因为在这里,事件的种类以及监听器的种类都是有限的,固定的,没有必要为了泛化性来实现通用繁琐的代码。

public interface SpringApplicationRunListener {
    default void starting() {
    }

    default void environmentPrepared(ConfigurableEnvironment environment) {
    }

    default void contextPrepared(ConfigurableApplicationContext context) {
    }

    default void contextLoaded(ConfigurableApplicationContext context) {
    }

    default void started(ConfigurableApplicationContext context) {
    }

    default void running(ConfigurableApplicationContext context) {
    }

    default void failed(ConfigurableApplicationContext context, Throwable exception) {
    }
}

SpringApplicationRunListener能做什么

从上面的部分的源码我们可以看出,SpringApplicationRunListener具有八个监听方法,会在SpringApplicaton的8个不同阶段被通过SpringApplicationRunListeners调用。
但我们现在只知道SpringApplicationRunListener是想SpringApplicationRunListener广播事件,但是SpringApplicationRunListeners是如何将SpringApplicationRunListener关联起来的,并且在SpringApplicationRunListener监听方法中到底做了什么还不知道

SpringApplicationRunListener怎么来的

在getRunListeners方法中创建SpringApplicationRunListener时我们在构造参数中使用了getSpringFactoriesInstances方法,前面提到过这个方法的作用。也就是说SpringApplicationRunListener来自于META-INF/spring.factories资源文件,同时前面提到监听器种类有限,就是因为内置的实现就只有一个EventPublishingRunListener

private SpringApplicationRunListeners getRunListeners(String[] args) {
        Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
        return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
    }
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

SpringApplicationRunListener接口对应的监听事件

监听方法SpringBoot事件说明
starting()ApplicationStartingEventSpringBoot上下文启动事件
environmentPrepared(ConfigurableEnvironment)ApplciaitonEnvironmentPreparedEvent上下文环境准备完毕事件
contextPrepared(ConfigurableApplicationContext)
contextLoaded(ConfigurableApplicationContext)ApplicationPreparedEvent容器加载完成事件
started(ConfigurableApplicationContext)ApplicationStartedEventSpringBoot上下文启动完毕事件
running(ConfigurableApplicationContext)ApplicationReadyEventSpringBoot上下文准备运行事件
failed(ConfigurableApplicationContext, Throwable)SpringBoot上下文运行失败完毕事件

EventPublishingRunListener

什么是EventPublishingRunListener

这个类是SpringBoot中关于SpringApplicationRunListener的唯一内建实现。
这个类负责对SpringApplication发布的事件进行监听

这里取一部分代码,其他的会在后面进行说明

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
    private final SpringApplication application;
    private final String[] args;
    private final SimpleApplicationEventMulticaster initialMulticaster;

    public EventPublishingRunListener(SpringApplication application, String[] args) {
        this.application = application;
        this.args = args;
        this.initialMulticaster = new SimpleApplicationEventMulticaster();
        Iterator var3 = application.getListeners().iterator();

        while(var3.hasNext()) {
            ApplicationListener<?> listener = (ApplicationListener)var3.next();
            this.initialMulticaster.addApplicationListener(listener);
        }

    }

    public int getOrder() {
        return 0;
    }

    public void starting() {
        this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
    }
    ...

EventPublishingRunListener能做什么

从源码中我们可以看到这个监听器中所有的操作都是与它关联的SimpleApplicationEventMulticaster类有关,这个类一看就是个事件发布器,再看我们starting方法,也很明显看出,这个EventPublishingRunListener其实就是在监听触发时再调用SimpleApplicationEventMulticaster去向其他类型的监听器发布事件

同时,再构造方法中我们可以看到在构造EventPublishingRunListener时直接创建了它依赖的SimpleApplicationEventMulticaster,并直接调用它的addApplicationListener方法将SpringApplication中缓存的所有ApplicationListener监听器添加到SimpleApplicationEventMulticaster实例中
该构造方法说明了: SimpleApplicationEventMulticaster一开始的监听器来自于ApplicationListener,也就是来自于Spring.factories中配置的SpringBoot内置监听器(关于为什么是来自于Spring.factories,可以直接看SpringApplcaition的构造函数或是看我关于SpringApplcation生命周期一章)

EventPublishingRunListener怎么来的

上面作为SpringApplicationRunListeners构造参数时,被getSpringFactoriesInstances方法从spring.factories中取出来创建出来的

SpringApplication类

public class SpringApplication {
	...
	private SpringApplicationRunListeners getRunListeners(String[] args) {
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
		return new SpringApplicationRunListeners(logger,
				getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
	}
	...
}

spring.factories

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

EventPublishingRunListener的监听实现

下面是在EventPublishingRunListener中对SpringApplicationRunListener的API实现

监听方法说明
starting()通过SimpleApplicationEventMulticaster发布ApplicationStartingEvent事件
environmentPrepared(ConfigurableEnvironment)通过SimpleApplicationEventMulticaster发布ApplicationEnvironmentPreparedEvent事件
contextPrepared(ConfigurableApplicationContext)通过SimpleApplicationEventMulticaster发布ApplicationEnvironmentPreparedEvent事件
contextLoaded(ConfigurableApplicationContext)1.为SpringApplication中所有实现ApplicationContextAware接口的ApplicationListener注入容器实例
2.将SpringApplication中所有实现ApplicationListener添加到容器监听器组中
3.通过SimpleApplicationEventMulticaster发布ApplicationPreparedEvent事件
started(ConfigurableApplicationContext)通过容器发布ApplicationStartedEvent事件
running(ConfigurableApplicationContext)如果容器还在运行,通过容器发布ApplicationStartingEvent事件,如果没有则通过SimpleApplicationEventMulticaster发布

可以很明显的看出在contextLoaded执行之后,也就是容器加载完毕后,所有的事件发布都从SimpleApplicationEventMulticaster转嫁到了容器本身,也就是说,容器自身就是一个事件发布器

事实上,我们通过下面源码看到,通过容器发布事件时使用了publishEvent方法,该方法继承自容器的一个差不多顶级的父类接口 ApplicationEventPublisher,所以说,容器本事就是一个事件发布器。在下面源码后附带一张ApplicationEventPublisher的继承实现图

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {

	private final SpringApplication application;

	private final String[] args;

	private final SimpleApplicationEventMulticaster initialMulticaster;

	...

	//通过SimpleApplicationEventMulticaster发布ApplicationStartingEvent事件
	@Override
	public void starting() {
		this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
	}

	//通过SimpleApplicationEventMulticaster发布ApplicationEnvironmentPreparedEvent事件
	public void environmentPrepared(ConfigurableEnvironment environment) {
		this.initialMulticaster
				.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
	}

	//通过SimpleApplicationEventMulticaster发布ApplicationContextInitializedEvent事件
	public void contextPrepared(ConfigurableApplicationContext context) {
		this.initialMulticaster
				.multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
	}

	//1.为SpringApplication中所有实现ApplicationContextAware接口的ApplicationListener注入容器实例
	//2.将SpringApplication中所有实现ApplicationListener添加到容器监听器组中
	//通过SimpleApplicationEventMulticaster发布ApplicationPreparedEvent事件
	public void contextLoaded(ConfigurableApplicationContext context) {
		for (ApplicationListener<?> listener : this.application.getListeners()) {
			if (listener instanceof ApplicationContextAware) {
				((ApplicationContextAware) listener).setApplicationContext(context);
			}
			context.addApplicationListener(listener);
		}
		this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));
	}

	//通过容器发布ApplicationStartedEvent事件
	public void started(ConfigurableApplicationContext context) {
		context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
		AvailabilityChangeEvent.publish(context, LivenessState.CORRECT);
	}

	//通过容器发布ApplicationReadyEvent事件
	public void running(ConfigurableApplicationContext context) {
		context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
		AvailabilityChangeEvent.publish(context, ReadinessState.ACCEPTING_TRAFFIC);
	}

	//如果容器还在运行,通过容器发布ApplicationStartingEvent事件,如果没有则通过SimpleApplicationEventMulticaster发布
	public void failed(ConfigurableApplicationContext context, Throwable exception) {
		ApplicationFailedEvent event = new ApplicationFailedEvent(this.application, this.args, context, exception);
		if (context != null && context.isActive()) {
			// Listeners have been registered to the application context so we should
			// use it at this point if we can
			context.publishEvent(event);
		}
		else {
			// An inactive context may not have a multicaster so we use our multicaster to
			// call all of the context's listeners instead
			if (context instanceof AbstractApplicationContext) {
				for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
						.getApplicationListeners()) {
					this.initialMulticaster.addApplicationListener(listener);
				}
			}
			this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
			this.initialMulticaster.multicastEvent(event);
		}
	}

}

ApplicationEventPublisher的继承实现图
ApplicationEventPublisher的继承实现图

SimpleApplicationEventMulticaster

前面我们提到EventPublishingRunListener中关联了一个SimpleApplicationEventMulticaster类。并在构造方法中直接实例化。而且在EventPublishingRunListener的监听事件中,基本都是对SimpleApplicationEventMulticaster对象的操作

SimpleApplicationEventMulticaster是什么

SimpleApplicationEventMulticaster是一个事件发布器

从下面这个类图中可以看出SimpleApplicationEventMulticaster实现了ApplicationEventMulticaster

在这里插入图片描述

SimpleApplicationEventMulticaster有什么用

ApplicationEventMulticaster是ApventplicationEvent的事件发布器,它有两个职责:

  1. 关联ApplicationListener
  2. 广播ApplicationEvent

我们通过其父类ApplicationEventMulticaster的API就可以看出来了

public interface ApplicationEventMulticaster {
    void addApplicationListener(ApplicationListener<?> var1);

    void addApplicationListenerBean(String var1);

    void removeApplicationListener(ApplicationListener<?> var1);

    void removeApplicationListenerBean(String var1);

    void removeAllListeners();

    void multicastEvent(ApplicationEvent var1);

    void multicastEvent(ApplicationEvent var1, @Nullable ResolvableType var2);
}

SimpleApplicationEventMulticaster从何而来

这个类被直接关联在EventPublishingRunListener中,由EventPublishingRunListener构造参数创建实例,也就是说,当EventPublishingRunListener被创建时,也就是SpringApplication调用getRunListners方法就也被创建了。可以参考下面EventPublishingRunListener的唯一构造函数

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
	....

	private final SimpleApplicationEventMulticaster initialMulticaster;

	public EventPublishingRunListener(SpringApplication application, String[] args) {
		this.application = application;
		this.args = args;
		this.initialMulticaster = new SimpleApplicationEventMulticaster();
		for (ApplicationListener<?> listener : application.getListeners()) {
			this.initialMulticaster.addApplicationListener(listener);
		}
	}

ApplicationEventMulticaster是如何注册ApplicationListener

从下面源码中可以看到两个addApplicationListener*方法,分别是向AbstractApplicationEventMulticaster中直接增加ApplicationListener对象或者是通过BeanName的方式增加。

public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware {
    private final AbstractApplicationEventMulticaster.ListenerRetriever defaultRetriever = new AbstractApplicationEventMulticaster.ListenerRetriever(false);
    final Map<AbstractApplicationEventMulticaster.ListenerCacheKey, AbstractApplicationEventMulticaster.ListenerRetriever> retrieverCache = new ConcurrentHashMap(64);
		...
    public void addApplicationListener(ApplicationListener<?> listener) {
        synchronized(this.retrievalMutex) {
            Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
            if (singletonTarget instanceof ApplicationListener) {
                this.defaultRetriever.applicationListeners.remove(singletonTarget);
            }

            this.defaultRetriever.applicationListeners.add(listener);
            this.retrieverCache.clear();
        }
    }

    
    ...

ApplicationListener何时被注册到ApplicationEventMulticaster中

所以这两个方法在哪里被调用。
1.EventPublishingRunListener被构造时。将SpringApplication关联的ApplicationListener集合注册到ApplicationEventMulticaster中

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
	...
	public EventPublishingRunListener(SpringApplication application, String[] args) {
		...
		this.initialMulticaster = new SimpleApplicationEventMulticaster();
		for (ApplicationListener<?> listener : application.getListeners()) {
			this.initialMulticaster.addApplicationListener(listener);
		}
	}
	...
}

2.AbstractApplicationContext#refresh()中在实例化所以单例Bean前会调用一个registerListeners(),来将容器中直接关联的ApplicationListener实例和容器中注册的ApplicationListener的BeanName注册到ApplicationEventMulticaster中

protected void registerListeners() {
		//获取容器中直接关联的ApplicationListeners(非Bean)
        Iterator var1 = this.getApplicationListeners().iterator();
		//将容器中直接关联的ApplicationListeners添加到它直接关联的事件发布器上
        while(var1.hasNext()) {
            ApplicationListener<?> listener = (ApplicationListener)var1.next();
            this.getApplicationEventMulticaster().addApplicationListener(listener);
        }
		//获取容器中注册的ApplicationListener类型的BeanName集合
        String[] listenerBeanNames = this.getBeanNamesForType(ApplicationListener.class, true, false);
        String[] var7 = listenerBeanNames;
        int var3 = listenerBeanNames.length;

        for(int var4 = 0; var4 < var3; ++var4) {
            String listenerBeanName = var7[var4];
        //将BeanName添加到事件发布器中  this.getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
        }
		//将earlyApplicationEvents中积压的ApplicationEvent发布出去
        Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
        this.earlyApplicationEvents = null;
        if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
            Iterator var9 = earlyEventsToProcess.iterator();

            while(var9.hasNext()) {
                ApplicationEvent earlyEvent = (ApplicationEvent)var9.next();
                this.getApplicationEventMulticaster().multicastEvent(earlyEvent);
            }
        }

    }

注:这里的两个ApplicationEventMulticaster并不在一个类中,前者在SpringApplication中,后者在容器中。后者在被创建后就通过registerListeners函数同步了所有的监听器

ApplicationEventMulticaster是如何关联ApplicationListener

上面是摘自ApplicationEventMulticaster实现抽象类AbstractApplicationEventMulticaster类的一部分,你翻遍ApplicationEventMulticaster的所有子类,会发现ApplicationEventMulticaster并没有直接关联我们的ApplicationListener,而我们两个增加ApplicationListener方法中可以看出我们的ApplicationListener是被关联在了一个ListenerRetriever类中
解释:我们的AbstractApplicationEventMulticaster通过defaultRetriever和retrieverCache关联我们的事件监听器,其中,前者包含了我们所有的ApplicationListener(有直接关联的,也有通过BeanName关联的),而后者则是对前者进行了分类,分类的方式就是通过ListenerCacheKey作为map的键,并通过ListenerRetriever关联一组监听器来实现分类

ListenerCacheKey

通过下面ListenerCacheKey的源码可以看出,该类重写了hashcode与equals方法来把eventType和sourceType作为计算因子,前者是我们的事件类型,而后者使我们的事件源,也就是事件构造器中作为source的对象。换句话说,通过事件类型和事件源类型的组合实现了监听器的分类

private static final class ListenerCacheKey implements Comparable<AbstractApplicationEventMulticaster.ListenerCacheKey> {
        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;
            } else if (!(other instanceof AbstractApplicationEventMulticaster.ListenerCacheKey)) {
                return false;
            } else {
                AbstractApplicationEventMulticaster.ListenerCacheKey otherKey = (AbstractApplicationEventMulticaster.ListenerCacheKey)other;
                return this.eventType.equals(otherKey.eventType) && ObjectUtils.nullSafeEquals(this.sourceType, otherKey.sourceType);
            }
        }

        public int hashCode() {
            return this.eventType.hashCode() * 29 + ObjectUtils.nullSafeHashCode(this.sourceType);
        }
    }

关于 ResolvableType

该类是Spring为简化java反射api而提供的组件,能够轻松获取泛型类型等。
这里只是提一下,相关内容还需要另外写一章节

public class ResolvableType implements Serializable {
    public static final ResolvableType NONE;
    private static final ResolvableType[] EMPTY_TYPES_ARRAY;
    private static final ConcurrentReferenceHashMap<ResolvableType, ResolvableType> cache;
    private final Type type;
    @Nullable
    private final TypeProvider typeProvider;
    @Nullable
    private final ResolvableType.VariableResolver variableResolver;
    @Nullable
    private final ResolvableType componentType;
    @Nullable
    private final Integer hash;
    @Nullable
    private Class<?> resolved;
    @Nullable
    private volatile ResolvableType superType;
    @Nullable
    private volatile ResolvableType[] interfaces;
    @Nullable
    private volatile ResolvableType[] generics;
...

从上面的源码的字段名中我们可以看出来它确实拥有很多 类对象中特有的一些类,只不过这里应该是封装过了,类都不一样,这里不做深究

ListenerRetriever

从applicationListeners字段我们可以看出该类确实关联了一组监听器

private class ListenerRetriever {
        public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet();
        public final Set<String> applicationListenerBeans = new LinkedHashSet();
        ...

ApplicationEventMulticaster如何发布事件

前面我们提到Map<ListenerCacheKey,ListenerRetriever>对监听器做了分类,所以,当我们需要发布事件时,只需要通过事件的类型和数据源类型就能获取到对应的监听器组了,然后再对获取的监听器进行迭代调用。

protected Collection<ApplicationListener<?>> getApplicationListeners() {
        synchronized(this.retrievalMutex) {
            return this.defaultRetriever.getApplicationListeners();
        }
    }

    protected Collection<ApplicationListener<?>> getApplicationListeners(ApplicationEvent event, ResolvableType eventType) {
        Object source = event.getSource();
        Class<?> sourceType = source != null ? source.getClass() : null;
        AbstractApplicationEventMulticaster.ListenerCacheKey cacheKey = new AbstractApplicationEventMulticaster.ListenerCacheKey(eventType, sourceType);
        AbstractApplicationEventMulticaster.ListenerRetriever retriever = (AbstractApplicationEventMulticaster.ListenerRetriever)this.retrieverCache.get(cacheKey);
        if (retriever != null) {
            return retriever.getApplicationListeners();
        } else if (this.beanClassLoader == null || ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) && (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader))) {
            synchronized(this.retrievalMutex) {
                retriever = (AbstractApplicationEventMulticaster.ListenerRetriever)this.retrieverCache.get(cacheKey);
                if (retriever != null) {
                    return retriever.getApplicationListeners();
                } else {
                    retriever = new AbstractApplicationEventMulticaster.ListenerRetriever(true);
                    Collection<ApplicationListener<?>> listeners = this.retrieveApplicationListeners(eventType, sourceType, retriever);
                    this.retrieverCache.put(cacheKey, retriever);
                    return listeners;
                }
            }
        } else {
            return this.retrieveApplicationListeners(eventType, sourceType, (AbstractApplicationEventMulticaster.ListenerRetriever)null);
        }
    }

前面我们提到我们这里出现了两种方式对监听器进行关联,这里用上面的源码可以解释
defaultRetriever只是关联了我们所有监听器,并且我们每次通过add方法添加的监听器都被放入其中,但是这里只是单纯进行监听器的关联,并未进行分类。
而当我们每次调用getApplicationListeners(ApplicationEvent event, ResolvableType eventType)时,都会有一轮分类操作,如果我们传入的计算因子无法获取到值,也就是retrieverCache中没有我们对于的监听组时,它会通过getApplicationListeners()获取 defaultRetriever中所有的监听器,然后调用他们的supportsEvent来判断目标监听器是否监听该事件,如果是就将其放入该事件类型和数据源的分组中。

我们能够在add监听器方法中发现每次的add都是放入 defaultRetriever并调用retrieverCache的clear方法,就是为了保证我们每次通过getApplicationListener获取的监听器一定是最全的。


ApplicationListener

ApplicationListener是什么

ApplicationListener代表Spring事件监听器的接口,用于监听SimpleApplicationEventMulticaster发布的所有事件。

在java中,我们的GUI组件就大量使用了监听器来监听事件,然后触发事件。但是由于我们的EventListener是一个空接口,这意味着我们需要拓展这个接口。而在GUI组件中每个监听器都会拓展大量的监听方法来监听不同事件,例如鼠标点击前后。这样做的好处是可以在一个监听器内监听不同的事件,缺点就是监听方法不确定,需要在事件发布者和监听器之间使用不同的适配器。
而我们的ApplicationListener则没有使用这样的方案,而是采用限定监听方法数量来实现通用性。但是仅仅这样还不够,这种实现的通用性需要借助接口和实现类来完成对不同事件的监听,,但是我们使用是是使用接口,这意味着我们无法在监听方法的参数中对监听事件类型进行限制,需要在实现类方法内部借助instanceof来分辨事件类型。在3.0后,Spring在监听器ApplicationListener中加入了泛型,通过泛型我们就可以实现接口来监听某些特定类型。

public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    void onApplicationEvent(E var1);
}

上面这种方式意味着我们可以在使用接口时通过泛型来限定监听事件的类型,因为泛型会提供编译期检查。但是这又引出了一个新的问题。由于添加了泛型类型,你无法同时监听不同的事件。所以下面就是Spring有引入的下面这种方式

public interface SmartApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {
    boolean supportsEventType(Class<? extends ApplicationEvent> var1);

    default boolean supportsSourceType(@Nullable Class<?> sourceType) {
        return true;
    }
	...
}

通过继承接口以及使用extends关键字来实现对一组事件类型的监听,同时依旧保证了面向接口的编程。唯一不足的就是相对于顶层接口,这样的方式无疑降低了一点通用性

ApplicationListener有什么用

用于监听SimpleApplicationEventMulticaster发布的所有事件。
根据我们定义的ApplicationListener的监听事件类型不同,事件发布器会根据我们事件类型的不同和事件源的不同对这些监听器进行分类,当其发布一个事件,就会根据事件类型和事件源检索出对应的监听器进行事件发布。
而我们监听器则是通过实现SmartApplicationListener的supportsEventType来选择我们要监听的事件类型

ApplicationListener从哪里来(ApplicationListener何时被注册到SimpleApplicationEventMulticaster中)

前面一直在说如何使用它们以及它们是如何运作发生联系的,但是如果监听器没有注册到事件发布器中,那么也就没有意义了

第一次注册发生在SpringAplication的构造器中
可以看到,SpringApplication中listeners变量也是通过getSpringFactoriesInstances从spring.factories中拿出来的。
但是这种方式适用用于在spring.factories中配置的监听器对象。我们一般不会对这样的文件进行操作

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        ...
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        ...
    }

第二次发生在容器初始化过程中,在完成容器中的bean对象注册后,也就是说我们自己配置的所有对象都已经被放入容器中了,当然,注册不等于实例化,当前只是代表容器中可以通过某个beanName获取到目标bean对象的全限定类名。
容器初始化过程中,有一个方法叫registerListeners,这个方法发生在initApplicationEventMulticaster()后也就是说,注册监听器时,容器已经把SpringApplicaiton中的事件发布器关联到自身了,所以我们registerListeners就是从某个地方获取到所有的监听器,然后将监听器们注册到之前的事件发布器中。

protected void registerListeners() {
        Iterator var1 = this.getApplicationListeners().iterator();

        while(var1.hasNext()) {
            ApplicationListener<?> listener = (ApplicationListener)var1.next();
            this.getApplicationEventMulticaster().addApplicationListener(listener);
        }

        String[] listenerBeanNames = this.getBeanNamesForType(ApplicationListener.class, true, false);
        String[] var7 = listenerBeanNames;
        int var3 = listenerBeanNames.length;

        for(int var4 = 0; var4 < var3; ++var4) {
            String listenerBeanName = var7[var4];
            this.getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
        }

        Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
        this.earlyApplicationEvents = null;
        if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
            Iterator var9 = earlyEventsToProcess.iterator();

            while(var9.hasNext()) {
                ApplicationEvent earlyEvent = (ApplicationEvent)var9.next();
                this.getApplicationEventMulticaster().multicastEvent(earlyEvent);
            }
        }

    }

该方法除了注册监听器以外,还做了一件事,那就去取出earlyApplicationEvents中保存的ApplicationEvent,然后遍历它们对它们进行广播。
我们都知道,在容器初始化事件发布器前还有不少步骤,比如注册BeanFactory和Bean的后置处理器,以及调用BeanFactory后置处理器等,但是这时候容器还不具有事件发布的器,也就睡没有向监听器发布事件的能力,但是由于容器实现了ApplicationEventPublisher接口,所以容器本身就具有发布事件的能力,但是在Spring中,这种能力是基于ApplicationEventMulticaster实现的,这就意味着在没有ApplicationEventMulticaster实例前,容器具有发布事件的行为,但是这是虚有其表的,那么这时候我们容器如果发布了事件就会先判断我们的earlyApplicationEvents是否为空,如果不为空就默认事件发布器还未初始化完成,先把方法堆起来,等到事件发布器和监听器注册初始化完成,我们就可以将之前积压的事件一次性发布,同时后面的新事件由于earlyApplicationEvents被设置为null,将不再把事件放入其中,而是直接通过ApplicationEventMulticaster实例进行事件发布

public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
...
	protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
        Assert.notNull(event, "Event must not be null");
        Object applicationEvent;
        if (event instanceof ApplicationEvent) {
            applicationEvent = (ApplicationEvent)event;
        } else {
            applicationEvent = new PayloadApplicationEvent(this, event);
            if (eventType == null) {
                eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();
            }
        }

        if (this.earlyApplicationEvents != null) {
            this.earlyApplicationEvents.add(applicationEvent);
        } else {
            this.getApplicationEventMulticaster().multicastEvent((ApplicationEvent)applicationEvent, eventType);
        }

        if (this.parent != null) {
            if (this.parent instanceof AbstractApplicationContext) {
                ((AbstractApplicationContext)this.parent).publishEvent(event, eventType);
            } else {
                this.parent.publishEvent(event);
            }
        }

    }
    ...
}

getApplicationListeners()

同时回顾以下EventPublishingRunListener构造方法,我们可以看到,在SimpleApplicationEventMulticaster创建之后,通过application.getListeners().iterator();获取ApplicationListener集合,这代表getListeners()可以获取到ApplicationListener集合,这是目前首次对ApplicationEventMulticaster注册监听器,getListeners()是SpringApplication中listeners变量的get方法,也就是说SpringApplication中listeners变量就是ApplicationListener集合。

public EventPublishingRunListener(SpringApplication application, String[] args) {
        this.application = application;
        this.args = args;
        this.initialMulticaster = new SimpleApplicationEventMulticaster();
        Iterator var3 = application.getListeners().iterator();

        while(var3.hasNext()) {
            ApplicationListener<?> listener = (ApplicationListener)var3.next();
            this.initialMulticaster.addApplicationListener(listener);
        }

    }

到这里我们会发现,在从SpringApplication发布事件开始,出现了连续两套观察者模式体系的连接
SpringApplication(事件源) →SpringApplicationRunListeners(发布者) → SpringApplicationRunListener(监听者) → ApplicationEventMulticaster(发布者) → ApplicationEvent(事件)→ ApplicationListener(监听器)
在SpringBoot中,存在两者事件,因为存在两个监听器以及事件发布器。

自定义Spring事件监听

我们前面所说的实现ApplicationListener,然后注册在容器中是最常见的手段,属于面向接口编程,但是SpringBoot中引入了@EventListener注册来实现面向注解编程

@EventListener

@EventListener必须标记在Bean的public方法中,同时标注了@EventListener的方法如果被重写,那么重写的方法也会继承该方法,并执行。它支持单监听也支持多监听,只需要在@EventListener参数中放入目标事件的类对象。同时不论监听几个类型的事件,监听方法的参数只能是零个或一个。

@Component
public class MyListeners {

    @EventListener({ContextRefreshedEvent.class})
    public void myFirstListener(ApplicationEvent event){
        System.out.println(event.getSource()+" ------------ "+event.getClass().getSimpleName());
    }
}

@EventListener的异步化

在事件发布器发布事件时,会判断当前事件发布器的线程池有没有被初始化,没有就同步发布事件,我们一般面向接口编程时默认都不会去初始化这个线程池。

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
	...
  public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
        ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
        Executor executor = this.getTaskExecutor();
        Iterator var5 = this.getApplicationListeners(event, type).iterator();

        while(var5.hasNext()) {
            ApplicationListener<?> listener = (ApplicationListener)var5.next();
            if (executor != null) {
                executor.execute(() -> {
                    this.invokeListener(listener, event);
                });
            } else {
                this.invokeListener(listener, event);
            }
        }

    }
    ...
}

如果我们希望异步,可以给它赋予一个线程池实例。当然,也可以通过注解的方式来实现异步化

@Component
public class MyListeners {

    @EventListener({ContextRefreshedEvent.class})
    @Async
    public void myFirstListener(ApplicationEvent event){
        System.out.println(event.getSource()+" ------------ "+event.getClass().getSimpleName());
    }
}

注意:如果我们加入了@Async还需要将返回值设置为非原生类型或者void

除了异步,还可以通过@Order或者实现Order接口来增加监听优先级

SpringApplicationRunListeners和ApplicationEventMulticaster的不同

我们都知道SpringBoot其实就是基于Spring的封装和增强,因此,Spring本身在SpringBoot中并没有消失
因此SpringApplicationRunListeners发布的是SpringBoot事件,而ApplicationEventMulticaster发布的是Spring容器事件
因为我们可以看出,这里有两个主体SpringApplication和ApplicationContext,前者负责构建配置后者,但两者都具有不同的声明周期,也会需要两套事件发布订阅体系

Spring事件ApplicationEvent

什么是ApplicationEvent

Spring容器中ApplicationEvent和ApplicationListener来表示事件和监听器。这是java中对于观察者模式实现的一个拓展,java中通过以下几个接口来表示观察者模式中的角色:

  • java.util.Observable:事件发布角色
  • java.util.EventListener表示事件监听角色
  • java.util.EventObejct:事件角色
    事件对象不提供默认构造器,需要外部传递一个soure参数,用于记录并跟踪事件的来源。Spring中的ApplicationEvent就是拓展了这个类

Spring内建事件

  • ContextRefreshedEvent:Spring应用上下文就绪实现,执行到finishReresh()时会被发布,此时容器中的Bean已经完成初始化,并能投入使用
  • ContextStartedEvent:Spring应用上下文启动事件。在start方法中被发布
  • ContextStoppedEvent:Spring应用上下文停止事件。在stop方法中被发布
  • ContextClosedEvent:Spring应用上下文关闭事件。在我们调用容器的close方法中被发布
  • RequestHandledEvent

自定义Spring事件

自定义事件需要拓展ApplicationEvent接口,然后由ApplicationEventPublisher#publishEvent方法发布

public class MyFirstEvent extends ApplicationEvent  {
    public MyFirstEvent(Object source) {
        super(source);
    }
    
}

泛型的ApplicationEvent还需要实现ResolvableTypeProvider
因为对于泛型事件,在发布事件时需要通过事件类型和数据源类型对监听器进行检索,其中由于事件的泛型的,需要转化成ResolvableType来提供一些必要的获取泛型的API

 public void multicastEvent(ApplicationEvent event) {
        this.multicastEvent(event, this.resolveDefaultEventType(event));
    }

    public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
        ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
        Executor executor = this.getTaskExecutor();
        Iterator var5 = this.getApplicationListeners(event, type).iterator();

        while(var5.hasNext()) {
            ApplicationListener<?> listener = (ApplicationListener)var5.next();
            if (executor != null) {
                executor.execute(() -> {
                    this.invokeListener(listener, event);
                });
            } else {
                this.invokeListener(listener, event);
            }
        }

    }

版权声明:本文为qq_33905217原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。