springboot源碼分析之SpringApplication

想必大家對(duì)springbootApplication很熟悉,它是我們springboot的項(xiàng)目的一個(gè)入口,來(lái)看一段大家都熟知的代碼:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class AppApplication {

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

}

上面這是我創(chuàng)建的一個(gè)springboot項(xiàng)目,這段代碼就是我們項(xiàng)目的入口,看似一段很簡(jiǎn)單的代碼,其真正的作用在于注解SpringBootApplication身上,既然我們知道了入口,那么我們來(lái)看看:

  • 注解SpringBootApplication在此處首先表明是一個(gè)springboot應(yīng)用,通過該注解可以開啟自動(dòng)裝配的功能.
  • SpringApplication.run(...)是用來(lái)啟動(dòng)該應(yīng)用.

所以接下來(lái)我們來(lái)看看springboot項(xiàng)目的啟動(dòng)的過程,關(guān)于注解SpringBootApplication我們專門來(lái)說.

SpringApplgication

SpringApplgication位于org.springframework.boot.SpringApplication包下,是spring應(yīng)用的啟動(dòng)類,來(lái)看一下官方是如何說的:

Class that can be used to bootstrap and launch a Spring application from a Java main method. By default class will perform the following steps to bootstrap your application

在實(shí)際的開發(fā)中,我們應(yīng)該用到的是它的static方法,來(lái)看代碼:

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);
}
public static void main(String[] args) throws Exception {
    SpringApplication.run(new Class<?>[0], args);
}
  • 簡(jiǎn)單的三個(gè)靜態(tài)方法,我們可以從中發(fā)現(xiàn),第二個(gè)和第三個(gè)靜態(tài)方法最后還是調(diào)用第一個(gè)靜態(tài)方法
  • 首先是new SpringApplication對(duì)象.
  • 接著是調(diào)用SpringApplication#run(Class<?> primarySource, String... args)方法,真正的運(yùn)行我們的spring應(yīng)用

既然知道了上面代碼的執(zhí)行順序,我們進(jìn)行一步一步的學(xué)習(xí),首先我們來(lái)看構(gòu)造SpringApplication的過程,代碼如下:

//用來(lái)保存javaConfig類的數(shù)組
private Set<Class<?>> primarySources;
//我們啟動(dòng)類的類型
private Class<?> mainApplicationClass;
//資源加載器
private ResourceLoader resourceLoader;
//針對(duì)于web類型的應(yīng)用
private WebApplicationType webApplicationType;
//用來(lái)保存ApplicationContextInitializer類型的集合
private List<ApplicationContextInitializer<?>> initializers;
//用來(lái)保存ApplicationListener的集合
private List<ApplicationListener<?>> listeners;

public SpringApplication(Class<?>... primarySources) {
    this(null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    //初始化ApplicationContextInitializer過程
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    //初始化ApplicationListener過程
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
}

簡(jiǎn)單的來(lái)看下我們上面提到的這些屬性:

  • primarySources:是用來(lái)保存javaConfig類的,也就是我們本篇開頭示例中的AppApplication類.
  • mainApplicationClass是通過調(diào)用 #deduceMainApplicationClass() 方法,來(lái)判斷我們調(diào)用的是哪個(gè)main方法.
  • resourceLoader也就是我們的資源加載器,我們?cè)谥暗膕pring文章中的spring容器實(shí)現(xiàn)之資源加載我們?cè)敿?xì)的講解了加載資源的過程,感興趣的可以去看看.
  • webApplicationType其主要的作用是通過調(diào)用WebApplicationType#deduceFromClasspath()方法來(lái)判斷我們的web類型.
  • initializers 是ApplicationContextInitializer集合,詳解后面來(lái)說.
  • listeners是用來(lái)ApplicationListener的集合,詳解見后面.
deduceMainApplicationClass()
private Class<?> deduceMainApplicationClass() {
    try {
        //獲取當(dāng)前StackTraceElement類型的數(shù)組
        StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
        //遍歷處理
        for (StackTraceElement stackTraceElement : stackTrace) {
            //判斷哪個(gè)執(zhí)行了main方法
            if ("main".equals(stackTraceElement.getMethodName())) {
                return Class.forName(stackTraceElement.getClassName());
            }
        }
    }
    catch (ClassNotFoundException ex) {
        // Swallow and continue
    }
    return null;
}

該方法是我們屬性mainApplicationClass的作用,位于SpringApplication.java中.

getSpringFactoriesInstances()

該方法的是啥哈,主要是針對(duì)于上面的屬性initializers和listeners來(lái)實(shí)現(xiàn)的,不同的是一個(gè)獲取ApplicationContextInitializer的對(duì)象集合,一個(gè)是ApplicationListener的對(duì)象集合,我們來(lái)看實(shí)現(xiàn)過程:

SpringApplication.java
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 classLoader = getClassLoader();
    // Use names and ensure unique to protect against duplicates
    //加載指定的類型的類名集合,從META-INF/spring.factories下加載
    Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    //創(chuàng)建實(shí)例對(duì)象
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
    //給創(chuàng)建的實(shí)例對(duì)象排序
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}

幾行代碼,卻做了很多事,簡(jiǎn)單的來(lái)看一下:

  • 首先是調(diào)用#getClassLoader()來(lái)獲取類加載器.
  • 然后是調(diào)用SpringFactoriesLoader#loadFactoryNames(...)方法去加載指定了類型的類名集合,在META-INF/spring.factories路徑下去加載.
  • 接著是通過#createSpringFactoriesInstances(...)來(lái)進(jìn)行實(shí)例的創(chuàng)建過程
  • 最后對(duì)剛創(chuàng)建的實(shí)例們進(jìn)行排序

這就是上面代碼的為我們所做的事,其中在第二步中,可能我們對(duì)META-INF/spring.factories路徑下的東東不太明白,其實(shí)就是以key-value的形式存在,關(guān)于具體的加載過程,后續(xù)我們來(lái)說,我們來(lái)看看創(chuàng)建實(shí)例的過程:

// SpringApplication.java

/**
 * 創(chuàng)建對(duì)象的集合
 *
 * @param type 父類
 * @param parameterTypes 構(gòu)造方法的參數(shù)類型
 * @param classLoader 類加載器
 * @param args 參數(shù)
 * @param names 類名的集合
 * @param <T> 泛型
 * @return 對(duì)象的數(shù)組
 */
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
        ClassLoader classLoader, Object[] args, Set<String> names) {
    //通過names集合的大小構(gòu)建一個(gè)新的同樣大小的集合
    List<T> instances = new ArrayList<>(names.size());
    //遍歷處理
    for (String name : names) {
        try {
            //利用反射機(jī)制,獲取name對(duì)應(yīng)的具體類
            Class<?> instanceClass = ClassUtils.forName(name, classLoader);
            Assert.isAssignable(type, instanceClass);
            //獲取構(gòu)造方法
            Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
            //構(gòu)建實(shí)例對(duì)象
            T instance = (T) BeanUtils.instantiateClass(constructor, args);
            //保存到我們構(gòu)建的集合中
            instances.add(instance);
        }
        catch (Throwable ex) {
            throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
        }
    }
    return instances;
}

上述創(chuàng)建實(shí)例的過程 其實(shí)也并不復(fù)雜,還是利用反射的機(jī)制來(lái)完成實(shí)例的創(chuàng)建過程,看完了第一個(gè)核心過程,我們來(lái)看看第二個(gè)過程就是調(diào)用方法#run(...),方法明確表示就是用來(lái)啟動(dòng)我們的spring的運(yùn)用的,接下來(lái)我們來(lái)看看具體的實(shí)現(xiàn)過程:

run方法
#SpringApplication.java
public ConfigurableApplicationContext run(String... args) {
    //1.創(chuàng)建StopWatch實(shí)例
    //調(diào)用它的start方法啟動(dòng)StopWatch來(lái)計(jì)時(shí)run方法的執(zhí)行cd
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    //2.配置Headless屬性
    configureHeadlessProperty();
    //通過getRunListeners來(lái)獲取所有的SpringApplicationRunListeners實(shí)例,同時(shí)開啟監(jiān)聽操作
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    try {
        //3.通過參數(shù)args來(lái)創(chuàng)建ApplicationArguments對(duì)象
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        //4.預(yù)加載所有的配置信息,包括environment
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);

        configureIgnoreBeanInfo(environment);
        //5.打印Banner圖樣
        Banner printedBanner = printBanner(environment);
        //6.創(chuàng)建上下文
        context = createApplicationContext();
        //7.創(chuàng)建異常報(bào)告器
        exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context);
        //8.初始化上下文(通過調(diào)用別的方法)前的準(zhǔn)備
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        //9.執(zhí)行初始化操作
        refreshContext(context);
        //10.后置處理的操作,默認(rèn)是空實(shí)現(xiàn)
        afterRefresh(context, applicationArguments);
        //11.停止執(zhí)行的運(yùn)行時(shí)間
        stopWatch.stop();
        //12.springboot的啟動(dòng)日志打印過程
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
        }
        //13.通知相應(yīng)的監(jiān)聽器spring容器啟動(dòng)完成
        listeners.started(context);
        //14.調(diào)用 ApplicationRunner或CommandLineRunner的運(yùn)行方法
        callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
        //若在此過程中發(fā)生異常,直接拋IllegalStateException
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }

    try {
        //15.再次通知相應(yīng)的監(jiān)聽器spring容器處于運(yùn)行的狀態(tài)
        listeners.running(context);
    }
    catch (Throwable ex) {
        //如果在此過程中發(fā)生異常,直接拋IllegalStateException
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }
    return context;
}

額,代碼有點(diǎn)長(zhǎng),我們簡(jiǎn)單的來(lái)看一下每一步的過程:

  • 在1.處,首先是創(chuàng)建StopWatch實(shí)例同時(shí)調(diào)用start方法啟動(dòng)它,其目的是用來(lái)監(jiān)控run方法運(yùn)行的時(shí)長(zhǎng).
  • 在2.處,通過調(diào)用#configureHeadlessProperty()方法來(lái)配置Headless屬性
private void configureHeadlessProperty() {
    System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
            System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}

好像該屬性和AWT有關(guān),感覺對(duì)我們來(lái)說作用不大,哈哈哈...,不是很清楚.

  • 在2.1.處,通過調(diào)用#getRunListeners()方法來(lái)獲取SpringApplicationRunListeners集合,同時(shí)開啟監(jiān)聽操作,代碼如下:
//SpringApplication.java
private SpringApplicationRunListeners getRunListeners(String[] args) {
    Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
    return new SpringApplicationRunListeners(logger,
            getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}

我們可以通過Dbug發(fā)現(xiàn)我們此時(shí)的listeners數(shù)組里的變量,如圖所示:

方法.png

listeners中僅有一個(gè)變量實(shí)例就是我們圖中的SpringApplicationRunListener類型.關(guān)于它后面來(lái)說,這里知道即可.

  • 在3.處通過我們傳入的參數(shù)args來(lái)構(gòu)建ApplicationArguments對(duì)象,作為下一步的必要參數(shù).
  • 在4.處通過調(diào)用#prepareEnvironment(...)來(lái)預(yù)加載所有的配置信息,包括environment.
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments) {
    // Create and configure the environment
    //1.通過調(diào)用#getOrCreateEnvironment()來(lái)創(chuàng)建ConfigurableEnvironment實(shí)例
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    //2.進(jìn)行相關(guān)的配置
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    //3.設(shè)置environment的屬性源
    ConfigurationPropertySources.attach(environment);
    //4.通知SpringApplicationRunListener當(dāng)前環(huán)境已就緒
    listeners.environmentPrepared(environment);
    //5.將environment綁定到SpringApplication上
    bindToSpringApplication(environment);
    //6.非自定義的環(huán)境,通過條件去轉(zhuǎn)換
    if (!this.isCustomEnvironment) {
        environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
                deduceEnvironmentClass());
    }
    //7.將environment添加到PropertySource中
    ConfigurationPropertySources.attach(environment);
    return environment;
}

好像代碼不是很多,邏輯挺多的,簡(jiǎn)單的來(lái)看一下:

  • 在4.1.處,主要是構(gòu)建ConfigurableEnvironment對(duì)象的過程,來(lái)看看如何構(gòu)建的
//SpringApplication.java
private ConfigurableEnvironment environment;
//針對(duì)于web類型的應(yīng)用
private WebApplicationType webApplicationType;
private ConfigurableEnvironment getOrCreateEnvironment() {
    //如果有,直接返回即可.
    if (this.environment != null) {
        return this.environment;
    }
    //不存在的話,通過webApplicationType來(lái)創(chuàng)建
    switch (this.webApplicationType) {
    case SERVLET:
        return new StandardServletEnvironment();
    case REACTIVE:
        return new StandardReactiveWebEnvironment();
    default:
        return new StandardEnvironment();
    }
}

可以看到的是:

  • 如果當(dāng)前存在environment的話,直接用它
  • 在不存在的情況下,通過webApplicationType來(lái)創(chuàng)建不同類型的ConfigurableEnvironment對(duì)象
  • 在4.2.處,通過#configureEnvironment(...)方法進(jìn)行環(huán)境的配置過程.
//是否添加共享的ConversionService 
  private boolean addConversionService = true;
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
    //設(shè)置environment的ConversionService屬性
    if (this.addConversionService) {
        ConversionService conversionService = ApplicationConversionService.getSharedInstance();
        environment.setConversionService((ConfigurableConversionService) conversionService);
    }
    //1.1.添加environment的PropertySource屬性源
    configurePropertySources(environment, args);
    //1.2.配置environment的activeProfiles屬性
    configureProfiles(environment, args);
}

該方法其主要的作用是設(shè)置environment的一些屬性其中包括:

  • 在4.1.1.處,通過方法#configurePropertySources(...)來(lái)設(shè)置environment的 PropertySource屬性
//SpringApplication.java
/**jvm參數(shù)*/
private boolean addCommandLineProperties = true;

protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
    //獲取Mu對(duì)象tablePropertySources
    MutablePropertySources sources = environment.getPropertySources();
    //設(shè)置默認(rèn)的PropertySources
    if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
        sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));
    }
    //通過啟動(dòng)參數(shù)來(lái)設(shè)置
    if (this.addCommandLineProperties && args.length > 0) {
        String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
        //如果存在的話,替換原來(lái)的
        if (sources.contains(name)) {
            PropertySource<?> source = sources.get(name);
            CompositePropertySource composite = new CompositePropertySource(name);
            composite.addPropertySource(
                    new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
            composite.addPropertySource(source);
            sources.replace(name, composite);
        }
        //不存在的話進(jìn)行添加
        else {
            sources.addFirst(new SimpleCommandLinePropertySource(args));
        }
    }
}

從代碼中可以看到的是作為PropertySources參數(shù)的分別有jvm參數(shù)以及默認(rèn)的propertySources.

  • 在4.1.2處通過方法#configureProfiles(...)來(lái)設(shè)置切換屬性
private Set<String> additionalProfiles = new HashSet<>();
protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
    //確保已經(jīng)被初始化
    environment.getActiveProfiles(); // ensure they are initialized
    // But these ones should go first (last wins in a property key clash)
    Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
    profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
    //設(shè)置ActiveProfiles
    environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}

代碼簡(jiǎn)單,這里涉及到了springboot是如何進(jìn)行Profiles的切換過程,后續(xù)來(lái)說.

  • 在4.3處,主要是添加屬性源的過程.
//ConfigurationPropertySource.java
private static final String ATTACHED_PROPERTY_SOURCE_NAME = "configurationProperties";
public static void attach(Environment environment) {
    Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
    MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();
    PropertySource<?> attached = sources.get(ATTACHED_PROPERTY_SOURCE_NAME);
    if (attached != null && attached.getSource() != sources) {
        sources.remove(ATTACHED_PROPERTY_SOURCE_NAME);
        attached = null;
    }
    if (attached == null) {
        sources.addFirst(new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME,
                new SpringConfigurationPropertySources(sources)));
    }
}

從代碼中我們可以看到的通過獲取到的attached來(lái)區(qū)分,存在的話從sources中移除,不存在的話,加入默認(rèn)的即可,這里不多說了,接著看:

  • 在4.4處通過方法#environmentPrepared(...)來(lái)通知SpringApplicationRunListener,其主要的目的是告訴它當(dāng)前環(huán)境已經(jīng)準(zhǔn)備就緒.

  • 在4.5.處,通過方法#bindToSpringApplication(...)將當(dāng)前environment綁定在當(dāng)前SpringApplication上.

  • 在4.6.處,如果非自定義 environment ,則根據(jù)條件轉(zhuǎn)換.默認(rèn)情況下isCustomEnvironment 為 false ,所以會(huì)執(zhí)行這塊邏輯。但是,一般情況下,返回的還是 environment 自身,所以可以無(wú)視這塊邏輯先.

  • 在4.7.處.跟4.3處一樣,這里就不多扯了.

  • 在5處,通過調(diào)用#printBanner(...)來(lái)打印springboot啟動(dòng)的Banner圖樣,相信這個(gè)大家都知道,就不多扯了...

  • 在6.處,通過方法#createApplicationContext()創(chuàng)建spring容器.后面來(lái)講

  • 在7.處,通過 #getSpringFactoriesInstances(Class<T> type) 方法,進(jìn)行獲取SpringBootExceptionReporter 類型的對(duì)象數(shù)組,SpringBootExceptionReporter ,記錄啟動(dòng)過程中的異常信息.

通過dbug我們發(fā)現(xiàn),在此處的exceptionReporters數(shù)組中有一個(gè)變量,如圖:

微信截圖_20190829214623.png
  • 在8處,通過調(diào)用#prepareContext(...)方法來(lái)初始化上下文環(huán)境,這里主要是通過調(diào)用別動(dòng)初始化方法來(lái)完成的,詳解見后面.
  • 在9處,調(diào)用#refreshContext(ConfigurableApplicationContext context)方法,啟動(dòng)(刷新) Spring 容器,詳解見后面.
  • 在10處,調(diào)用#afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) 方法執(zhí),行 Spring 容器的初始化的后置處理,默認(rèn)實(shí)現(xiàn)為空,代碼如下:
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
}
  • 在11處,通過調(diào)用stopWatch#stop()來(lái)停止統(tǒng)計(jì)run方法的執(zhí)行時(shí)長(zhǎng).
  • 在12處,打印 Spring Boot 啟動(dòng)時(shí)的時(shí)長(zhǎng)日志,如下圖所示:
微信截圖_20190829221253.png
  • 在13處,調(diào)用SpringApplicationRunListeners#started(ConfigurableApplicationContext context) 方法,通知 SpringApplicationRunListener 的集合,Spring 容器啟動(dòng)完成,關(guān)于該方法的詳解后面來(lái)說.
  • 在14.處,首先是調(diào)用# ApplicationRunner或CommandLineRunner的運(yùn)行方法,若在此過程中發(fā)生異常,直接拋IllegalStateException
  • 在15處,通過SpringApplicationRunListeners#running(ConfigurableApplicationContext context) 方法,通知 SpringApplicationRunListener 的數(shù)組,Spring 容器運(yùn)行中,如果在此過程中發(fā)生異常,直接拋IllegalStateException

接下來(lái)我們分別來(lái)看看上述我們遺留的一些方法的詳解,首先我們來(lái)看看6處遺留的創(chuàng)建spring容器的過程來(lái)看是如何實(shí)現(xiàn)的.

createApplicationContext()方法
//SpringApplication.java
private Class<? extends ConfigurableApplicationContext> applicationContextClass;
/**
 * The class name of application context that will be used by default for web
 * environments.
 */
public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
        + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
/**
 * The class name of application context that will be used by default for reactive web
 * environments.
 */
public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
        + "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";

/**
 * The class name of application context that will be used by default for non-web
 * environments.
 */
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
        + "annotation.AnnotationConfigApplicationContext";
protected ConfigurableApplicationContext createApplicationContext() {
    //通過webApplicationType類型類來(lái)完成ApplicationContext的創(chuàng)建過程
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
        try {
            switch (this.webApplicationType) {
            case SERVLET:
                contextClass = Class.forName(DEFAULT_SERVLET_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);
        }
    }
    //這里表示contextClass不為null,則直接通過反射的方法去創(chuàng)建
    return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

從上述代碼可以看出,創(chuàng)建spring容器的過程并不復(fù)雜,還是根據(jù)webApplicationType來(lái)判斷進(jìn)行創(chuàng)建不同的ApplicationContext對(duì)象,接著我們來(lái)看在8處遺留的通過方法#prepareContext(....)來(lái)初始化spring容器

prepareContext()
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
        SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
    //設(shè)置context的environment的環(huán)境
    context.setEnvironment(environment);
    //1.設(shè)置一些跟當(dāng)前ApplicationContext有關(guān)的屬性
    postProcessApplicationContext(context);
    //2.初始化ApplicationContextInitializer
    applyInitializers(context);
    //3.通知SpringApplicationRunListener的數(shù)組,Spring容器準(zhǔn)備完成
    listeners.contextPrepared(context);
    //4.打印啟動(dòng)日志
    if (this.logStartupInfo) {
        logStartupInfo(context.getParent() == null);
        logStartupProfileInfo(context);
    }
    // Add boot specific singleton beans
    //5.獲取單例ConfigurableListableBeanFactory
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    //將applicationArguments注冊(cè)到beanFactory中
    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    if (printedBanner != null) {
        beanFactory.registerSingleton("springBootBanner", printedBanner);
    }
    if (beanFactory instanceof DefaultListableBeanFactory) {
        ((DefaultListableBeanFactory) beanFactory)
                .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    }
    // Load the sources
    //6.加載資源文件主要是(beanDefinitions)
    Set<Object> sources = getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    load(context, sources.toArray(new Object[0]));
    //7.通知 SpringApplicationRunListener的數(shù)組,Spring容器加載完成
    listeners.contextLoaded(context);
}

我們可以看到的是該方法主要的作用是針對(duì)applicationContext對(duì)象的屬性做了一些初始化的操作.我們簡(jiǎn)單的來(lái)看下過程:

  • 首先是對(duì)environment的設(shè)置
  • 在1處,通過調(diào)用#postProcessApplicationContext(ConfigurableApplicationContext context)方法,設(shè)置一些跟當(dāng)前ApplicationContext有關(guān)的屬性,代碼如下:
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
    if (this.beanNameGenerator != null) {
        context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
                this.beanNameGenerator);
    }
    if (this.resourceLoader != null) {
        if (context instanceof GenericApplicationContext) {
            ((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
        }
        if (context instanceof DefaultResourceLoader) {
            ((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
        }
    }
    if (this.addConversionService) {
        context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
    }
}
  • 在2處,通過調(diào)用 #applyInitializers(ConfigurableApplicationContext context) 方法,初始化 ApplicationContextInitializer,代碼如下:
/**
 * Apply any {@link ApplicationContextInitializer}s to the context before it is
 * refreshed.
 * @param context the configured ApplicationContext (not refreshed yet)
 * @see ConfigurableApplicationContext#refresh()
 */
@SuppressWarnings({ "rawtypes", "unchecked" })
protected void applyInitializers(ConfigurableApplicationContext context) {
    //遍歷處理ApplicationContextInitializer數(shù)組
    for (ApplicationContextInitializer initializer : getInitializers()) {
        //非空的泛型校驗(yàn)
        Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
                ApplicationContextInitializer.class);
        Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
        //初始化context
        initializer.initialize(context);
    }
}
  • 在3處,通過SpringApplicationRunListeners#contextPrepared(ConfigurableApplicationContext context) 方法,通知 SpringApplicationRunListener 的數(shù)組,Spring 容器準(zhǔn)備完成
  • 在4處,打印springboot啟動(dòng)時(shí)的日志
  • 在5處,只要是獲取單例的ConfigurableListableBeanFactory實(shí)例并設(shè)置相關(guān)屬性
  • 在6處,主要是加載資源文件(beanDefinition),來(lái)看代碼:
protected void load(ApplicationContext context, Object[] sources) {
    if (logger.isDebugEnabled()) {
        logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
    }
    //<1>創(chuàng)建BeanDefinitionLoader實(shí)例
    BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
    if (this.beanNameGenerator != null) {
        //2.設(shè)置loader的BeanNameGenerator屬性
        loader.setBeanNameGenerator(this.beanNameGenerator);
    }
    if (this.resourceLoader != null) {
        //設(shè)置loader的ResourceLoader屬性
        loader.setResourceLoader(this.resourceLoader);
    }
    if (this.environment != null) {
        //設(shè)置loader的Environment屬性
        loader.setEnvironment(this.environment);
    }
    //<3>執(zhí)行加載操作
    loader.load();
}

我們可以看到的是在<1>處,首先是通過方法#getBeanDefinitionRegistry(...)來(lái)構(gòu)建BeanDefinitionRegistry對(duì)象,我們來(lái)看代碼:

private BeanDefinitionRegistry getBeanDefinitionRegistry(ApplicationContext context) {
    if (context instanceof BeanDefinitionRegistry) {
        return (BeanDefinitionRegistry) context;
    }
    if (context instanceof AbstractApplicationContext) {
        return (BeanDefinitionRegistry) ((AbstractApplicationContext) context).getBeanFactory();
    }
    throw new IllegalStateException("Could not locate BeanDefinitionRegistry");
}

代碼簡(jiǎn)單就不多扯了,接著看:

  • 同樣是在<1>處,等創(chuàng)建完BeanDefinitionRegistry對(duì)象時(shí),作為構(gòu)建BeanDefinitionLoader對(duì)象的參數(shù)進(jìn)行創(chuàng)建過程,其中調(diào)用的是#createBeanDefinitionLoader(...)方法,額,大家都懂,由于篇幅太長(zhǎng)我們后續(xù)來(lái)說吧.
  • 在<2>處,通過條件的判斷對(duì)BeanDefinitionLoader對(duì)象分別進(jìn)行屬性BeanNameGenerator 和ResourceLoader以及Environment的設(shè)置過程.
  • 在<3>處才是執(zhí)行加載的過程,感興趣的可以去看看,到這里我就為止了...
  • 在7處,調(diào)用 SpringApplicationRunListeners#contextLoaded(ConfigurableApplicationContext context) 方法,通知 SpringApplicationRunListener 的數(shù)組,Spring 容器加載完成

接著我們來(lái)看9處遺留的關(guān)于方法#refreshContext(...)啟動(dòng)spring容器的過程,直接看代碼:

//是否開啟對(duì)ShutdownHook的注冊(cè)
private boolean registerShutdownHook = true;
private void refreshContext(ConfigurableApplicationContext context) {
    //1.開啟spring容器
    refresh(context);
    if (this.registerShutdownHook) {
        try {
            //2.向context中注冊(cè)ShutdownHook
            context.registerShutdownHook();
        }
        catch (AccessControlException ex) {
            // Not allowed in some environments.
        }
    }
}

代碼結(jié)構(gòu)清晰簡(jiǎn)單,其主要的邏輯處理還是通過調(diào)用別的方法來(lái)完成,簡(jiǎn)單的來(lái)看一下:

  • 在1處,通過調(diào)用#refresh(ApplicationContext applicationContext)方法來(lái)開啟spring容器.代碼如下:
/**
 * Refresh the underlying {@link ApplicationContext}.
 * @param applicationContext the application context to refresh
 */
protected void refresh(ApplicationContext applicationContext) {
    Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
    ((AbstractApplicationContext) applicationContext).refresh();
}
  • 在2.處,調(diào)用 ConfigurableApplicationContext#registerShutdownHook() 方法,注冊(cè) ShutdownHook ,該類主要用于 Spring 應(yīng)用的關(guān)閉時(shí),銷毀相應(yīng)的 Bean 們

看完了該方法的作用我們來(lái)看看在14.處我們遺留的callRunners(...)的詳解.

callRunners

該方法主要的作用是通過調(diào)用ApplicationRunners或者是CommandLineRunner方法,我們來(lái)看看代碼:

private void callRunners(ApplicationContext context, ApplicationArguments args) {
    //<1>.用來(lái)保存所有的runners
    List<Object> runners = new ArrayList<>();
    //<1.1>將從context中獲取到的ApplicationRunner的bean保存到runners中
    runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
    //<1.2>將從context中獲取到的CommandLineRunner的bean保存到runners中
    runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
    //<1.3>對(duì)runners進(jìn)行排序
    AnnotationAwareOrderComparator.sort(runners);
    //<1.4>遍歷進(jìn)行執(zhí)行相應(yīng)的邏輯
    for (Object runner : new LinkedHashSet<>(runners)) {
        if (runner instanceof ApplicationRunner) {
            callRunner((ApplicationRunner) runner, args);
        }
        if (runner instanceof CommandLineRunner) {
            callRunner((CommandLineRunner) runner, args);
        }
    }
}

方法簡(jiǎn)單明了,我們簡(jiǎn)單的總結(jié)一下:

  • 在<1>處,創(chuàng)建一個(gè)用來(lái)保存runners類型bean的數(shù)組.
  • 在<1.4>處,遍歷runners數(shù)組,執(zhí)行邏輯.我們來(lái)看代碼:
private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
    try {
        (runner).run(args);
    }
    catch (Exception ex) {
        throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
    }
}

private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
    try {
        (runner).run(args.getSourceArgs());
    }
    catch (Exception ex) {
        throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
    }
}

在上述代碼中,用到了ApplicationRunner接口和CommandLineRunner接口,這里就不多說了.

SpringApplicationRunListeners

該類位于org.springframework.boot.SpringApplicationRunListeners包下,是對(duì)SpringApplicationRunListener的封裝,我們來(lái)看代碼:

''''''
/**
 * 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);
}

public void starting() {
    for (SpringApplicationRunListener listener : this.listeners) {
        listener.starting();
    }
}

public void environmentPrepared(ConfigurableEnvironment environment) {

    for (SpringApplicationRunListener listener : this.listeners) {
        listener.environmentPrepared(environment);
    }
}

public void contextPrepared(ConfigurableApplicationContext context) {
    for (SpringApplicationRunListener listener : this.listeners) {
        listener.contextPrepared(context);
    }
}

public void contextLoaded(ConfigurableApplicationContext context) {
    for (SpringApplicationRunListener listener : this.listeners) {
        listener.contextLoaded(context);
    }
}

public void started(ConfigurableApplicationContext context) {
    for (SpringApplicationRunListener listener : this.listeners) {
        listener.started(context);
    }
}

public void running(ConfigurableApplicationContext context) {
    for (SpringApplicationRunListener listener : this.listeners) {
        listener.running(context);
    }
}

public void failed(ConfigurableApplicationContext context, Throwable exception) {
    for (SpringApplicationRunListener listener : this.listeners) {
        callFailedListener(listener, context, exception);
    }
}

private void callFailedListener(SpringApplicationRunListener listener, ConfigurableApplicationContext context,
        Throwable exception) {
    try {
        listener.failed(context, exception);
    }
    catch (Throwable ex) {
        if (exception == null) {
            ReflectionUtils.rethrowRuntimeException(ex);
        }
        if (this.log.isDebugEnabled()) {
            this.log.error("Error handling failed", ex);
        }
        else {
            String message = ex.getMessage();
            message = (message != null) ? message : "no error message";
            this.log.warn("Error handling failed (" + message + ")");
        }
    }
}

我們來(lái)看一下該接口方法的定義:

''''
public interface SpringApplicationRunListener {

/**
 * Called immediately when the run method has first started. Can be used for very
 * early initialization.
 */
void starting();

/**
 * Called once the environment has been prepared, but before the
 * {@link ApplicationContext} has been created.
 * @param environment the environment
 */
void environmentPrepared(ConfigurableEnvironment environment);

/**
 * Called once the {@link ApplicationContext} has been created and prepared, but
 * before sources have been loaded.
 * @param context the application context
 */
void contextPrepared(ConfigurableApplicationContext context);

/**
 * Called once the application context has been loaded but before it has been
 * refreshed.
 * @param context the application context
 */
void contextLoaded(ConfigurableApplicationContext context);

/**
 * The context has been refreshed and the application has started but
 * {@link CommandLineRunner CommandLineRunners} and {@link ApplicationRunner
 * ApplicationRunners} have not been called.
 * @param context the application context.
 * @since 2.0.0
 */
void started(ConfigurableApplicationContext context);

/**
 * Called immediately before the run method finishes, when the application context has
 * been refreshed and all {@link CommandLineRunner CommandLineRunners} and
 * {@link ApplicationRunner ApplicationRunners} have been called.
 * @param context the application context.
 * @since 2.0.0
 */
void running(ConfigurableApplicationContext context);

/**
 * Called when a failure occurs when running the application.
 * @param context the application context or {@code null} if a failure occurred before
 * the context was created
 * @param exception the failure
 * @since 2.0.0
 */
void failed(ConfigurableApplicationContext context, Throwable exception);

}

該接口里面的方法都有詳細(xì)的解釋,可以自己去看看,我們發(fā)現(xiàn)該接口唯一的實(shí)現(xiàn)類是EventPublishingRunListener

EventPublishingRunListener類
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {

/**
 * 當(dāng)前的spring應(yīng)用
 */
private final SpringApplication application;
/***
 * 保存所有的參數(shù)的集合
 */
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) {
    this.initialMulticaster
            .multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, 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);
    }
}

private static class LoggingErrorHandler implements ErrorHandler {

    private static Log logger = LogFactory.getLog(EventPublishingRunListener.class);

    @Override
    public void handleError(Throwable throwable) {
        logger.warn("Error calling ApplicationEventListener", throwable);
    }

}

代碼簡(jiǎn)單,可以自己看看,這里我們可以將EventPublishingRunListener認(rèn)為是springApplication的一個(gè)事件轉(zhuǎn)換器,關(guān)于它的詳解我們后面來(lái)看....

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 2018年6月17日 姓名:潘紅軍 公司 :揚(yáng)州市方圓建筑工程有限公司 【日精進(jìn)打卡第145天】 南京第349期...
    5119a64ee3ab閱讀 90評(píng)論 0 0
  • 上一章 我和老王在那間辦公室手拉著手終于就我的升職問題達(dá)成了一致,這才喜笑顏開的又坐了下來(lái)。 老王又一本正經(jīng)的強(qiáng)調(diào)...
    島主王仙客閱讀 332評(píng)論 0 1
  • 別人的想法 是飄忽不定的 他們想著和戀人幽會(huì) 想走大運(yùn)或出大名 我總是想著麻煩 我的想法是穩(wěn)重的 所以當(dāng)麻煩來(lái)臨時(shí)...
    五星連珠閱讀 3,318評(píng)論 1 4
  • 文‖林翻飛 “小飛,你的快遞!” 小飛抬起趴在桌子上的頭。 “小紅,我哪有什么快遞,又不網(wǎng)購(gòu),最近也沒人寄東西??!...
    林翻飛閱讀 618評(píng)論 0 1

友情鏈接更多精彩內(nèi)容