5. [SpringBoot source code] SpringBoot monitoring mechanism analysis

Table of contents

1. Introduction

2. SpringBoot event monitoring mechanism

1), load the ApplicationListener listener implementation class

2), get the running listener EventPublishingRunListener

3), release events

4), Spring event publishing multicastEvent() 


The source code of this article is analyzed based on the spring-boot-2.1.0.RELEASE version, and there may be some differences in each version.

1. Introduction

During the SpringBoot startup process, each different startup phase will broadcast different built-in life cycle events, and then the corresponding listener will do corresponding processing after listening to the event. For example, ConfigFileApplicationListener will listen to the onApplicationEnvironmentPreparedEvent event to load the environment variables of the configuration file application.properties, etc.

The listener mechanism of springboot is similar to the observer mode of our design pattern, and the listener mechanism has good scalability. When an event is published, there will be different listeners to handle it. If we want to listen to an event and do some other processing, then we only need to add a listener to listen to this event.

The abstract event object of SpringBoot is SpringApplicationEvent, which inherits from Spring's abstract event object ApplicationEvent . The class diagram is as follows:

Next, we analyze the source code of SpringBoot's event monitoring mechanism.

2. SpringBoot event monitoring mechanism

Through the previous analysis of the springboot startup process, we know that during the entire life cycle of the springboot application startup process, many events are released. Next, we will use this to analyze how springboot events are released.

@SpringBootApplication
public class SampleTomcatApplication {

	public static void main(String[] args) {
		SpringApplication.run(SampleTomcatApplication.class, args);
	}

}

1), load the ApplicationListener listener implementation class

Before executing the run() method to run SpringApplication, you must first create a SpringApplication object. Let's look at its construction method:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    // 传递的resourceLoader为null
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    // 记录主方法的配置类名称
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    // 推导出当前启动的项目的类型
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    // 加载配置在spring.factories文件中的ApplicationContextInitializer对应的类型并实例化. 并将加载的数据存储在了 initializers 成员变量中。
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 初始化监听器,并将加载的监听器实例对象存储在了listeners成员变量中
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 反推出main方法所在的Class对象
    this.mainApplicationClass = deduceMainApplicationClass();
}

 We can see that in the construction method of SpringApplication, some listeners are initialized through setListeners((Collection) getSpringFactoriesInstances(ApplicationListener. Find the listeners corresponding to ApplicationListener in the INF/spring.factories" file. After the acquisition is successful, assign these listeners to the SpringApplication#listeners property and save them.

For example, nine ApplicationListeners are configured in spring-boot/META-INF/spring.factories, as shown in the following figure:

When the springboot application starts, it will load ApplicationListener from all spring.factories configuration files, as shown below:

After this step, all the obtained ApplicationListeners have been saved in the listeners property of the SpringApplication object, and we can take them out of the SpringApplication object directly when we use them later.

2), get the running listener EventPublishingRunListener

Let's take a look at the run() method that runs SpringApplication:

// 运行 Spring 应用程序,创建并刷新一个新的ApplicationContext
public ConfigurableApplicationContext run(String... args) {
    // 创建一个任务执行观察器,用于统计run启动过程花了多少时间
    StopWatch stopWatch = new StopWatch();
    // 记录开始时间
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    // exceptionReporters集合用来存储异常报告器,用来报告SpringBoot启动过程的异常
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    // 设置了一个名为java.awt.headless的系统属性, 其实是想设置该应用程序,即使没有检测到显示器,也允许其启动. 对于服务器来说,是不需要显示器的,所以要这样设置.
    configureHeadlessProperty();
    // 从spring.factories配置文件中加载到EventPublishingRunListener对象并赋值给SpringApplicationRunListeners
    // EventPublishingRunListener对象主要用来发布SpringBoot启动过程中内置的一些生命周期事件,标志每个不同启动阶段
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 发布启动事件
    listeners.starting();
    try {
        // 创建ApplicationArguments对象,封装了args参数
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        // 准备环境,包括系统变量、环境变量、命令行参数、默认变量等
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        // 配置需要忽略的BeanInfo信息
        configureIgnoreBeanInfo(environment);
        // 启动时控制台打印Banner
        Banner printedBanner = printBanner(environment);
        // 根据不同类型创建不同类型的spring容器ApplicationContext应用程序上下文
        context = createApplicationContext();
        // 加载spring.factories配置文件配置的异常报告器
        exceptionReporters = getSpringFactoriesInstances(
                SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context);
        // 准备上下文,刷新容器前的一些操作
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        // 刷新应用上下文。完成Spring IOC容器的初始化
        refreshContext(context);
        // 在刷新上下文后调用的钩子,这个方法是一个模板方法
        afterRefresh(context, applicationArguments);
        // 停止记录执行时间
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass)
                    .logStarted(getApplicationLog(), stopWatch);
        }
        // 事件广播,启动完成了
        listeners.started(context);
        // 执行ApplicationRunner、CommandLineRunner的run方法,实现spring容器启动成功后需要执行的一些逻辑
        callRunners(context, applicationArguments);
    } catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }

    try {
        listeners.running(context);
    } catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }
    return context;
}

It can be seen that during the startup process of springboot, a spring running listener object is obtained through the getRunListeners(args) method, which is actually used to publish various life cycle events during the startup process of SpringBoot. 

SpringApplicationRunListeners listeners = getRunListeners(args);

private SpringApplicationRunListeners getRunListeners(String[] args) {
    Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
    // 获取spring.factories配置文件中配置的SpringApplicationRunListener,并通过反射实例化
    return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
            SpringApplicationRunListener.class, types, this, args));
}

See getSpringFactoriesInstances() again. Similarly, find the running listener corresponding to SpringApplicationRunListener from all "META-INF/spring.factories" files in the project, and then instantiate it by calling the construction method through reflection.

It can be seen that only one running listener --- EventPublishingRunListener is obtained at this time. Because it needs to be instantiated, let's look at the construction method of EventPublishingRunListener:

public EventPublishingRunListener(SpringApplication application, String[] args) {
    this.application = application;
    this.args = args;
    // 初始化事件发布器
    this.initialMulticaster = new SimpleApplicationEventMulticaster();
    // 循环添加事件监听者
    // application.getListeners(): 在实例化SpringApplication时设值的: SpringApplication.SpringApplication(org.springframework.core.io.ResourceLoader, java.lang.Class<?>...)
    for (ApplicationListener<?> listener : application.getListeners()) {
        this.initialMulticaster.addApplicationListener(listener);
    }
}

In the EventPublishingRunListener construction method, a SimpleApplicationEventMulticaster event publisher is created and assigned to the member property initialMulticaster, which is responsible for broadcasting lifecycle events when SpringBoot starts.

In addition to creating the SimpleApplicationEventMulticaster event publisher, the constructor also takes out the ApplicationListener that was previously set when instantiating SpringApplication, and adds them to the event publisher SimpleApplicationEventMulticaster one by one.

This step is equivalent to the registered listener in the observer mode.

3), release events

When running SpringApplication, that is, in the run() method, we can see that a total of 7 different types of life cycle events will be emitted during the startup process of SpringBoot to mark the different startup stages of SpringBoot:

  • ApplicationStartingEvent: Triggered when SpringBoot starts but before environment variables or IOC containers are created;
  • ApplicationEnvironmentPreparedEvent: SpringBoot has started, triggered when the environment variable Environment is ready;
  • ApplicationContextInitializedEvent: The initialization method of the initializer has been called, triggered before the bean definition is loaded;
  • ApplicationPreparedEvent: triggered before the spring container refresh refresh;
  • ApplicationStartedEvent: Triggered after the spring container is refreshed, before calling the run() method of ApplicationRunner and CommandLineRunner, it indicates that the spring container has been refreshed, and all bean instances have been loaded at this time;
  • ApplicationFailedEvent: An event published when an exception is encountered during SpringBoot startup;
  • ApplicationReadyEvent: Triggered after the run() method of ApplicationRunner and CommandLineRunner is called, indicating that SpringApplication is already running, that is, successfully started;

Let's take listeners.starting(); as an example to see the process of EventPublishingRunListener publishing events:

// org.springframework.boot.SpringApplicationRunListeners#starting
public void starting() {
    // EventPublishingRunListener
    for (SpringApplicationRunListener listener : this.listeners) {
        listener.starting();
    }
}

// org.springframework.boot.context.event.EventPublishingRunListener#starting
public void starting() {
		// 通过事件发布器发布容器启动事件ApplicationStartingEvent
		this.initialMulticaster.multicastEvent(
				new ApplicationStartingEvent(this.application, this.args));
	}

In fact, the event is published through the multicastEvent() method of SimpleApplicationEventMulticaster, and the event type is ApplicationStartingEvent.
SimpleApplicationEventMulticaster is actually a class in spring-context, continue to track the source code of Spring's event mechanism.

4), Spring event publishing multicastEvent() 

// org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)
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);
        }
    }
}

getApplicationListeners(event, type): Returns a collection of ApplicationListeners that match a given event type, and unmatched ApplicationListeners will be excluded in advance. [Get a list of listeners interested in the current event] 

protected Collection<ApplicationListener<?>> getApplicationListeners(
        ApplicationEvent event, ResolvableType eventType) {

    // 事件来源,实际上就是SpringApplication对象,里面存放着【1)、加载ApplicationListener监听器实现类】的listeners
    Object source = event.getSource();
    // class org.springframework.boot.SpringApplication
    Class<?> sourceType = (source != null ? source.getClass() : null);
    //  缓存key
    ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);

    // Quick check for existing entry on ConcurrentHashMap...
    ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
    
    // 如果已经在缓存中存在感兴趣的监听器,直接返回
    if (retriever != null) {
        return retriever.getApplicationListeners();
    }

    if (this.beanClassLoader == null ||
            (ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
                    (sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
        // Fully synchronized building and caching of a ListenerRetriever
        synchronized (this.retrievalMutex) {
            retriever = this.retrieverCache.get(cacheKey);
            if (retriever != null) {
                return retriever.getApplicationListeners();
            }
            retriever = new ListenerRetriever(true);
			// 循环所有的listeners,判断哪些listener满足条件
            // 只有对当前eventType感兴趣的listerer才会添加到监听器列表中
            Collection<ApplicationListener<?>> listeners =
                    retrieveApplicationListeners(eventType, sourceType, retriever);
            // 将满足条件的listener添加到缓存中
            this.retrieverCache.put(cacheKey, retriever);
            return listeners;
        }
    }
    else {
        // No ListenerRetriever caching -> no synchronization necessary
        return retrieveApplicationListeners(eventType, sourceType, null);
    }
}

Let's look at the retrieveApplicationListeners() method:

// 真正获取到与传入的事件匹配的那些监听器listener
private Collection<ApplicationListener<?>> retrieveApplicationListeners(
        ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable ListenerRetriever retriever) {

    List<ApplicationListener<?>> allListeners = new ArrayList<>();
    Set<ApplicationListener<?>> listeners;
    Set<String> listenerBeans;
    synchronized (this.retrievalMutex) {
        // 这个defaultRetriever里面存放的就是上图中source的所有的listeners
        listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
        listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
    }

    // 循环前面获取到的10个listeners,挨个调用supportsEvent()方法判断每个listener是否感兴趣
    for (ApplicationListener<?> listener : listeners) {
        if (supportsEvent(listener, eventType, sourceType)) {
            // 如果感兴趣,则加入到retriever中applicationListeners集合里
            if (retriever != null) {
                retriever.applicationListeners.add(listener);
            }
            allListeners.add(listener);
        }
    }
    if (!listenerBeans.isEmpty()) {
        BeanFactory beanFactory = getBeanFactory();
        for (String listenerBeanName : listenerBeans) {
            try {
                Class<?> listenerType = beanFactory.getType(listenerBeanName);
                if (listenerType == null || supportsEvent(listenerType, eventType)) {
                    ApplicationListener<?> listener =
                            beanFactory.getBean(listenerBeanName, ApplicationListener.class);
                    if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
                        if (retriever != null) {
                            if (beanFactory.isSingleton(listenerBeanName)) {
                                retriever.applicationListeners.add(listener);
                            }
                            else {
                                retriever.applicationListenerBeans.add(listenerBeanName);
                            }
                        }
                        allListeners.add(listener);
                    
                }
            }
            catch (NoSuchBeanDefinitionException ex) {
                // Singleton listener instance (without backing bean definition) disappeared -
                // probably in the middle of the destruction phase
            }
        }
    }
    // 排一下序
    AnnotationAwareOrderComparator.sort(allListeners);
    if (retriever != null && retriever.applicationListenerBeans.isEmpty()) {
        retriever.applicationListeners.clear();
        retriever.applicationListeners.addAll(allListeners);
    }
    // 返回匹配的那些监听器listener
    return allListeners;
}

After matching, it can be seen that 4 listeners are matched out of 10 listeners, and they are interested in the event org.springframework.boot.context.event.ApplicationStartingEvent, as shown in the figure below:

Let's go back and look at the source code of the multicastEvent() method:

// org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)
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);
        }
    }
}

It was obtained earlier that only 4 ApplicationListeners listened to the org.springframework.boot.context.event.ApplicationStartingEvent event, so these 4 listeners will be cycled and their onApplicationEvent() callback methods will be triggered in turn.

Here we select one of the listeners [LoggingApplicationListener] that listened to the ApplicationStartingEvent event to analyze:

It can be seen that in the onApplicationEvent() method, instanceof is used to judge different events to do different things.

Guess you like

Origin blog.csdn.net/Weixiaohuai/article/details/128939174