详细分析Springboot启动流程

3552 篇文章 108 订阅

前言

本文会对Springboot启动流程进行详细分析。但是请注意,Springboot启动流程是Springboot的逻辑,请千万不要将Springboot启动流程相关逻辑与Spring的相关逻辑混在一起,比如把Springbean生命周期的逻辑混在Springboot启动流程中,那么整个体系就复杂且混乱了。

所以本文仅重点关注Springboot启动流程,涉及Spring的部分,会略作说明并跳过。

整体的一个结构图如下。

Springboot版本:2.4.1

正文

一. Springboot启动流程图及说明

如下是Springboot的一个启动流程图。

SpringApplication完成初始化后,就会调用SpringApplication对象的run() 方法,该方法就是Springboot启动的入口,也对应着全流程图中的开始。下面给出SpringApplication对象的run() 方法说明,如下所示。

public ConfigurableApplicationContext run(String... args) {
    // 创建StopWatch,用于统计Springboot启动的耗时
    StopWatch stopWatch = new StopWatch();
    // 开始计时
    stopWatch.start();
    DefaultBootstrapContext bootstrapContext = createBootstrapContext();
    ConfigurableApplicationContext context = null;
    configureHeadlessProperty();
    // 获取运行时监听器
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 调用运行时监听器的starting()方法
    // 该方法需要在Springboot一启动时就调用,用于特别早期的初始化
    listeners.starting(bootstrapContext, this.mainApplicationClass);
    try {
        // 获取args参数对象
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        // 读取Springboot配置文件并创建Environment对象
        // 这里创建的Environment对象实际为ConfigurableEnvironment
        ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
        configureIgnoreBeanInfo(environment);
        // 打印Banner图标
        Banner printedBanner = printBanner(environment);
        // 创建ApplicationContext应用行下文,即创建容器
        context = createApplicationContext();
        context.setApplicationStartup(this.applicationStartup);
        // 准备容器
        prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
        // 初始化容器
        refreshContext(context);
        afterRefresh(context, applicationArguments);
        // 停止计时
        stopWatch.stop();
        if (this.logStartupInfo) {
            // 打印启动耗时等信息
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
        }
        // 调用运行时监听器的started()方法
        // 该方法需要在应用程序启动后,CommandLineRunners和ApplicationRunners被调用前执行
        listeners.started(context);
        callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, listeners);
        throw new IllegalStateException(ex);
    }

    try {
        // 调用运行时监听器的running()方法
        // 该方法需要在SpringApplication的run()方法执行完之前被调用
        listeners.running(context);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, null);
        throw new IllegalStateException(ex);
    }
    return context;
}
复制代码

二. SpringApplication的初始化

通常,Springboot应用程序的启动类定义如下。

@SpringBootApplication
public class LearnStartApplication {

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

}
复制代码

SpringApplication的静态run() 方法一路跟进,会发现如下的实现。

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

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

也就是Springboot启动时会先创建SpringApplication,然后再通过SpringApplicationrun() 方法完成启动。所以下面分析一下SpringApplication的初始化逻辑,其构造方法如下所示。

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    // 设置源
    // 通常Springboot的启动类就是源
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    // 推断并设置WEB应用程序类型
    // 根据classpath下的类来推断
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    // 加载并设置Bootstrapper
    this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));
    // 加载并设置初始化器
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 加载并设置应用事件监听器
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 推断并设置应用程序主类的Class对象
    this.mainApplicationClass = deduceMainApplicationClass();
}
复制代码

梳理一下在SpringApplication的构造方法中,做了如下事情。

  1. 设置源。通常,Springboot中的源就是Springboot的启动类;
  2. 设置WEB应用程序类型。通过判断classpath下是否存在某些类,来推断当前WEB应用程序的类型;
  3. 加载并设置BootstrapperApplicationContextInitializerApplicationListener。借助SpringFactoriesLoader基于SPI机制完成BootstrapperApplicationContextInitializerApplicationListener的加载,然后设置到SpringApplication中;
  4. 设置应用程序主类的Class对象。

下面对上述事情进行分析。

1. 设置源

这里的源,也就是Spring容器启动时依赖的初始配置类,在Springboot中,初始配置类通常为启动类。下面可以通过调试看一下primarySources字段的值,如下所示。

可见源就是Springboot的启动类的Class对象。

2. 设置WEB应用程序类型

WebApplicationType#deduceFromClasspath方法如下所示。

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)) {
        // classpath下存在DispatcherHandler,但不存在DispatcherServlet和ServletContainer
        // 则WEN应用程序类型推断为REACTIVE,即响应式WEB应用程序
        return WebApplicationType.REACTIVE;
    }
    for (String className : SERVLET_INDICATOR_CLASSES) {
        if (!ClassUtils.isPresent(className, null)) {
            // 非WEB应用程序
            return WebApplicationType.NONE;
        }
    }
    // 基于Servlet的WEB应用程序
    return WebApplicationType.SERVLET;
}
复制代码

WebApplicationType中预定义了若干种用于判断的类的全限定名,然后在deduceFromClasspath() 方法中使用ClassUtils来判断预定义的类是否存在,通过这样的方式最终可以推断出当前WEB应用程序类型。在示例工程中,如果只引入spring-boot-starter包,那么推断出来的WebApplicationTypeNONE,如下所示。

如果再引入spring-boot-starter-web包,则推断出来的WebApplicationTypeSERVLET,如下所示。

3. 加载并设置Bootstrapper,ApplicationContextInitializer和ApplicationListener

这里主要分析一下是如何加载BootstrapperApplicationContextInitializerApplicationListener的。它们的加载均使用了getSpringFactoriesInstances() 方法,下面看一下实现。

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();
    // 通过SpringFactoriesLoader扫描classpath所有jar包的META-INF目录下的spring.factories文件
    // 将type全限定名对应的全限定名的集合获取到
    Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    // 实例化
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}
复制代码

主要就是基于SpringFactoriesLoader完成加载,加载机制和Springboot中的自动装配是一样,唯一的区别就是自动装配中在spring.factories文件中是根据@EnableAutoConfiguration的全限定名作为key去获取全限定名集合,而在这里是根据BootstrapperApplicationContextInitializerApplicationListener的全限定名作为key去获取全限定名集合,以spring-boot-autoconfigure包中的spring.factories文件为例,说明如下。

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

org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
复制代码

4. 设置应用程序主类的Class对象

获取应用程序主类的Class对象的SpringApplication#deduceMainApplicationClass方法如下所示。

private Class<?> deduceMainApplicationClass() {
    try {
        // 通过RuntimeException获取堆栈
        StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
        for (StackTraceElement stackTraceElement : stackTrace) {
            // 判断堆栈元素的发生方法名是否为main
            if ("main".equals(stackTraceElement.getMethodName())) {
                // 通过反射获取到main方法所在类的Class对象
                return Class.forName(stackTraceElement.getClassName());
            }
        }
    }
    catch (ClassNotFoundException ex) {
        
    }
    return null;
}
复制代码

获取应用程序主类的Class对象是通过堆栈实现的,下面给出调试截图。

三. Springboot事件机制

Springboot启动的一开始,有一步逻辑是获取运行时监听器,最终会获取到一个SpringApplicationRunListeners对象,下面看一下获取运行时监听器的getRunListeners() 方法的实现。

private SpringApplicationRunListeners getRunListeners(String[] args) {
    Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
    // 先基于SpringFactoriesLoader的SPI机制获取SpringApplicationRunListener的实现类集合
    // 然后创建SpringApplicationRunListeners对象
    return new SpringApplicationRunListeners(logger,
            getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),
            this.applicationStartup);
}

SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners,
    ApplicationStartup applicationStartup) {
    // SpringApplicationRunListeners的构造方法中只是进行简单赋值
    this.log = log;
    this.listeners = new ArrayList<>(listeners);
    this.applicationStartup = applicationStartup;
}
复制代码

getRunListeners() 方法中会先基于SpringFactoriesLoaderSPI机制将SpringApplicationRunListener接口的实现类获取出来,在spring-boot包中提供了一个SpringApplicationRunListener接口的实现类,为EventPublishingRunListener,这也是Springboot提供的唯一一个内置运行时监听器,所以通过getRunListeners() 方法获取到的SpringApplicationRunListeners对象中持有一个SpringApplicationRunListener的集合,这个集合中默认情况下一定会包含一个EventPublishingRunListener的对象。

下面再以SpringApplicationRunListenersstarting() 方法为例,分析一下SpringApplicationRunListeners是如何工作的。

void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {
    doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),
            (step) -> {
                if (mainApplicationClass != null) {
                    step.tag("mainApplicationClass", mainApplicationClass.getName());
                }
            });
}

private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction,
        Consumer<StartupStep> stepAction) {
    StartupStep step = this.applicationStartup.start(stepName);
    // 集合中每个运行时监听器都会执行listenerAction函数
    this.listeners.forEach(listenerAction);
    if (stepAction != null) {
        stepAction.accept(step);
    }
        step.end();
}
复制代码

结合SpringApplicationRunListenersstarting()doWithListeners() 方法,可知SpringApplicationRunListeners会将starting() 方法的调用传递给其持有的每个运行时监听器,所以SpringApplicationRunListeners是组合模式的一个应用。

那么Springboot中的事件机制按理应该由Springboot提供的唯一一个运行时监听器EventPublishingRunListener实现。下面分析EventPublishingRunListener的逻辑,还是以EventPublishingRunListenerstarting() 方法为例,进行说明。

public void starting(ConfigurableBootstrapContext bootstrapContext) {
    // 先创建一个ApplicationStartingEvent事件对象
    // 然后调用SimpleApplicationEventMulticaster来发布事件对象
    this.initialMulticaster
            .multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args));
}
复制代码

EventPublishingRunListenerstarting() 方法中会先创建ApplicationStartingEvent事件对象,然后通过EventPublishingRunListener持有的一个SimpleApplicationEventMulticaster对象来发布事件。

那么下面继续分析SimpleApplicationEventMulticaster怎么发布事件,发布给谁,SimpleApplicationEventMulticastermulticastEvent() 方法如下所示。

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

public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    Executor executor = getTaskExecutor();
    // 调用getApplicationListeners()方法将所有适合接收当前事件的ApplicationListener获取出来
    // 然后基于异步或者同步的方式向符合条件的ApplicationListener发布事件
    for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
        if (executor != null) {
            // 异步发布
            executor.execute(() -> invokeListener(listener, event));
        }
        else {
            // 同步发布
            invokeListener(listener, event);
        }
    }
}
复制代码

SimpleApplicationEventMulticastermulticastEvent() 方法中会先将初始化SpringApplication时加载的ApplicationListener获取到,然后遍历其中适合接收当前事件的ApplicationListener,然后异步或者同步的向ApplicationListener发布事件,继续看invokeListener() 方法,如下所示。

protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
    ErrorHandler errorHandler = getErrorHandler();
    // 实际调用doInvokeListener()方法来向ApplicationListener发布事件
    if (errorHandler != null) {
        try {
            doInvokeListener(listener, event);
        }
        catch (Throwable err) {
            errorHandler.handleError(err);
        }
    }
    else {
        doInvokeListener(listener, event);
    }
}

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
    try {
        // ApplicationListener接口的实现类都会实现onApplicationEvent()方法
        // 在onApplicationEvent()方法中会处理当前接收到的事件
        listener.onApplicationEvent(event);
    }
    catch (ClassCastException ex) {
        String msg = ex.getMessage();
        if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
            Log logger = LogFactory.getLog(getClass());
            if (logger.isTraceEnabled()) {
                logger.trace("Non-matching event type for listener: " + listener, ex);
            }
        }
        else {
            throw ex;
        }
    }
}
复制代码

SimpleApplicationEventMulticasterinvokeListener() 方法中实际会调用到doInvokeListener() 方法,在doInvokeListener() 方法中会调用ApplicationListeneronApplicationEvent() 方法,所以在这里就调用到了ApplicationListener实际处理事件的逻辑。

现在对Springboot中的事件监听机制进行小结。

  1. SpringApplication初始化时会加载所有的ApplicationListener
  2. Springboot启动的一开始,会调用到SpringApplication#getRunListeners方法创建一个SpringApplicationRunListeners对象;
  3. SpringApplicationRunListeners是组合模式的应用,其持有一个SpringApplicationRunListener的集合,集合中默认会存在一个Springboot提供的SpringApplicationRunListener的实现类EventPublishingRunListener,所有对SpringApplicationRunListeners的调用请求都会被传递给集合中的每一个SpringApplicationRunListener
  4. EventPublishingRunListener中持有一个事件发布器SimpleApplicationEventMulticaster,在EventPublishingRunListener的构造函数中,会将SimpleApplicationEventMulticaster创建出来并将SpringApplication中的所有ApplicationListener设置给SimpleApplicationEventMulticaster。当EventPublishingRunListenerstarting()environmentPrepared() 等方法被调用时,EventPublishingRunListener会创建对应的事件(ApplicationStartingEventApplicationEnvironmentPreparedEvent)并通过SimpleApplicationEventMulticaster向适合接收当前事件的ApplicationListener发布;
  5. SimpleApplicationEventMulticaster发布事件时,会先获取出所有适合接收当前事件的ApplicationListener,然后调用这些ApplicationListeneronApplicationEvent() 方法,每一个ApplicationListener会在其实现的onApplicationEvent() 方法中完成对事件的处理。

图示如下。

四. 外部化配置加载

Springboot启动时,会在调用运行时监听器的starting() 方法后创建DefaultApplicationArguments对象,然后就会开始加载外部化配置。

外部化配置通常由application.yml文件(或者application.properties文件)提供,在application.yml文件中添加配置项也是最常用的外部化配置方式。实际上为Springboot应用程序添加外部化配置的方式还有许多种,可以参考Springboot-外部化配置,下面是较为常用的外部化配置方式的优先级(由上到下优先级逐渐降低)。

  1. 命令行参数,即Command line arguments
  2. JAVA系统属性,即Java System propertiesSystem#getProperties);
  3. 操作系统环境变量,即OS environment variables
  4. 配置数据文件(例如application.yml文件),即Config datasuch as application.properties files
    1. jar包外指定了profile的配置数据文件:application-{profile}.yml
    2. jar包外的配置数据文件:application.yml
    3. jar包内指定了profile的配置数据文件:application-{profile}.yml
    4. jar包内的配置数据文件:application.yml
  5. 作用在由@Configuration注解修饰的类上的@PropertySource注解,即@PropertySource annotations on your @Configuration classes
  6. 默认属性,即Default propertiesspecified by setting SpringApplication#setDefaultProperties)。

Springboot在启动过程中的SpringApplication#prepareEnvironment方法中会加载上述的外部化配置为EnvironmentEnvironmentSpringboot外部化配置的入口,通过Environment可以获取到Springboot加载的所有外部化配置。

下图给出了SpringApplication#prepareEnvironment方法执行完后Environment的详细信息。

可见Environment的实际类型为StandardServletEnvironment,这是和Springboot的应用程序类型挂钩,这点后面再说。StandardServletEnvironment内部持有一个MutablePropertySources对象,该对象持有一个PropertySource的集合,Springboot加载的每一种外部化配置都会最终被解析为一个PropertySource的实现类并存放在MutablePropertySourcesPropertySource集合中,PropertySource就是每一种外部化配置源在Springboot中的体现,其提供了对外部化配置的各种操作。根据上图为例,给出一部分外部化配置源与PropertySource的实现类的对应关系。

外部化配置PropertySource
命令行参数SimpleCommandLinePropertySource
JAVA系统属性PropertiesPropertySource
操作系统环境变量OriginAwareSystemEnvironmentPropertySource
配置数据文件OriginTrackedMapPropertySource

启动程序时通过命令行指定的应用程序参数(args)会被先创建为DefaultApplicationArguments对象,然后再被解析为SimpleCommandLinePropertySource,例如通过IDEA进行如下配置。

那么对应的SimpleCommandLinePropertySource如下所示。

如果在resources目录创建一个application.yml文件,且内容如下。

server:
  port: 8080
  address: 127.0.0.1
复制代码

那么对应的OriginTrackedMapPropertySource如下所示。

下面将从SpringApplication#prepareEnvironment方法为入口,对Springboot启动流程中的外部化配置加载进行简要分析。SpringApplication#prepareEnvironment方法如下所示。

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
        DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
    // 创建ConfigurableEnvironment对象
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    // 将命令行参数解析为PropertySource并加载到Environment中
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    ConfigurationPropertySources.attach(environment);
    // 发布Environment准备好的事件
    // 进一步加载更多的外部化配置到Environment中
    listeners.environmentPrepared(bootstrapContext, environment);
    DefaultPropertiesPropertySource.moveToEnd(environment);
    configureAdditionalProfiles(environment);
    bindToSpringApplication(environment);
    if (!this.isCustomEnvironment) {
        environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
                deduceEnvironmentClass());
    }
    ConfigurationPropertySources.attach(environment);
    return environment;
}
复制代码

SpringApplication#prepareEnvironment方法中,首先会调用getOrCreateEnvironment() 方法创建ConfigurableEnvironment对象,创建出来的ConfigurableEnvironment的实际类型会根据SpringApplication初始化时推断出来的WEB应用程序类型而定,如果WEB应用程序类型为SERVLET,则创建出来的ConfigurableEnvironment实际类型为StandardServletEnvironment,并且在初始化StandardServletEnvironment时还会一并将JAVA系统属性和操作系统环境变量这两个外部化配置加载到StandardServletEnvironment中。

在创建好StandardServletEnvironment后,会再将命令行参数解析为PropertySource并加载到StandardServletEnvironment中,随后就通过Springboot事件机制向ApplicationListener发布Environment准备好的事件,这里会接收该事件的ApplicationListenerEnvironmentPostProcessorApplicationListener2.4.0版本以前为ConfigFileApplicationListener,该监听器从2.4.0版本起被废弃)。

接下来先分析一下getOrCreateEnvironment() 方法的实现。

private ConfigurableEnvironment getOrCreateEnvironment() {
    if (this.environment != null) {
        return this.environment;
    }
    // 根据WEB应用程序类型创建不同的ConfigurableEnvironment
    switch (this.webApplicationType) {
    case SERVLET:
        return new StandardServletEnvironment();
    case REACTIVE:
        return new StandardReactiveWebEnvironment();
    default:
        return new StandardEnvironment();
    }
}
复制代码

StandardServletEnvironment的类图如下所示。

StandardServletEnvironment在初始化时会先调用到其父类AbstractEnvironment的构造方法,如下所示。

public AbstractEnvironment() {
    customizePropertySources(this.propertySources);
}
复制代码

实际会调用到StandardServletEnvironment实现的customizePropertySources() 方法,如下所示。

protected void customizePropertySources(MutablePropertySources propertySources) {
    // Servlet相关的外部化配置的加载
    propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
    propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
    if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
        propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));
    }
    // 调用父类StandardEnvironment实现的customizePropertySources()方法
    super.customizePropertySources(propertySources);
}
复制代码

继续看StandardEnvironment实现的customizePropertySources() 方法,如下所示。

protected void customizePropertySources(MutablePropertySources propertySources) {
    // 将JAVA系统属性解析为PropertiesPropertySource,并加载到PropertySource集合中
    propertySources.addLast(
            new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
    // 将操作系统环境变量解析为SystemEnvironmentPropertySource,并加载到PropertySource集合中
    propertySources.addLast(
            new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}
复制代码

到这里getOrCreateEnvironment() 方法做的事情分析完毕。

下面再分析一下EnvironmentPostProcessorApplicationListener接收到Environment准备好的事件(ApplicationEnvironmentPreparedEvent)后的执行流程,EnvironmentPostProcessorApplicationListeneronApplicationEvent() 方法如下所示。

public void onApplicationEvent(ApplicationEvent event) {
    if (event instanceof ApplicationEnvironmentPreparedEvent) {
        // 事件event的实际类型为ApplicationEnvironmentPreparedEvent
        onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
    }
    if (event instanceof ApplicationPreparedEvent) {
        onApplicationPreparedEvent((ApplicationPreparedEvent) event);
    }
    if (event instanceof ApplicationFailedEvent) {
        onApplicationFailedEvent((ApplicationFailedEvent) event);
    }
}
复制代码

继续看onApplicationEnvironmentPreparedEvent() 方法。

private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
    ConfigurableEnvironment environment = event.getEnvironment();
    SpringApplication application = event.getSpringApplication();
    // 遍历所有EnvironmentPostProcessor的实现类,每个EnvironmentPostProcessor的实现类都会对相应的外部化配置做后置处理
    // 处理配置数据文件的EnvironmentPostProcessor的实际类型为ConfigDataEnvironmentPostProcessor
    for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(event.getBootstrapContext())) {
        postProcessor.postProcessEnvironment(environment, application);
    }
}
复制代码

EnvironmentPostProcessor的继承树如下所示。

每一个EnvironmentPostProcessor的实现类都会对相应的外部化配置做后置处理,例如RandomValuePropertySourceEnvironmentPostProcessor会加载一个RandomValuePropertySourceEnvironment中,SystemEnvironmentPropertySourceEnvironmentPostProcessor会将Environment中的SystemEnvironmentPropertySource替换为SystemEnvironmentPropertySource的子类OriginAwareSystemEnvironmentPropertySource

EnvironmentPostProcessor的实现类中,有一个较为重要的实现类叫做ConfigDataEnvironmentPostProcessor,其可以将配置数据文件(application.yml等)加载为OriginTrackedMapPropertySource并设置到Environment中。

至此,Springboot启动流程中的外部化配置加载分析完毕,下面是小结。

  1. 首先会根据初始化SpringApplication时推断出来的WEB应用程序类型创建不同的Environment,例如WEB应用程序类型为SERVLET时,创建的Environment的实际类型为StandardServletEnvironment
  2. 在创建StandardServletEnvironment时,就会向StandardServletEnvironment中加载一部分外部化配置,在这个阶段加载的外部化配置主要是JAVA系统属性和操作系统环境变量;
  3. 在创建StandardServletEnvironment后,还会通过Springboot事件机制向EnvironmentPostProcessorApplicationListener发布ApplicationEnvironmentPreparedEvent事件,EnvironmentPostProcessorApplicationListener中收到ApplicationEnvironmentPreparedEvent事件后,会调用EnvironmentPostProcessor的实现类完成对Environment的后置处理,即会继续向Environment加载外部化配置,配置数据文件(application.yml等)的加载就在这个阶段完成;
  4. StandardServletEnvironment内部持有一个MutablePropertySources对象,该对象持有一个PropertySource的集合,Springboot加载的每一种外部化配置都会最终被解析为一个PropertySource的实现类并存放在MutablePropertySourcesPropertySource集合中,PropertySource就是每一种外部化配置源在Springboot中的体现,其提供了对外部化配置的各种操作。

总结

Springboot启动时,第一件重要事件就是初始化SpringApplication,并主要完成如下事情。

  1. 设置源。实际就是设置Spring容器启动时依赖的初始配置类,也就是Springboot中的启动类;
  2. 设置WEB应用程序类型。例如可以是SERVLETREACTIVE等;
  3. 加载并设置BootstrapperApplicationContextInitializerApplicationListener
  4. 设置应用程序主类的Class对象。

然后Springboot启动时还会开启事件机制,主要就是通过运行时监听器EventPublishingRunListener创建事件并分发给对应的ApplicationListener

再然后会加载外部化配置,也就是得到很重要的Environment对象,通过Environment对象就可以拿到Springboot加载的所有外部化配置。

再然后会完成容器刷新,也就是执行Spring中的各种扩展点,初始化各种bean,这部分逻辑属于是Spring的逻辑,故本文并未详细介绍。除此之外,在容器刷新时,还会完成WEB容器的启动,例如启动Springboot内嵌的Tomcat,这部分内容比较多,会在后面单独进行分析。

最后,Springboot在整个启动流程中,会借助事件机制来发布各种事件,发布事件就是借助于上述提到的EventPublishingRunListener,这是一个运行时监听器,是Springboot中提供的监听器,不要和Spring中的ApplicationListener混淆了。

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot是一个开源的Java框架,用于构建独立的、可执行的、生产级的Spring应用程序。它提供了一个快速、简单的方式来开发和部署应用程序。而在Spring Boot的启动过程中,有以下几个主要的步骤: 1. 加载启动类:Spring Boot应用程序的启动类通常是一个带有`@SpringBootApplication`注解的Java类。在应用程序启动时,会通过`main`方法加载这个启动类。 2. 创建Spring Application对象:Spring Boot会创建一个`SpringApplication`对象,用于启动应用程序。`SpringApplication`是Spring Boot框架的核心类,它负责管理整个应用程序的生命周期。 3. 解析配置信息:在启动过程中,`SpringApplication`会解析`application.properties`或`application.yaml`文件中的配置信息,并将其加载到Spring环境中。这些配置信息可以用来配置应用程序的各个方面,如数据库连接、日志级别等。 4. 创建并配置Spring容器:Spring Boot使用Spring容器来管理应用程序中的各个Bean。在启动过程中,`SpringApplication`会根据配置信息创建并配置一个Spring容器,该容器负责加载和管理应用程序中的所有Bean。 5. 执行自定义逻辑:在Spring Boot的启动过程中,可以添加自定义的逻辑。例如,可以通过实现`CommandLineRunner`接口来在应用程序启动后执行一些初始化操作。 6. 启动应用程序:完成上述步骤后,`SpringApplication`会启动应用程序,并通过Servlet容器(如Tomcat、Jetty等)监听端口,开始接收和处理HTTP请求。 总体而言,Spring Boot启动流程是一个通过加载启动类、解析配置信息、创建和配置Spring容器的过程。通过Spring Boot的自动配置和快速启动能力,开发者可以更加方便地构建和部署Spring应用程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值