掘金 后端 ( ) • 2024-04-18 13:46

监听器

EventPublishingRunListener

今天我们来讲监听器,话不多说先从SpringApplication#run方法中的getRunListeners讲起

/**
 * Run the Spring application, creating and refreshing a new
 * {@link ApplicationContext}.
 * @param args the application arguments (usually passed from a Java main method)
 * @return a running {@link ApplicationContext}
 */
public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    configureHeadlessProperty();
    // 获取监听器
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
   
    // ... 以下代码省略
}

进入getRunListeners方法

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

如果看过SpringFactoriesLoader的话应该就很熟悉这段代码了,这里的getSpringFactoriesInstances就是通过这个工厂加载类来加载实体类,贴上代码,具体不再详细说明,可以看我的上一篇文章,注意这个方法传入的第一个参数是SpringApplicationRunListener.class

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
       Class<?>[] parameterTypes, Object... args) {
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    // Use names and ensure unique to protect against duplicates
    Set<String> names = new LinkedHashSet<>(
          SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
          classLoader, args, names);
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}

根据第一个参数,也就是说我们最后会在spring.factories中找到对应的实例也就是EventPublishingRunListener

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

先来看下EventPublishingRunListener是做什么的

从源代码中可以看到EventPublishingRunListener实现了SpringApplicationRunListener这个接口,该接口定义了几个方法用于监听SpringApplication启动和运行或者叫生命周期的不同阶段。所以EventPublishingRunListener通过事件发布的方式通知注册的监听器应用程序的不同阶段,比如启动、环境准备、应用程序上下文准备、应用程序启动、运行以及失败等

/**
 * Listener for the {@link SpringApplication} {@code run} method.
 * {@link SpringApplicationRunListener}s are loaded via the {@link SpringFactoriesLoader}
 * and should declare a public constructor that accepts a {@link SpringApplication}
 * instance and a {@code String[]} of arguments. A new
 * {@link SpringApplicationRunListener} instance will be created for each run.
 *
 * @author Phillip Webb
 * @author Dave Syer
 * @author Andy Wilkinson
 */
public interface SpringApplicationRunListener {
​
    /**
     * Called immediately when the run method has first started. Can be used for very
     * early initialization.
     在首次启动run方法时立即调用。 可用于非常早的初始化
     */
    void starting();
​
    /**
     * Called once the environment has been prepared, but before the
     * {@link ApplicationContext} has been created.
     * @param environment the environment
     在准备ApplicationContext之后,创建好ApplicationContext之前调用
     */
    void environmentPrepared(ConfigurableEnvironment environment);
​
    /**
     * Called once the {@link ApplicationContext} has been created and prepared, but
     * before sources have been loaded.
     * @param context the application context
     在创建和准备ApplicationContext之后,但在加载源之前调用
     */
    void contextPrepared(ConfigurableApplicationContext context);
​
    /**
     * Called once the application context has been loaded but before it has been
     * refreshed.
     * @param context the application context
      在ApplicationContext加载完之后但是在其刷新之前调用
     */
    void contextLoaded(ConfigurableApplicationContext context);
​
    /**
     * The context has been refreshed and the application has started but
     * {@link CommandLineRunner CommandLineRunners} and {@link ApplicationRunner
     * ApplicationRunners} have not been called.
     * @param context the application context.
     * @since 2.0.0
     上下文已刷新,应用程序已启动,但CommandLineRunner 和ApplicationRunner 尚未被调用
     */
    void started(ConfigurableApplicationContext context);
​
    /**
     * Called immediately before the run method finishes, when the application context has
     * been refreshed and all {@link CommandLineRunner CommandLineRunners} and
     * {@link ApplicationRunner ApplicationRunners} have been called.
     * @param context the application context.
     * @since 2.0.0
     在刷新应用程序上下文并已调用所有CommandLineRunner和ApplicationRunner后,在run方法完成之前立即调用
     */
    void running(ConfigurableApplicationContext context);
​
    /**
     * Called when a failure occurs when running the application.
     * @param context the application context or {@code null} if a failure occurred before
     * the context was created
     * @param exception the failure
     * @since 2.0.0
     在应用程序启动或运行期间出现异常时调用。可以用于处理应用程序失败的情况。
     */
    void failed(ConfigurableApplicationContext context, Throwable exception);
​
}
​

事实上,在createSpringFactoriesInstances中还会调用构造方法从而创建实例,从上面我们知道这边找到了EventPublishingRunListener,因此会调用该类的构造方法

@SuppressWarnings("unchecked")
private <T> List<T> createSpringFactoriesInstances(Class<T> type,
       Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
       Set<String> names) {
    List<T> instances = new ArrayList<>(names.size());
    for (String name : names) {
       try {
          Class<?> instanceClass = ClassUtils.forName(name, classLoader);
          Assert.isAssignable(type, instanceClass);
          Constructor<?> constructor = instanceClass
                .getDeclaredConstructor(parameterTypes);
          // 调用构造方法创建实例
          T instance = (T) BeanUtils.instantiateClass(constructor, args);
          instances.add(instance);
       }
       catch (Throwable ex) {
          throw new IllegalArgumentException(
                "Cannot instantiate " + type + " : " + name, ex);
       }
    }
    return instances;
}

我们来看一下它的构造方法做了那些事情

private final SimpleApplicationEventMulticaster initialMulticaster;
​
public EventPublishingRunListener(SpringApplication application, String[] args) {
    this.application = application;
    this.args = args;
    this.initialMulticaster = new SimpleApplicationEventMulticaster();
    // 这边的application.getListeners() 在 new SpringApplication(primarySources).run(args)时保存了listeners,所以可以拿到注册的监听器
    for (ApplicationListener<?> listener : application.getListeners()) {
        // 存储在SimpleApplicationEventMulticaster的内部类中是一个set(继承了抽象类AbstractApplicationEventMulticaster)
       this.initialMulticaster.addApplicationListener(listener);
    }
}

该方法将加载到的监听器放入了容器中,先想一下这里的application.getListeners()从何而来?下面这段代码还记得吗,没错在SpringApplication构建时就加载了包括系统初始化器以及监听器,从而将这些监听器存入了SimpleApplicationEventMulticaster

    /**
     * Create a new {@link SpringApplication} instance. The application context will load
     * beans from the specified primary sources (see {@link SpringApplication class-level}
     * documentation for details. The instance can be customized before calling
     * {@link #run(String...)}.
     * @param resourceLoader the resource loader to use
     * @param primarySources the primary bean sources
     * @see #run(Class, String[])
     * @see #setSources(Set)
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        // 初始化器
        setInitializers((Collection) getSpringFactoriesInstances(
                ApplicationContextInitializer.class));
        // 监听器
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = deduceMainApplicationClass();
    }

接着我们来看看SimpleApplicationEventMulticaster又是什么

ApplicationEventMulticaster

/**
 * Simple implementation of the {@link ApplicationEventMulticaster} interface.
 *
 * <p>Multicasts all events to all registered listeners, leaving it up to
 * the listeners to ignore events that they are not interested in.
 * Listeners will usually perform corresponding {@code instanceof}
 * checks on the passed-in event object.
 *
 * <p>By default, all listeners are invoked in the calling thread.
 * This allows the danger of a rogue listener blocking the entire application,
 * but adds minimal overhead. Specify an alternative task executor to have
 * listeners executed in different threads, for example from a thread pool.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @author Stephane Nicoll
 * @see #setTaskExecutor
 */
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
}

首先SimpleApplicationEventMulticaster继承自AbstractApplicationEventMulticaster

/**
 * Abstract implementation of the {@link ApplicationEventMulticaster} interface,
 * providing the basic listener registration facility.
 *
 * <p>Doesn't permit multiple instances of the same listener by default,
 * as it keeps listeners in a linked Set. The collection class used to hold
 * ApplicationListener objects can be overridden through the "collectionClass"
 * bean property.
 *
 * <p>Implementing ApplicationEventMulticaster's actual {@link #multicastEvent} method
 * is left to subclasses. {@link SimpleApplicationEventMulticaster} simply multicasts
 * all events to all registered listeners, invoking them in the calling thread.
 * Alternative implementations could be more sophisticated in those respects.
 *
 * @author Juergen Hoeller
 * @author Stephane Nicoll
 * @since 1.2.3
 * @see #getApplicationListeners(ApplicationEvent, ResolvableType)
 * @see SimpleApplicationEventMulticaster
 */
public abstract class AbstractApplicationEventMulticaster
       implements ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware {
        
    private final ListenerRetriever defaultRetriever = new ListenerRetriever(false);    
    final Map<ListenerCacheKey, ListenerRetriever> retrieverCache = new ConcurrentHashMap<>(64);
    
    ...
    
    /**
     * Helper class that encapsulates a specific set of target listeners,
     * allowing for efficient retrieval of pre-filtered listeners.
     * <p>An instance of this helper gets cached per event type and source type.
     该类主要用于帮助 ApplicationEventMulticaster 的实现类管理监听器(listeners)和它们的订阅关系
     */
    private class ListenerRetriever {
​
        public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();
​
        public final Set<String> applicationListenerBeans = new LinkedHashSet<>();
​
        private final boolean preFiltered;
​
        public ListenerRetriever(boolean preFiltered) {
            this.preFiltered = preFiltered;
        }
​
        public Collection<ApplicationListener<?>> getApplicationListeners() {
            List<ApplicationListener<?>> allListeners = new ArrayList<>(
                    this.applicationListeners.size() + this.applicationListenerBeans.size());
            allListeners.addAll(this.applicationListeners);
            if (!this.applicationListenerBeans.isEmpty()) {
                BeanFactory beanFactory = getBeanFactory();
                for (String listenerBeanName : this.applicationListenerBeans) {
                    try {
                        ApplicationListener<?> listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class);
                        if (this.preFiltered || !allListeners.contains(listener)) {
                            allListeners.add(listener);
                        }
                    }
                    catch (NoSuchBeanDefinitionException ex) {
                        // Singleton listener instance (without backing bean definition) disappeared -
                        // probably in the middle of the destruction phase
                    }
                }
            }
            if (!this.preFiltered || !this.applicationListenerBeans.isEmpty()) {
                AnnotationAwareOrderComparator.sort(allListeners);
            }
            return allListeners;
        }
    }
}

AbstractApplicationEventMulticaster又实现了ApplicationEventMulticaster,该类提供了一些基本的、通用的方法和属性,如对 ApplicationListener 的管理(ListenerRetriever)等。

/**
 * Interface to be implemented by objects that can manage a number of
 * {@link ApplicationListener} objects, and publish events to them.
 这是一个可以管理多个 ApplicationListener 对象并向它们发布事件的接口。
 这意味着它负责将事件传递给所有注册的监听器
 *
 * <p>An {@link org.springframework.context.ApplicationEventPublisher}, typically
 * a Spring {@link org.springframework.context.ApplicationContext}, can use an
 * ApplicationEventMulticaster as a delegate for actually publishing events.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @author Stephane Nicoll
 */
public interface ApplicationEventMulticaster {
​
    /**
     * Add a listener to be notified of all events.
     * @param listener the listener to add
     */
    void addApplicationListener(ApplicationListener<?> listener);
​
    /**
     * Add a listener bean to be notified of all events.
     * @param listenerBeanName the name of the listener bean to add
     */
    void addApplicationListenerBean(String listenerBeanName);
​
    /**
     * Remove a listener from the notification list.
     * @param listener the listener to remove
     */
    void removeApplicationListener(ApplicationListener<?> listener);
​
    /**
     * Remove a listener bean from the notification list.
     * @param listenerBeanName the name of the listener bean to add
     */
    void removeApplicationListenerBean(String listenerBeanName);
​
    /**
     * Remove all listeners registered with this multicaster.
     * <p>After a remove call, the multicaster will perform no action
     * on event notification until new listeners are being registered.
     */
    void removeAllListeners();
​
    /**
     * Multicast the given application event to appropriate listeners.
     * <p>Consider using {@link #multicastEvent(ApplicationEvent, ResolvableType)}
     * if possible as it provides a better support for generics-based events.
     * @param event the event to multicast
     */
    void multicastEvent(ApplicationEvent event);
​
    /**
     * Multicast the given application event to appropriate listeners.
     * <p>If the {@code eventType} is {@code null}, a default type is built
     * based on the {@code event} instance.
     * @param event the event to multicast
     * @param eventType the type of event (can be null)
     * @since 4.2
     */
    void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);
​
}
​

ApplicationEventMulticaster 它允许对象管理一组 ApplicationListener 对象,并将事件发布给这些监听器

接着我们来看它是如何管理对象和发布事件的

回到代码EventPublishingRunListener#starting,代码流程为

SpringApplication#run -> listeners.starting() -> SpringApplicationRunListeners#starting -> listener.starting() -> EventPublishingRunListener#starting 该方法广播了一个事件,名为ApplicationStartingEvent,也就是multicastEvent方法的第一个参数

private final SimpleApplicationEventMulticaster initialMulticaster;
@Override
public void starting() {
    this.initialMulticaster.multicastEvent(
          new ApplicationStartingEvent(this.application, this.args));
}

看一下SimpleApplicationEventMulticastermulticastEvent方法

@Override
public void multicastEvent(ApplicationEvent event) {
    multicastEvent(event, resolveDefaultEventType(event));
}
​
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
       Executor executor = getTaskExecutor();
       if (executor != null) {
          executor.execute(() -> invokeListener(listener, event));
       }
       else {
          invokeListener(listener, event);
       }
    }
}

这两个方法需要传入ApplicationEvent事件

/**
 * Class to be extended by all application events. Abstract as it
 * doesn't make sense for generic events to be published directly.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 */
public abstract class ApplicationEvent extends EventObject {
​
    /** use serialVersionUID from Spring 1.2 for interoperability */
    private static final long serialVersionUID = 7099057708183571937L;
​
    /** System time when the event happened */
    private final long timestamp;
​
​
    /**
     * Create a new ApplicationEvent.
     * @param source the object on which the event initially occurred (never {@code null})
     */
    public ApplicationEvent(Object source) {
        super(source);
        this.timestamp = System.currentTimeMillis();
    }
​
​
    /**
     * Return the system time in milliseconds when the event happened.
     */
    public final long getTimestamp() {
        return this.timestamp;
    }
​
}

看一下该类的子类都有哪些

image-20240417102444696.png 有没有觉得在哪里见过,没错这些子类事件正好对应了SpringApplicationRunListener定义的不同阶段

回到SimpleApplicationEventMulticaster#multicastEvent方法

@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    // 获取事件对应的监听器
    for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
       Executor executor = getTaskExecutor();
        // 执行监听器方法
       if (executor != null) {
          executor.execute(() -> invokeListener(listener, event));
       }
       else {
          invokeListener(listener, event);
       }
    }
}

整体来说该方法就做了两件事。首先根据事件类型获取该事件对应监听器,获取到对应监听器之后调用监听器的onApplicationEvent方法完成相应的动作

流程

最后来看下整体流程

  1. 方法启动时首先进入构造方法,构造方法会通过spring.factores文件找到ApplicationListener所有的实例并存储
  2. 构造器执行完毕进入run方法,同样的,在getRunListeners中会获取SpringApplicationRunListener的实现,也就是EventPublishingRunListener。还有调用EventPublishingRunListener的构造方法,保存上一步存储的监听器到内部容器中(AbstractApplicationEventMulticaster#ListenerRetriever)
  3. listeners.starting(),进入EventPublishingRunListener的starting方法并调用SimpleApplicationEventMulticaster的multicastEvent,这里会传入事件类型ApplicationStartingEvent
  4. multicastEvent方法会找到事件对应的监听器并调用监听器的onApplicationEvent

流程图如下(图片出自:https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/SpringBoot/深入SpringBoot源码学习之——监听器与事件机制.mdSpringBoot监听器流程图