springboot源碼分析系列(二)--SpringApplication.run()啓動流程

  衆所周知,類上面帶有@SpringBootApplication註解的類,即爲springboot的啓動類。一個springboot項目只能有一個啓動類。咱們來分析一下SpringBoot項目的啓動過程,首先看看啓動類裏面都包含什麼java

@SpringBootApplication
public class HelloWorldMainApplication {
    public static void main(String[] args) {
        //spring應用啓動起來
        SpringApplication.run(HelloWorldMainApplication.class,args);

    }
}

  從上面的代碼中能夠看出真正起做用的是SpringApplication.run();這個方法,下面主要分析一下這個方法。
圖片描述react

1、實例化SpringApplication

  SpringApplication初始化時主要作三件事情:web

  • 1.根據classpath下是否存在(ConfigurableWebApplicationContext)判斷是否要啓動一個web applicationContext
  • 2.SpringFactoriesInstances加載classpath下全部可用的ApplicationContextInitializer
  • 3.SpringFactoriesInstances加載classpath下全部可用的ApplicationListener
/**
 * Create a new {@link SpringApplication} instance. The application context will load
 * beans from the specified primary sources (see {@link SpringApplication class-level}
 * documentation for details. The instance can be customized before calling
 * {@link #run(String...)}.
 * @param resourceLoader the resource loader to use
 * @param primarySources the primary bean sources
 * @see #run(Class, String[])
 * @see #setSources(Set)
 */
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    //1.根據classpath下是否存在(ConfigurableWebApplicationContext)判斷是否要啓動一個web applicationContext
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    //2.SpringFactoriesInstances加載classpath下全部可用的ApplicationContextInitializer
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    //3.SpringFactoriesInstances加載classpath下全部可用的ApplicationListener
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
}

2、實例化完成後調用run()方法

  調用run()方法執行的過程主要分爲如下幾步:spring

  • 1.遍歷SpringApplication初始化過程當中加載的SpringApplicationRunListeners
  • 2.調用Starting()監聽SpringApplication的啓動
  • 3.加載SpringBoot配置環境(ConfigurableEnvironment)
  • 4.設置banner屬性
  • 5.建立ConfigurableApplicationContext(應用配置上下文)
  • 6.將listeners、environment、applicationArguments、bannner等重要組件與上下文對象關聯
  • 7.bean的實力化完成
/**
 * Run the Spring application, creating and refreshing a new
 * {@link ApplicationContext}.
 * @param args the application arguments (usually passed from a Java main method)
 * @return a running {@link ApplicationContext}
 */
public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    configureHeadlessProperty();
    //1.遍歷SpringApplication初始化過程當中加載的SpringApplicationRunListeners
    SpringApplicationRunListeners listeners = getRunListeners(args);
    //2.調用starting()監聽SpringApplication的啓動
    listeners.starting();
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        //3.加載SpringBoot配置環境
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        configureIgnoreBeanInfo(environment);
        //4.設置banner屬性
        Banner printedBanner = printBanner(environment);
        //5.建立ConfigurableApplicationContext(應用配置上下文)
        context = createApplicationContext();
        exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context);
        //6.將listeners、environment、applicationArguments、banner等重要組件與上下文對象關聯
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        //7.實例化bean
        refreshContext(context);
        afterRefresh(context, applicationArguments);
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
        }
        listeners.started(context);
        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;
}

1.遍歷SpringApplication初始化過程當中加載的SpringApplicationRunListeners

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

2.調用Starting()監聽SpringApplication的啓動

public void starting() {
    //遍歷全部的SpringApplicationRunListener,調用starting()方法監聽SpringApplication的啓動
    for (SpringApplicationRunListener listener : this.listeners) {
        listener.starting();
    }
}

3.加載SpringBoot配置環境(ConfigurableEnvironment)

  加載SpringBoot配置環境(configurableEnvironment),若是是經過web容器發佈,會加載StandardEnvironment。將配置文件(Environment)加入到監聽器對象中(SpringApplicationRunListeners)tomcat

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments) {
    // Create and configure the environment
    //若是environment不爲空直接返回 || 若是是web環境則直接實例化StandardServletEnvironment類 || 若是不是web環境則直接實例化StandardEnvironment類
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    //配置環境信息
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    //通知全部的監聽者,環境已經準備好了
    listeners.environmentPrepared(environment);
    bindToSpringApplication(environment);
    if (!this.isCustomEnvironment) {
        environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
                deduceEnvironmentClass());
    }
    ConfigurationPropertySources.attach(environment);
    return environment;
}

4.設置banner屬性

private Banner printBanner(ConfigurableEnvironment environment) {
    //若是未開啓banner打印直接返回
    if (this.bannerMode == Banner.Mode.OFF) {
        return null;
    }
    //建立ResourceLoader對象
    ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
            : new DefaultResourceLoader(getClassLoader());
    //建立SpringApplicationBannerPrinter,該對象用來打印banner
    SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
    //若是bannerMode模式爲LOG,則將bannner打印到log文件中
    if (this.bannerMode == Mode.LOG) {
        return bannerPrinter.print(environment, this.mainApplicationClass, logger);
    }
    //打印banner到控制檯
    return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}

5.初始化ConfigurableApplicationContext(應用配置上下文)

  在SpringBoot中,應用類型分爲三類springboot

public enum WebApplicationType {
    /**
     * The application should not run as a web application and should not start an
     * embedded web server.
     */
    // 應用程序不是web應用,也不該該用web服務器去啓動
    NONE,
    /**
     * The application should run as a servlet-based web application and should start an
     * embedded servlet web server.
     */
    //應用程序應做爲基於servlet的web應用程序運行,並應啓動嵌入式servlet web(tomcat)服務器
    SERVLET,
    /**
     * The application should run as a reactive web application and should start an
     * embedded reactive web server.
     */
    //應用程序應做爲 reactive web應用程序運行,並應啓動嵌入式 reactive web服務器。
    REACTIVE;
}

  根據webEnvironment是不是web環境建立默認的contextClass,AnnotationConfigEnbeddedWebApplicationContext(經過掃描全部註解類來加載bean)和ConfigurableWebApplicationContext),最後經過BeanUtils實例化上下文對象,並返回。服務器

/**
 * Strategy method used to create the {@link ApplicationContext}. By default this
 * method will respect any explicitly set application context or application context
 * class before falling back to a suitable default.
 * @return the application context (not yet refreshed)
 * @see #setApplicationContextClass(Class)
 */
protected ConfigurableApplicationContext createApplicationContext() {
    //根據webEnvironment是不是web環境建立默認的contextClass
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
        try {
            switch (this.webApplicationType) {
            case SERVLET:
                //AnnotationConfigServletWebServerApplicationContext
                contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
                break;
            case REACTIVE:
                //AnnotationConfigReactiveWebServerApplicationContext
                contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                break;
            default:
                //AnnotationConfigApplicationContext
                contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
            }
        }
        catch (ClassNotFoundException ex) {
            throw new IllegalStateException(
                    "Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass",
                    ex);
        }
    }
    //BeanUtils實例化上下文對象
    return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

6.將listeners、environment、applicationArguments、banner等重要組件與上下文對象關聯

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
        SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
    //設置上下文的environment
    context.setEnvironment(environment);
    //應用上下文後處理
    postProcessApplicationContext(context);
    //在context refresh以前,對其應用ApplicationContextInitializer
    applyInitializers(context);
    //上下文準備
    listeners.contextPrepared(context);
    //打印啓動日誌和啓動應用的profile
    if (this.logStartupInfo) {
        logStartupInfo(context.getParent() == null);
        logStartupProfileInfo(context);
    }
    // Add boot specific singleton beans
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    //向beanFactory註冊單例bean:命令行參數bean
    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    if (printedBanner != null) {
        //向beanFactory註冊單例bean:banner bean
        beanFactory.registerSingleton("springBootBanner", printedBanner);
    }
    if (beanFactory instanceof DefaultListableBeanFactory) {
        ((DefaultListableBeanFactory) beanFactory)
                .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    }
    // Load the sources
    //獲取SpringApplication的primarySources屬性
    Set<Object> sources = getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    //將bean加載到應用上下文
    load(context, sources.toArray(new Object[0]));
    //向上下文添加ApplicationListener,並廣播ApplicationPreparedEvent事件
    listeners.contextLoaded(context);
}

7.bean的實例化完成,刷新應用上下文

相關文章
相關標籤/搜索