SpringApplication.run 到底做了什么?

用过 SpringBoot 的同学都知道,其程序的启动类是在一个main方法中调用SpringApplication.run方法执行的,如:

@SpringBootApplication
public class SpringApplicationBootstrap {

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

那么,这里面到底做了什么呢?本篇文章将深入源码,带你一起探究底层实现。

SpringApplication 初始化阶段

进入到SpringApplication.run方法,其首先会创建一个SpringApplication对象,我们看其构造函数:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        // primarySources 为 run 方法传入的引导类
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        // 推断web应用类
        this.webApplicationType = deduceWebApplicationType();
        // 初始化 initializers 属性
        setInitializers((Collection) getSpringFactoriesInstances(
                ApplicationContextInitializer.class));
        // 初始化监听器
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        // 推断应用引导类
        this.mainApplicationClass = deduceMainApplicationClass();
    }

我们先看推断web应用类的方法deduceWebApplicationType()

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

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

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

    private WebApplicationType deduceWebApplicationType() {
        if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
                && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {
            return WebApplicationType.REACTIVE;
        }
        for (String className : WEB_ENVIRONMENT_CLASSES) {
            if (!ClassUtils.isPresent(className, null)) {
                return WebApplicationType.NONE;
            }
        }
        return WebApplicationType.SERVLET;
    }

根据 classpath 下是否存在某个特征类来决定是否应该创建一个为 Web 应用使用的ApplicationContext类型。具体判断为:

如果仅存在 Reactive 的包,则为WebApplicationType.REACTIVE类型;
如果 Servlet 和 Reactive的包都不存在,则为WebApplicationType.NONE类型;
其他情况都为WebApplicationType.SERVLET类型。

接下来我们看初始化initializers属性的过程,其通过getSpringFactoriesInstances(ApplicationContextInitializer.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;
    }

该方法流程为:

  1. 通过SpringFactoriesLoader.loadFactoryNames(type, classLoader)方法,在 META-INF/spring.factories 文件下查找ApplicationContextInitializer类型对应的资源名称。
  2. 实例化上面的资源信息(初始化器)。
  3. 对初始化器根据Ordered接口或者@Order注解进行排序。

同理,初始化listeners监听器也是类似的,这里不再累赘。

SpringApplication 初始化阶段的最后一步是推断引导类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;
    }

其将调用栈中main方法所在的类作为引导类。

SpringApplication 运行阶段

SpringApplication 运行阶段属于核心过程,完全围绕 run(String...) 方法展开。该过程结合初始化阶段完成的状态,进一步完善运行时所需要准备的资源,随后启动 Spring 应用上下文。在此期间伴随着 Spring Boot 和 Spring 事件的触发,形成完整的 SpringApplication 生命周期。因此,下面将围绕以下三个子议题进行讨论。

  • SpringApplication 准备阶段
  • ApplicationContext 启动阶段
  • ApplicationContext 启动后阶段
1. SpringApplication 准备阶段

本阶段属于 ApplicationContext 启动阶段的前一阶段,设计的范围从 run(String...)方法调用开始,到refreshContext(ConfigurableApplicationContext)调用前:

public ConfigurableApplicationContext run(String... args) {
        //记录程序运行时间
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        //Spring 应用的上下文
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        configureHeadlessProperty();
        // 获取 SpringApplicationRunListeners
        SpringApplicationRunListeners listeners = getRunListeners(args);
        listeners.starting();
        try {
            // 创建 ApplicationArguments 对象
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                    args);
            // 加载属性配置
            ConfigurableEnvironment environment = prepareEnvironment(listeners,
                    applicationArguments);
            // 处理需要忽略的Bean
            configureIgnoreBeanInfo(environment);
            // 打印 banner
            Banner printedBanner = printBanner(environment);
            // 创建 Spring 应用上下文
            context = createApplicationContext();
            // 实例化 SpringBootExceptionReporter,用来报告关于启动过程中的错误
            exceptionReporters = getSpringFactoriesInstances(
                    SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
            // 应用上下文的准备阶段
            prepareContext(context, environment, listeners, applicationArguments,
                    printedBanner);
            // 刷新应用上下文(自动装配,初始化 IOC 容器)
            refreshContext(context);
            ...
        }
  }
  • getRunListeners(args) 方法:
private SpringApplicationRunListeners getRunListeners(String[] args) {
        Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
        return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
                SpringApplicationRunListener.class, types, this, args));
    }

该方法会通过getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args)方法,获取 META-INF/spring.factories 文件下SpringApplicationRunListener对应的资源,并且实例化这些资源:

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

我们发现,其有且仅有一个实现类EventPublishingRunListener

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()) {
            this.initialMulticaster.addApplicationListener(listener);
        }
    }

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

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

    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {
        this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
                this.application, this.args, environment));
    }

    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {

    }

    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {
        for (ApplicationListener<?> listener : this.application.getListeners()) {
            if (listener instanceof ApplicationContextAware) {
                ((ApplicationContextAware) listener).setApplicationContext(context);
            }
            context.addApplicationListener(listener);
        }
        this.initialMulticaster.multicastEvent(
                new ApplicationPreparedEvent(this.application, this.args, context));
    }

    @Override
    public void started(ConfigurableApplicationContext context) {
        context.publishEvent(
                new ApplicationStartedEvent(this.application, this.args, context));
    }

    @Override
    public void running(ConfigurableApplicationContext context) {
        context.publishEvent(
                new ApplicationReadyEvent(this.application, this.args, context));
    }

    @Override
    public void failed(ConfigurableApplicationContext context, Throwable exception) {
        ApplicationFailedEvent event = new ApplicationFailedEvent(this.application,
                this.args, context, exception);
        if (context != null && context.isActive()) {
            // Listeners have been registered to the application context so we should
            // use it at this point if we can
            context.publishEvent(event);
        }
        else {
            // An inactive context may not have a multicaster so we use our multicaster to
            // call all of the context's listeners instead
            if (context instanceof AbstractApplicationContext) {
                for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
                        .getApplicationListeners()) {
                    this.initialMulticaster.addApplicationListener(listener);
                }
            }
            this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
            this.initialMulticaster.multicastEvent(event);
        }
    }
    ...

}

在实例化EventPublishingRunListener的过程中,会给它最重要的属性initialMulticaster赋值,其类型是SimpleApplicationEventMulticaster。接着遍历 SpringApplication 初始化阶段的listeners监听器集合,将监听器存入其关联的ListenerRetriever#applicationListeners属性中。

了解 Spring 事件监听机制的同学应该对SimpleApplicationEventMulticaster不陌生,它是ApplicationEvent事件的发布者。Spring Boot 的事件监听机制也是如出一辙,具体可参考我的另一篇文章 深入理解 Spring 的事件发布监听机制

于是接下来调用listeners.starting()方法就会通过其内部的initialMulticaster属性发布ApplicationStartingEvent事件。

  • prepareEnvironment(listeners,applicationArguments) 方法:

加载属性配置。执行完成后,所有的environment的属性都会加载进来,包括 application.properties 和外部的属性配置。

private ConfigurableEnvironment prepareEnvironment(
            SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments) {
        // 创建 ConfigurableEnvironment 对象
        ConfigurableEnvironment environment = getOrCreateEnvironment();
        // 配置 ConfigurableEnvironment
        configureEnvironment(environment, applicationArguments.getSourceArgs());
        // 发布 ApplicationEnvironmentPreparedEvent 事件
        listeners.environmentPrepared(environment);
        // 将 ConfigurableEnvironment 绑定到 SpringApplication 中
        bindToSpringApplication(environment);
        if (this.webApplicationType == WebApplicationType.NONE) {
            environment = new EnvironmentConverter(getClassLoader())
                    .convertToStandardEnvironmentIfNecessary(environment);
        }
        ConfigurationPropertySources.attach(environment);
        return environment;
    }

大致流程为:

  1. 创建ConfigurableEnvironment对象。
  2. 配置environment变量。
  3. 发布ApplicationEnvironmentPreparedEvent事件。
    其对应的监听器为ConfigFileApplicationListener,当接收到上面的事件时,加载并实例化 “META-INF/spring.factories” 文件中EnvironmentPostProcessor类型的实现类,遍历并执行其postProcessEnvironment方法。值得注意的是,ConfigFileApplicationListener自身也是EnvironmentPostProcessor的实现类,于是也会执行其postProcessEnvironment方法:
public void postProcessEnvironment(ConfigurableEnvironment environment,
            SpringApplication application) {
        // 将配置文件信息添加到 environment 中
        addPropertySources(environment, application.getResourceLoader());
        configureIgnoreBeanInfo(environment);
        // 将 environment 绑定到 Spring 应用上下文中
        bindToSpringApplication(environment, application);
    }

该方法的目的是,加载配置文件信息至enviroment,并将enviroment绑定到 Spring 应用上下文中。

当我们需要在配置文件加载完成之后做一些事情的话,我们就可以自定义一个EnvironmentPostProcessor的实现类,操作逻辑写在postProcessEnvironment方法中,当然,别忘了在你的 “META-INF/spring.factories” 文件中加上配置。

  1. 绑定environmentSpringApplication上。
  • createApplicationContext 方法

该方法会根据webApplicationType类型,创建不同的ConfigurableApplicationContextSpring 应用上下文:

protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                switch (this.webApplicationType) {
                case SERVLET:
                    contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
                    break;
                case REACTIVE:
                    contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                    break;
                default:
                    contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
                }
            }
            catch (ClassNotFoundException ex) {
                throw new IllegalStateException(
                        "Unable create a default ApplicationContext, "
                                + "please specify an ApplicationContextClass",
                        ex);
            }
        }
        return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
    }

我们以 SERVLET 类型为例,它会创建AnnotationConfigServletWebServerApplicationContext应用上下文实例。

  • 获取 Spring 异常报告器
    getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context)方法,获取 META-INF/spring.factories 文件下类型为SpringBootExceptionReporter的资源实例:
# Error Reporters
org.springframework.boot.SpringBootExceptionReporter=\
org.springframework.boot.diagnostics.FailureAnalyzers

其实现类有且仅有一个,即FailureAnalyzers

  • prepareContext 方法
    Spring 应用上下文启动前的准备工作:
private void prepareContext(ConfigurableApplicationContext context,
            ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments, Banner printedBanner) {
        //设置 context 的 environment 属性
        context.setEnvironment(environment);
        // Spring 应用上下文的后置处理
        postProcessApplicationContext(context);
        // 运用 Spring 应用上下文初始化器
        applyInitializers(context);
        listeners.contextPrepared(context);
        if (this.logStartupInfo) {
            logStartupInfo(context.getParent() == null);
            logStartupProfileInfo(context);
        }

        // Add boot specific singleton beans
        context.getBeanFactory().registerSingleton("springApplicationArguments",
                applicationArguments);
        if (printedBanner != null) {
            context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
        }

        // Load the sources
        Set<Object> sources = getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        // 加载 BeanDefinition
        load(context, sources.toArray(new Object[0]));
        listeners.contextLoaded(context);
    }

大致流程为:

  1. context的属性做赋值,如设置环境变量,调用初始化器来初始化context
  2. 获取所有配置源信息,包括 Configuration Class、类名、包名及Spring XML 配置资源路径信息。
  3. 加载 Spring 应用上下文配置源。将BeanDefinition加载到context中。
  4. 发布上下文已准备事件ApplicationPreparedEvent

这里,我们要着重看第三步load(context, sources.toArray(new Object[0])):

protected void load(ApplicationContext context, Object[] sources) {
        if (logger.isDebugEnabled()) {
            logger.debug(
                    "Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
        }
        BeanDefinitionLoader loader = createBeanDefinitionLoader(
                getBeanDefinitionRegistry(context), sources);
        if (this.beanNameGenerator != null) {
            loader.setBeanNameGenerator(this.beanNameGenerator);
        }
        if (this.resourceLoader != null) {
            loader.setResourceLoader(this.resourceLoader);
        }
        if (this.environment != null) {
            loader.setEnvironment(this.environment);
        }
        loader.load();
    }

该方法将 Spring 应用上下文转载的任务交给了BeanDefinitionLoader:

class BeanDefinitionLoader {

    private final Object[] sources;

    private final AnnotatedBeanDefinitionReader annotatedReader;

    private final XmlBeanDefinitionReader xmlReader;

    private BeanDefinitionReader groovyReader;

    private final ClassPathBeanDefinitionScanner scanner;

    private ResourceLoader resourceLoader;
    ...
}

BeanDefinitionLoader组合了多个属性,第一个属性为SpringApplication#getAllSources()方法返回值,而属性annotatedReaderxmlReadergroovyReader分别为注解驱动实现AnnotatedBeanDefinitionReader、XML 配置实现XmlBeanDefinitionReader和 Groovy 实现GroovyBeanDefinitionReader。其中AnnotatedBeanDefinitionReaderClassPathBeanDefinitionScanner配合,形成AnnotationConfigApplicationContext扫描和注册配置类的基础,随后这些配置类被解析为 Bean 定义BeanDefinition

public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {

    private final AnnotatedBeanDefinitionReader reader;

    private final ClassPathBeanDefinitionScanner scanner;


    /**
     * Create a new AnnotationConfigApplicationContext that needs to be populated
     * through {@link #register} calls and then manually {@linkplain #refresh refreshed}.
     */
    public AnnotationConfigApplicationContext() {
        this.reader = new AnnotatedBeanDefinitionReader(this);
        this.scanner = new ClassPathBeanDefinitionScanner(this);
    }

    ...

    public void register(Class<?>... annotatedClasses) {
        Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified");
        this.reader.register(annotatedClasses);
    }

    public void scan(String... basePackages) {
        Assert.notEmpty(basePackages, "At least one base package must be specified");
        this.scanner.scan(basePackages);
    }
    ...
}

不难看出,Spring Boot 中的BeanDefinitionLoader是以上BeanDefinition读取的综合实现。当其load()方法调用时,这些BeanDefinitionReader类型的属性各司其职,为 Spring 应用上下文从不同的配置源装载 Spring Bean 定义(BeanDefinition)。
需要注意的是,load()方法仅仅是装载SpringApplication#primarySources的资源,即启动类的资源,而不会加载其他配置源的BeanDefinition

public Set<Object> getAllSources() {
        Set<Object> allSources = new LinkedHashSet<>();
        if (!CollectionUtils.isEmpty(this.primarySources)) {
            allSources.addAll(this.primarySources);
        }
        if (!CollectionUtils.isEmpty(this.sources)) {
            allSources.addAll(this.sources);
        }
        return Collections.unmodifiableSet(allSources);
    }

上面的SpringApplication#sources是没有值的,于是只会加载启动类的资源,其他配置源的加载操作是在 ApplicationContext 启动阶段的AbstractApplicationContext#invokeBeanFactoryPostProcessors的方法中执行的,下面会讲到。

装载完启动类的BeanDefinition到 Spring 应用上下文之后,就调用listeners.contextLoaded(context)方法,发布 Spring 应用上下文已准备ApplicationPreparedEvent事件,以结束 SpringApplication 准备阶段。

2. ApplicationContext 启动阶段

本阶段的执行由refreshContext(ConfigurableApplicationContext)完成,其核心方法是AbstractApplicationContext#refresh:

public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);

                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.
                initMessageSource();

                // Initialize event multicaster for this context.
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.
                onRefresh();

                // Check for listener beans and register them.
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                finishBeanFactoryInitialization(beanFactory);

                // Last step: publish corresponding event.
                finishRefresh();
            }

            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }

                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();

                // Reset 'active' flag.
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            }

            finally {
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                resetCommonCaches();
            }
        }
    }

随着该方法的执行,Spring Boot 核心特性也随之启动,如组件自动装配、嵌入式容器启动。了解过 Spring Boot 自动装配机制的同学应该知道(如不清楚,请参考我的另一篇文章 Spring Boot 自动装配),在根据应用类型创建不同的 Spring 应用上下文的方法createApplicationContext()中,会实例化AnnotatedBeanDefinitionReader对象,该对象的构造方法中,会将ConfigurationClassPostProcessor封装成 Spring Bean 定义(BeanDefinition),并将其注入到 Ioc 容器DefaultListableBeanFactory(其实现了BeanDefinitionRegistry,具有注入BeanDefinition的功能)中。于是,在该阶段的invokeBeanFactoryPostProcessors(beanFactory)方法中,就会取出ConfigurationClassPostProcessor对象,随后调用其postProcessBeanFactory(beanFactory)方法进行装配工作。

  • AbstractApplicationContext#postProcessBeanFactory
    对于 Web 类型为 Servlet 时,这里的AbstractApplicationContextAnnotationConfigServletWebServerApplicationContext,它除了添加beanPostProcessor之外,还有加载 Spring Bean 定义(BeanDefinition)到 Spring 上下文的操作。
    @Override
    protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {

        super.postProcessBeanFactory(beanFactory);
        if (this.basePackages != null && this.basePackages.length > 0) {
            this.scanner.scan(this.basePackages);
        }
        if (!this.annotatedClasses.isEmpty()) {
            this.reader.register(ClassUtils.toClassArray(this.annotatedClasses));
        }
    }

这里似乎和我们上文讲的 Spring Bean 的加载操作是在invokeBeanFactoryPostProcessors有冲突,其实在判断 Web 类型时,生成的AbstractApplicationContext实例是无参构造,以AnnotationConfigServletWebServerApplicationContext为例:

public AnnotationConfigServletWebServerApplicationContext() {
        this.reader = new AnnotatedBeanDefinitionReader(this);
        this.scanner = new ClassPathBeanDefinitionScanner(this);
    }

AnnotationConfigServletWebServerApplicationContext#annotatedClassesAnnotationConfigServletWebServerApplicationContext#basePackages的属性值都为null。故不会走postProcessBeanFactory中的加载 Spring bean 的操作。

  • invokeBeanFactoryPostProcessors(beanFactory)
    这里才是真正将 Spring Bean 定义BeanDefinition加载到 Spring 上下文的操作,同时也是激活自动装配的入口。
  • registerBeanPostProcessors(beanFactory)
    注册 BeanPostProcessor,用来干预 Spring Bean 的初始化操作,在 Spring Bean 的初始化之后,做一些额外的操作。
  • finishBeanFactoryInitialization(beanFactory)
    实例化容器中未设置懒加载的类。
3. ApplicationContext 启动后阶段

实际上,SpringApplication#afterRefresh方法并未给 Spring 应用上下文启动后阶段提供实现,而是将其交给开发人员自行扩展:

protected void afterRefresh(ConfigurableApplicationContext context,
            ApplicationArguments args) {
    }

所有,直接跳过该步,接下来调用listeners.started(context)方法,发布 Spring 应用上下文已启动ApplicationStartedEvent事件。

该阶段最后,调用callRunners(context, applicationArguments)方法,来调用实现了CommandLineRunner或者ApplicationRunner接口的类的 run 方法,得以满足需要在 Spring 应用上下文完全准备完毕后,执行一些操作的场景。

private void callRunners(ApplicationContext context, ApplicationArguments args) {
        List<Object> runners = new ArrayList<>();
        runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
        runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
        AnnotationAwareOrderComparator.sort(runners);
        for (Object runner : new LinkedHashSet<>(runners)) {
            if (runner instanceof ApplicationRunner) {
                callRunner((ApplicationRunner) runner, args);
            }
            if (runner instanceof CommandLineRunner) {
                callRunner((CommandLineRunner) runner, args);
            }
        }
    }

总结

至此,SpringApplciation.run 方法的执行流程已经讲解完毕。下面我们来整理一下大体的步骤:

  1. 初始化 SpringApplication 实例:决定web应用类型、加载初始化器和监听器、推断 main 方法的定义类。
  2. 通过 SpringFactoriesLoader 加载的 SpringApplicationRunListener,调用它们的 started 方法。
  3. 创建并配置当前 Spring Boot 应用将要使用的 Environment,如 applocation.properties 文件和外部配置。
  4. 根据 Web 服务类型创建不同的 Spring 应用上下文,并将之前准备好的 Environment 设置给 Spring 应用上下文 ApplicationContext 使用。
  5. 遍历初始化器,对 ApplicationContext 进行初始化操作。
  6. 初始化上下文 refresh(),在invokeBeanFactoryPostProcessors中,加载所有资源,如 Configuration Class、类名、包名以及 Spring XML 配置资源路径,将所有 BeanDefinition 加载至 ApplicationContext,并进行自动装配,初始化 Ioc 容器等操作。
  7. 寻找当前 ApplicationContext 中是否注册有 CommandLineRunner 或者 ApplicationRunner,如果有,则遍历执行它们。


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容