码迷,mamicode.com
首页 > 编程语言 > 详细

Spring Boot -- 启动流程分析一

时间:2020-06-26 20:23:39      阅读:50      评论:0      收藏:0      [点我收藏+]

标签:2-2   gets   种类   versions   extc   stp   web   搜索   如何   

我们在开发Spring Boot程序的时候,我们只需要在启动类上加入@SpringBootApplication注解,然后运行SpringApplication.run(),这样Spring容器就运行起来了。

@SpringBootApplication(scanBasePackages={"com.jnu.example"})
@CoreMapperScan
@EnableAspectAutoProxy
public class App {
    public static void main(String[] args) {
        SpringApplication.run(BlogApplication.class, args);
    }
}

那么问题来了,相比最初Spring MVC繁琐的xml的配置方式,现在只需要简单几行代码,Spring容器就可以启动起来,我们就可以从容器中获取到bean,Spring Boot内部是如何做到的呢?

一、SringApplication准备阶段

1.1、SpringApplication准备阶段

SpringApplication 在运行前做了一系列的准备工作,如:推断 Web 应用类型、加载 Spring 上下文初始器、事件监听器等。接下来,就通过源码的方式进行学习。

我们首先进入SpringApplication的静态帮助方法run里面,该方法接受两个参数:

    /**
     * Static helper that can be used to run a {@link SpringApplication} from the
     * specified source using default settings.
     * @param primarySource the primary source to load
     * @param args the application arguments (usually passed from a Java main method)
     * @return the running {@link ApplicationContext}
     */
    public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
        return run(new Class<?>[] { primarySource }, args);
    }
  • primarySource:要加载的主要配置类,也就是被@SpringBootApplication注解的App类;
  • args:一个可变数组,保存的是应用程序的运行参数,具体可以指定哪些属性,可以查看官网说明:Common Application properties

接下来,调用了另一个静态帮助方法run,并将primarySource转换为数组参数传入;

    public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        return new SpringApplication(primarySources).run(args);
    }

这里通过入参primarySources创建了一个SpringApplication对象,其中,准备阶段的工作皆在 SpringApplication 的构造器中处理:

    /**
     * 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 primarySources the primary bean sources
     * @see #run(Class, String[])
     * @see #SpringApplication(ResourceLoader, Class...)
     * @see #setSources(Set)
     */
    public SpringApplication(Class<?>... primarySources) {
        this(null, primarySources);
    }

这里调用了另一个构造方法,然后初始化primarySource,webApplicationType、initializers、listeners、mainApplicationClass等字段:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    
        // resourceLoader 主要用来获取 Resource 及 ClassLoader。这里值为 null
        this.resourceLoader = resourceLoader;
        
        // 断言primarySources不能为null,否则报错
        Assert.notNull(primarySources, "PrimarySources must not be null");
        
        // primarySources是SpringApplication.run的参数,存放的是主配置类
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        
        // 进行Web应用的类型推断
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        
        // 加载应用上下文初始化器 initializer
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        
        // 加载应用事件监听器 listener
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        
        // 推断引导类,也就是找到入口类
        this.mainApplicationClass = deduceMainApplicationClass();
    }

1.2、推断Web应用类型

// 进行Web应用的类型推断
this.webApplicationType = WebApplicationType.deduceFromClasspath();

SpringApplication允许指定应用的类型,大体上分为Web应用和非Web应用。从Spring Boot2.0开始,Web应用又可以分为Servlet Web和Reactive Web。而在准备阶段,是通过检查当前ClassPath下某些Class是否存在,从而推导Web应用的类型:

/**
 * An enumeration of possible types of web application.
 *
 * @author Andy Wilkinson
 * @author Brian Clozel
 * @since 2.0.0
 */
public enum WebApplicationType {

    /**
     * The application should not run as a web application and should not start an
     * embedded web server.
     */
    NONE,

    /**
     * The application should run as a servlet-based web application and should start an
     * embedded servlet web server.
     */
    SERVLET,

    /**
     * The application should run as a reactive web application and should start an
     * embedded reactive web server.
     */
    REACTIVE;

    private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
            "org.springframework.web.context.ConfigurableWebApplicationContext" };

    private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";

    private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";

    private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";

    private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";

    private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";

    static WebApplicationType deduceFromClasspath() {
        if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
                && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
            return WebApplicationType.REACTIVE;
        }
        for (String className : SERVLET_INDICATOR_CLASSES) {
            if (!ClassUtils.isPresent(className, null)) {
                return WebApplicationType.NONE;
            }
        }
        return WebApplicationType.SERVLET;
    }

    static WebApplicationType deduceFromApplicationContext(Class<?> applicationContextClass) {
        if (isAssignable(SERVLET_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
            return WebApplicationType.SERVLET;
        }
        if (isAssignable(REACTIVE_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
            return WebApplicationType.REACTIVE;
        }
        return WebApplicationType.NONE;
    }

    private static boolean isAssignable(String target, Class<?> type) {
        try {
            return ClassUtils.resolveClassName(target, null).isAssignableFrom(type);
        }
        catch (Throwable ex) {
            return false;
        }
    }

}

可以看到,在方法中利用 ClassUtils.isPresent 进行判断, 当DispatcherHandler存在,而DispatcherServlet和ServletContainer不存在时,则当前应用推导为 Reactive web 类型;当 Servlet 和 ConfigurableWebApplicationContext 不存在时,当前应用为非 Web 类型;其他的则为 Servlet Web 类型。

注:Reactive:Reactive响应式编程是一种新的编程风格,其特点是异步或并发、事件驱动、推送PUSH机制以及观察者模式的衍生。

该函数执行完后,结果如下:

技术图片

1.3、加载应用上下文初始器ApplicationContextInitializer

setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class))

接着进入加载Spring应用上下文初始器的过程;

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
        return getSpringFactoriesInstances(type, new Class<?>[] {});
    }

    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = getClassLoader();
        // 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 工厂加载机制 SpringFactoriesLoader.loadFactoryNames(type, classLoader) 方法获取,采用这种方式可以将非本项目的外部包的bean加载到Spring容器中

技术图片

该方法是从项目引用的所有的jar的 META-INF/spring.factories 资源中获取key为 ApplicationContextInitializer 的实现类集合,如下是 spring-boot-autoconfigure 包下的 spring.factories 文件:

# Initializers
org.springframework.context.ApplicationContextInitializer=org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

这里获取的就是 SharedMetadataReaderFactoryContextInitializer 和 ConditionEvaluationReportLoggingListener 上下文初始化器,接下来通过 createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names) 方法初始化这些实现类:

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

这里先通过 BeanUtils.instantiate 初始化这些类,然后将初始化的类保存至List进行返回。

技术图片

 并进行排序操作,最后添加到SpringApplication的initializers集合变量中。至此,该流程结束。 

    /**
     * Sets the {@link ApplicationContextInitializer} that will be applied to the Spring
     * {@link ApplicationContext}.
     * @param initializers the initializers to set
     */
    public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {
        this.initializers = new ArrayList<>(initializers);
    }

我们举例来看看初始器中的内容,如SharedMetadataReaderFactoryContextInitializer:

技术图片
/*
 * Copyright 2012-2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.boot.autoconfigure;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.boot.type.classreading.ConcurrentReferenceCachingMetadataReaderFactory;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReaderFactory;

/**
 * {@link ApplicationContextInitializer} to create a shared
 * {@link CachingMetadataReaderFactory} between the
 * {@link ConfigurationClassPostProcessor} and Spring Boot.
 *
 * @author Phillip Webb
 */
class SharedMetadataReaderFactoryContextInitializer
        implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {

    public static final String BEAN_NAME = "org.springframework.boot.autoconfigure."
            + "internalCachingMetadataReaderFactory";

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        applicationContext.addBeanFactoryPostProcessor(new CachingMetadataReaderFactoryPostProcessor());
    }

    @Override
    public int getOrder() {
        return 0;
    }

    /**
     * {@link BeanDefinitionRegistryPostProcessor} to register the
     * {@link CachingMetadataReaderFactory} and configure the
     * {@link ConfigurationClassPostProcessor}.
     */
    private static class CachingMetadataReaderFactoryPostProcessor
            implements BeanDefinitionRegistryPostProcessor, PriorityOrdered {

        @Override
        public int getOrder() {
            // Must happen before the ConfigurationClassPostProcessor is created
            return Ordered.HIGHEST_PRECEDENCE;
        }

        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        }

        @Override
        public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
            register(registry);
            configureConfigurationClassPostProcessor(registry);
        }

        private void register(BeanDefinitionRegistry registry) {
            BeanDefinition definition = BeanDefinitionBuilder
                    .genericBeanDefinition(SharedMetadataReaderFactoryBean.class, SharedMetadataReaderFactoryBean::new)
                    .getBeanDefinition();
            registry.registerBeanDefinition(BEAN_NAME, definition);
        }

        private void configureConfigurationClassPostProcessor(BeanDefinitionRegistry registry) {
            try {
                BeanDefinition definition = registry
                        .getBeanDefinition(AnnotationConfigUtils.CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME);
                definition.getPropertyValues().add("metadataReaderFactory", new RuntimeBeanReference(BEAN_NAME));
            }
            catch (NoSuchBeanDefinitionException ex) {
            }
        }

    }

    /**
     * {@link FactoryBean} to create the shared {@link MetadataReaderFactory}.
     */
    static class SharedMetadataReaderFactoryBean
            implements FactoryBean<ConcurrentReferenceCachingMetadataReaderFactory>, BeanClassLoaderAware,
            ApplicationListener<ContextRefreshedEvent> {

        private ConcurrentReferenceCachingMetadataReaderFactory metadataReaderFactory;

        @Override
        public void setBeanClassLoader(ClassLoader classLoader) {
            this.metadataReaderFactory = new ConcurrentReferenceCachingMetadataReaderFactory(classLoader);
        }

        @Override
        public ConcurrentReferenceCachingMetadataReaderFactory getObject() throws Exception {
            return this.metadataReaderFactory;
        }

        @Override
        public Class<?> getObjectType() {
            return CachingMetadataReaderFactory.class;
        }

        @Override
        public boolean isSingleton() {
            return true;
        }

        @Override
        public void onApplicationEvent(ContextRefreshedEvent event) {
            this.metadataReaderFactory.clearCache();
        }

    }

}
View Code

可以看到该类实现了 Spring 的 ApplicationContextInitializer 接口,并重写了initialize()方法。同理,其他的 Initializer 接口也是类似实现。 而在这里则是在上下文中加入了 CachingMetadataReaderFactoryPostProcessor bean工厂后置处理器。

ApplicationContextInitializer 接口的主要作用是在 ConfigurableApplicationContext#refresh() 方法调用之前做一些初始化工作。

1.4、加载应用事件监听器ApplicationListener

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class))

接着加载应用事件监听器 ,过程与“加载应用上下文初始器”基本一致,同样是调用 getSpringFactoriesInstances 方法,不过这里获取的是 key 为 ApplicationListener 的对象集合:

技术图片

如下是 spring-boot-autoconfigure 包下的 spring.factories 文件:

# Application Listeners
org.springframework.context.ApplicationListener=org.springframework.boot.autoconfigure.BackgroundPreinitializer

最后,将获取的 BackgroundPreinitializer 对象通过 setListeners 方法放入 listeners 属性变量中:

    /**
     * Sets the {@link ApplicationListener}s that will be applied to the SpringApplication
     * and registered with the {@link ApplicationContext}.
     * @param listeners the listeners to set
     */
    public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
        this.listeners = new ArrayList<>(listeners);
    }

我们同样举例,来看看监听器中的内容,如BackgroundPreinitializer:

技术图片
/*
 * Copyright 2012-2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.boot.autoconfigure;

import java.nio.charset.StandardCharsets;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.validation.Configuration;
import javax.validation.Validation;

import org.springframework.boot.context.event.ApplicationFailedEvent;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.boot.context.event.ApplicationStartingEvent;
import org.springframework.boot.context.event.SpringApplicationEvent;
import org.springframework.boot.context.logging.LoggingApplicationListener;
import org.springframework.context.ApplicationListener;
import org.springframework.core.annotation.Order;
import org.springframework.format.support.DefaultFormattingConversionService;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;

/**
 * {@link ApplicationListener} to trigger early initialization in a background thread of
 * time consuming tasks.
 * <p>
 * Set the {@link #IGNORE_BACKGROUNDPREINITIALIZER_PROPERTY_NAME} system property to
 * {@code true} to disable this mechanism and let such initialization happen in the
 * foreground.
 *
 * @author Phillip Webb
 * @author Andy Wilkinson
 * @author Artsiom Yudovin
 * @since 1.3.0
 */
@Order(LoggingApplicationListener.DEFAULT_ORDER + 1)
public class BackgroundPreinitializer implements ApplicationListener<SpringApplicationEvent> {

    /**
     * System property that instructs Spring Boot how to run pre initialization. When the
     * property is set to {@code true}, no pre-initialization happens and each item is
     * initialized in the foreground as it needs to. When the property is {@code false}
     * (default), pre initialization runs in a separate thread in the background.
     * @since 2.1.0
     */
    public static final String IGNORE_BACKGROUNDPREINITIALIZER_PROPERTY_NAME = "spring.backgroundpreinitializer.ignore";

    private static final AtomicBoolean preinitializationStarted = new AtomicBoolean(false);

    private static final CountDownLatch preinitializationComplete = new CountDownLatch(1);

    @Override
    public void onApplicationEvent(SpringApplicationEvent event) {
        if (!Boolean.getBoolean(IGNORE_BACKGROUNDPREINITIALIZER_PROPERTY_NAME)
                && event instanceof ApplicationStartingEvent && multipleProcessors()
                && preinitializationStarted.compareAndSet(false, true)) {
            performPreinitialization();
        }
        if ((event instanceof ApplicationReadyEvent || event instanceof ApplicationFailedEvent)
                && preinitializationStarted.get()) {
            try {
                preinitializationComplete.await();
            }
            catch (InterruptedException ex) {
                Thread.currentThread().interrupt();
            }
        }
    }

    private boolean multipleProcessors() {
        return Runtime.getRuntime().availableProcessors() > 1;
    }

    private void performPreinitialization() {
        try {
            Thread thread = new Thread(new Runnable() {

                @Override
                public void run() {
                    runSafely(new ConversionServiceInitializer());
                    runSafely(new ValidationInitializer());
                    runSafely(new MessageConverterInitializer());
                    runSafely(new JacksonInitializer());
                    runSafely(new CharsetInitializer());
                    preinitializationComplete.countDown();
                }

                public void runSafely(Runnable runnable) {
                    try {
                        runnable.run();
                    }
                    catch (Throwable ex) {
                        // Ignore
                    }
                }

            }, "background-preinit");
            thread.start();
        }
        catch (Exception ex) {
            // This will fail on GAE where creating threads is prohibited. We can safely
            // continue but startup will be slightly slower as the initialization will now
            // happen on the main thread.
            preinitializationComplete.countDown();
        }
    }

    /**
     * Early initializer for Spring MessageConverters.
     */
    private static class MessageConverterInitializer implements Runnable {

        @Override
        public void run() {
            new AllEncompassingFormHttpMessageConverter();
        }

    }

    /**
     * Early initializer for javax.validation.
     */
    private static class ValidationInitializer implements Runnable {

        @Override
        public void run() {
            Configuration<?> configuration = Validation.byDefaultProvider().configure();
            configuration.buildValidatorFactory().getValidator();
        }

    }

    /**
     * Early initializer for Jackson.
     */
    private static class JacksonInitializer implements Runnable {

        @Override
        public void run() {
            Jackson2ObjectMapperBuilder.json().build();
        }

    }

    /**
     * Early initializer for Spring‘s ConversionService.
     */
    private static class ConversionServiceInitializer implements Runnable {

        @Override
        public void run() {
            new DefaultFormattingConversionService();
        }

    }

    private static class CharsetInitializer implements Runnable {

        @Override
        public void run() {
            StandardCharsets.UTF_8.name();
        }

    }

}
View Code

可以看到,该类实现了 Spring 的 ApplicationListener 接口,在重写的 onApplicationEvent 方法中触发相应的事件进行操作。同理,其他 Listener 也是类似实现。而该接口的主要功能是另起一个后台线程触发那些耗时的初始化,包括验证器、消息转换器等等。 

1.5、推断应用引导类

// 推断引导类,也就是找到入口类
this.mainApplicationClass = deduceMainApplicationClass();

准备阶段的最后一步是推断应用的引导类,也就是获取启动 main 方法的类,执行的是 deduceMainApplicationClass() 方法:

    private Class<?> deduceMainApplicationClass() {
        try {
            StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
            for (StackTraceElement stackTraceElement : stackTrace) {
                if ("main".equals(stackTraceElement.getMethodName())) {
                    return Class.forName(stackTraceElement.getClassName());
                }
            }
        }
        catch (ClassNotFoundException ex) {
            // Swallow and continue
        }
        return null;
    }

可以看到,通过 getStackTrace()方法获取当前线程的执行栈,再通过 getMethodName()获取方法名,判断是否是 main 方法,最后返回 main 方法的所在类。

技术图片

二、SpringApplication运行阶段——事件监听机制

前面我们已经讲了 SpringApplication 的构造方法,这里我们就来讲讲 SpringApplication 的核心,也就是run方法:

public ConfigurableApplicationContext run(String... args) {
        // 这是 Spring 的一个计时器,计算代码的执行时间(ms级别)
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        
        // 这俩变量在后面赋值处进行说明
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        
        // 用来设置java.awt.headless属性值
        configureHeadlessProperty();
        
        // 该对象属于组合模式的实现,核心是内部关联的 SpringApplicationRunListener 集合,SpringApplicationRunListener 是 Spring Boot 的运行时监听器
        SpringApplicationRunListeners listeners = getRunListeners(args);
        // 会在不同的阶段调用对应的方法,这里表示SpringApplication的run方法刚开始执行
        listeners.starting();
        
        try {
        
            // 用来获取 SpringApplication.run(args)传入的参数
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            
            // 获取 properties 配置文件
            ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
            
            // 设置 spring.beaninfo.ignore 的属性值,判断是否跳过搜索BeanInfo类
            configureIgnoreBeanInfo(environment);
            
            // 这里是项目启动时,控制台打印的 Banner
            Banner printedBanner = printBanner(environment);
            
            // 这里就是创建 Spring 应用上下文
            context = createApplicationContext();
            
            // 获取 spring.factories 中key为 SpringBootExceptionReporter 的类名集合
            exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
                    
            // 这里是准备 Spring 应用上下文
            prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            
            // 这里是启动 Spring 应用上下文,底层调用的是 ApplicationContext 的 refresh() 方法,到这里就正式进入了 Spring 的生命周期,同时,SpringBoot的自动装配特性也随之启动
            refreshContext(context);
            
            // 里面是空的,猜测应该是交由开发人员自行扩展
            afterRefresh(context, applicationArguments);
            stopWatch.stop();
            
            // 这里打印启动信息
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
            }
            
            // ApplicationContext 启动时,调用该方法
            listeners.started(context);
            
            // 项目启动后,做的一些操作,开发人员可自行扩展
            callRunners(context, applicationArguments);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, listeners);
            throw new IllegalStateException(ex);
        }
    
        try {
        
            // ApplicationContext 启动完成时,调用该方法
            listeners.running(context);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, exceptionReporters, null);
            throw new IllegalStateException(ex);
        }
        return context;
    }

 在介绍这段代码时,我们先介绍一下监听器模式。

2.1、监听器模式

将一个监听器(listener)与特定的控件(如按钮等)绑定起来,当发生用户点击等事件(Event)时,调用监听器的处理方法,从而响应用户的动作,就叫做事件/监听器模式。

目前spring boot中支持的应用事件类型如下:

技术图片

  • ApplicationFailedEvent:该事件为spring boot启动失败时的操作;
  • ApplicationPreparedEvent:上下文context准备时触发;
  • ApplicationReadyEvent:上下文已经准备完毕的时候触发;
  • ApplicationStartedEvent:spring boot 启动监听类;
  • SpringApplicationEvent:获取SpringApplication;
  • ApplicationEnvironmentPreparedEvent:环境事先准备;

我们1.4节中介绍的ApplicationListener的实现类就是监听器,当某个应用事件发生的时候,就会调用ApplicationListener的onApplicationEvent 方法;

2.2、SpringApplicationRunListeners结构

 // 该对象属于组合模式的实现,核心是内部关联的 SpringApplicationRunListener 集合,SpringApplicationRunListener 是 Spring Boot 的运行时监听器
 SpringApplicationRunListeners listeners = getRunListeners(args);

我们先来看看 SpringApplicationRunListeners 对象,从代码可以看出该对象是由 getRunListeners 方法创建的:

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

可以看到,通过传入的 getSpringFactoriesInstances 方法的返回值,执行 SpringApplicationRunListeners 的构造方法,进行对象的创建。

同样是调用 getSpringFactoriesInstances 方法,不过这里获取的是 key 为 SpringApplicationRunListener 的对象集合.

技术图片

 最后,就是将该集合传入 SpringApplicationRunListeners 的构造方法:

/**
 * A collection of {@link SpringApplicationRunListener}.
 *
 * @author Phillip Webb
 */
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() {
        for (SpringApplicationRunListener listener : this.listeners) {
            listener.starting();
        }
    }
      ...
}

里面是将集合赋值到 listeners 属性,可以看到 SpringApplicationRunListeners 属于组合模式的实现,核心其实是内部关联的 SpringApplicationRunListener 对象集合,当外部调用该阶段方法时,就会迭代执行集合中 SpringApplicationRunListener 对应的方法。所以接下来我们就来讨论 SpringApplicationRunListener。

2.3、监听器触发机制

SpringApplicationRunListener 负责在 SpringBoot 的不同阶段广播相应的事件,然后调用实际的 ApplicationListener 类,在该类的 onApplicationEvent 方法中,根据不同的 Spring Boot 事件执行相应操作。整个过程大概如此:

技术图片

接下来进行详细讨论,先来看看 SpringApplicationRunListener 定义:

public interface SpringApplicationRunListener {

    // 在run()方法开始执行时被调用,表示应用刚刚启动,对应的 Spring Boot 事件为 ApplicationStartingEvent
    void starting();

    // ConfigurableEnvironment 构建完成时调用,对应的 Spring Boot 事件为 ApplicationEnvironmentPreparedEvent
    void environmentPrepared(ConfigurableEnvironment environment);

    // ApplicationContext 构建完成时调用,对应的 Spring Boot 事件为 ApplicationContextInitializedEvent
    void contextPrepared(ConfigurableApplicationContext context);

    // ApplicationContext 完成加载但还未启动时调用,对应的 Spring Boot 事件为 ApplicationPreparedEvent
    void contextLoaded(ConfigurableApplicationContext context);

    // ApplicationContext 已启动,但 callRunners 还未执行时调用,对应的 Spring Boot 事件为 ApplicationStartedEvent
    void started(ConfigurableApplicationContext context);

    // ApplicationContext 启动完毕被调用,对应的 Spring Boot 事件为 ApplicationReadyEvent
    void running(ConfigurableApplicationContext context);

    // 应用出错时被调用,对应的 Spring Boot 事件为 ApplicationFailedEvent
    void failed(ConfigurableApplicationContext context, Throwable exception);

}

我们来看看它的实现类,也就是上面加载的 spring.factories 文件中的 EventPublishingRunListener 类,该类也是 Spring Boot 内建的唯一实现类,具体广播事件的操作在该类中进行,代码如下: 

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();
        for (ApplicationListener<?> listener : application.getListeners()) {
//添加ApplicationListener监听器
this.initialMulticaster.addApplicationListener(listener); } } @Override public void starting() {
//调用广播器来触发事件
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args)); } ... }

可以看到,通过构造方法创建 EventPublishingRunListener 实例的过程中,调用了 getListeners 方法,将 SpringApplication 中所有 ApplicationListener 监听器关联到了 initialMulticaster 属性中。没错,这里的 ApplicationListener 监听器就是在 SpringApplication 准备阶段从 spring.factories 文件加载的 key 为 ApplicationListener 的实现类集合,该实现类集合全部重写了 onApplicationEvent 方法。

2.4、SimpleApplicationEventMulticaster 广播器

这里又引出了另一个类, 也就是 SimpleApplicationEventMulticaster ,该类是 Spring 的事件广播器,也就是通过它来广播各种事件。接着,当外部迭代的执行到 EventPublishingRunListener 的 starting 方法时,会通过 SimpleApplicationEventMulticaster 的 multicastEvent 方法进行事件的广播,这里广播的是 ApplicationStartingEvent 事件,我们进入 multicastEvent 方法:

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {

    ...
    
    @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));
//获取线程池 Executor executor
= getTaskExecutor();
//获取对当前事件感兴趣的监听器列表
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) { if (executor != null) { executor.execute(() -> invokeListener(listener, event)); } else { invokeListener(listener, event); } } } }

通过 getApplicationListeners 方法,根据事件类型返回从上面关联的 ApplicationListener 集合中筛选出匹配的 ApplicationListener 集合:

技术图片

然后依次遍历这些监听器,同步或异步的调用 invokeListener 方法:

/**
     * Invoke the given listener with the given event.
     * @param listener the ApplicationListener to invoke
     * @param event the current event to propagate
     * @since 4.1
     */
    protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
        ErrorHandler errorHandler = getErrorHandler();
        if (errorHandler != null) {
            try {
                doInvokeListener(listener, event);
            }
            catch (Throwable err) {
                errorHandler.handleError(err);
            }
        }
        else {
            doInvokeListener(listener, event);
        }
    }

    @SuppressWarnings({"rawtypes", "unchecked"})
    private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
        try {
            listener.onApplicationEvent(event);
        }
        catch (ClassCastException ex) {
            String msg = ex.getMessage();
            if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
                // Possibly a lambda-defined listener which we could not resolve the generic event type for
                // -> let‘s suppress the exception and just log a debug message.
                Log logger = LogFactory.getLog(getClass());
                if (logger.isTraceEnabled()) {
                    logger.trace("Non-matching event type for listener: " + listener, ex);
                }
            }
            else {
                throw ex;
            }
        }
    }

可以看到,最终调用的是 doInvokeListener 方法,在该方法中执行了 ApplicationListener 的 onApplicationEvent 方法,入参为广播的事件对象。我们就拿其中一个的监听器来看看 onApplicationEvent 中的实现,如 BackgroundPreinitializer 类:

public class BackgroundPreinitializer implements ApplicationListener<SpringApplicationEvent> {

    ...
    
    @Override
    public void onApplicationEvent(SpringApplicationEvent event) {
        if (!Boolean.getBoolean(IGNORE_BACKGROUNDPREINITIALIZER_PROPERTY_NAME)
                && event instanceof ApplicationStartingEvent && preinitializationStarted.compareAndSet(false, true)) {
            performPreinitialization();
        }
        if ((event instanceof ApplicationReadyEvent || event instanceof ApplicationFailedEvent)
                && preinitializationStarted.get()) {
            try {
                preinitializationComplete.await();
            }
            catch (InterruptedException ex) {
                Thread.currentThread().interrupt();
            }
        }
    }
    
    ...
}

在该方法中,通过 instanceof 判断事件的类型,从而进行相应的操作。该监听器主要的操作是新建一个后台线程去执行那些耗时的初始化工作,包括验证器、消息转换器等。LoggingApplicationListener 监听器则是对 Spring Boot 的日志系统做一些初始化的前置操作。另外两个监听器在该阶段无任何操作。 至此,SpringBoot 事件机制的整体流程大概如此,我们简要回顾一下几个核心组件: 

  • SpringApplicationRunListeners:首先,在SpringApplication的run 方法的执行过程中,通过该类在 Spring Boot 不同的阶段调用不同的阶段方法,如在刚启动阶段调用的 starting 方法。
  • SpringApplicationRunListener:而 SpringApplicationRunListeners 属于组合模式的实现,它里面关联了 SpringApplicationRunListener 实现类集合,当外部调用阶段方法时,会迭代执行该集合中的阶段方法。实现类集合是 spring.factories 文件中定义好的类。
  • EventPublishingRunListener:该类是 Spring Boot 内置的 SpringApplicationRunListener 唯一实现类,所以,当外部调用各阶段的方法时,真正执行的是该类中的方法。
  • SimpleApplicationEventMulticaster:在阶段方法中,会通过 Spring 的 SimpleApplicationEventMulticaster 事件广播器,广播各个阶段对应的事件,如这里的 starting 方法广播的事件是 ApplicationStartingEvent。
  • ApplicationListener:最后 ApplicationListener 的实现类也就是 Spring Boot 监听器会监听到广播的事件,根据不同的事件,进行相应的操作。

到这里 Spring Boot 事件监听机制差不多就结束了,值得注意的是 Spring Boot 监听器实现的是 Spring 的 ApplicationListener 类,事件类最终继承的也是 Spring 的 ApplicationEvent 类,所以,Spring Boot 的事件和监听机制都基于 Spring 而实现的。 

三、SpringApplication运行阶段——加载启动参数

当执行完 listeners.starting 方法后,接着进入构造 ApplicationArguments 阶段:

ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

该类是用于简化 Spring Boot 应用启动参数的封装接口,我们启动项目时输入的命令参数会封装在该类中。一种是通过 IDEA 输入的参数,如下:

技术图片

另一种是 springboot jar包运行时传递的参数: cmd中运行java -jar xxx.jar name=张三 pwa=123 。

然后,可以通过 @Autowired 注入 ApplicationArguments 的方式进行使用:

public class Test {

    @Autowired
    private ApplicationArguments applicationArguments;

    public void getArgs() {
        // 获取 args 中的所有 non option 参数
        applicationArguments.getNonOptionArgs();

        // 获取 args 中所有的 option 参数的 name
        applicationArguments.getOptionNames();

        // 获取传递给应用程序的原始未处理参数
        applicationArguments.getSourceArgs();

        // 获取 args 中指定 name 的 option 参数的值
        applicationArguments.getOptionValues("name");

        // 判断从参数中解析的 option 参数是否包含指定名称的选项
        applicationArguments.containsOption("name");
    }
}

四、SpringApplication运行阶段——加载外部化配置

 接着进入构造 ConfigurableEnvironment 的阶段,该类是用来处理我们外部化配置的,如 properties、YAML 等,提供对配置文件的基础操作。当然,它能处理的外部配置可不仅仅如此:

ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);

进入创建该类的 prepareEnvironment 方法:

    private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments) {
        // Create and configure the environment
        ConfigurableEnvironment environment = getOrCreateEnvironment();
        configureEnvironment(environment, applicationArguments.getSourceArgs());
        ConfigurationPropertySources.attach(environment);
        listeners.environmentPrepared(environment);
        bindToSpringApplication(environment);
        if (!this.isCustomEnvironment) {
            environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
                    deduceEnvironmentClass());
        }
        ConfigurationPropertySources.attach(environment);
        return environment;
    }

 

这里通过 getOrCreateEnvironment 方法返回具体的 Environment:

private ConfigurableEnvironment getOrCreateEnvironment() {
        if (this.environment != null) {
            return this.environment;
        }
        switch (this.webApplicationType) {
        case SERVLET:
            return new StandardServletEnvironment();
        case REACTIVE:
            return new StandardReactiveWebEnvironment();
        default:
            return new StandardEnvironment();
        }
    }

可以看到,这里通过 webApplicationType 属性来判断当前应用的类型,有 Servlet 、 Reactive 、 非Web 3种类型,该属性是在SpringApplication 准备阶段确定的,这里我们通常都是 Servlet 类型,返回的是 StandardServletEnvironment 实例。 之后,还调用了 SpringApplicationRunListeners 的 environmentPrepared 阶段方法,表示 ConfigurableEnvironment 构建完成,同时向 Spring Boot 监听器发布 ApplicationEnvironmentPreparedEvent 事件。监听该事件的监听器有: 

技术图片

参考文章:

[1]SpringBoot(一)自动装配基础

[2]SpringBoot(三)SpringApplication启动类准备阶段(转载)

[3]SpringBoot(四)SpringApplication启动类运行阶段(转载)

[4]SpringBoot事件监听机制

Spring Boot -- 启动流程分析一

标签:2-2   gets   种类   versions   extc   stp   web   搜索   如何   

原文地址:https://www.cnblogs.com/zyly/p/13194186.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!