SpringBoot系列(7):SpringBoot啓動流程源碼分析()

前言

經過前面《SpringBoot2.1.x源碼閱讀環境搭建詳解》,本節主要內容----SpringBoot啓動流程源碼分析。

首先看下環境準備:

項目/工具 版本
SpringBoot v2.1.x
spring v5.1.x
maven v3.5.4

SpringBoot框架減少的大量的文件配置,框架集成便捷,給項目開發帶來了很多便利。SpringBoot項目一般都會有註解*Application標註的入口類,入口類會有一個main方法,main方法是一個標準的Java應用程序的入口,可以直接啓動。

OK,廢話不多說,進入正題。

1、項目啓動類

在入口程序,我們可以看到其引入了@SpringBootApplication這個註解,它是SpringBoot的核心註解,用此註解標註的入口類是應用的啓動類,通常會在啓動類的在main()方法中創建了SpringApplication類的實例,然後調用該類的run()方法來啓動SpringBoot項目。

@SpringBootApplication
public class SpringBootAnalysisApplication {
    private static final Logger logger = LoggerFactory.getLogger(SpringStudyPractise.class);

    public static void main(String[] args) {
        logger.debug("================正在啓動==============");
        SpringApplication app = new SpringApplication(SpringStudyPractise.class);
        app.run(args);
        logger.debug("================啓動成功==============");
    }
}

@SpringBootApplication其實是一個組合註解。源碼如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

	@AliasFor(annotation = EnableAutoConfiguration.class)
	Class<?>[] exclude() default {};

	@AliasFor(annotation = EnableAutoConfiguration.class)
	String[] excludeName() default {};

	@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
	String[] scanBasePackages() default {};

	@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
	Class<?>[] scanBasePackageClasses() default {};

}

這個註解主要組合了以下註解:

【1】@SpringBootConfiguration:它是SpringBoot項目的配置註解,也是一個組合註解,源碼如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {

}

 在SpringBoot項目中推薦使用@SpringBootConfiguration註解來替代@Configuration註解。

【2】@EnableAutoConfiguration:啓動自動配置,該註解會讓SpringBoot根據當前項目所依賴的jar包自動配置項目的相關配置項。

【3】@ComponentScan:掃描配置,SpringBoot默認會掃描@SpringBootApplication所在類的同級包以及它的子包,所以建議將@SpringBootApplication修飾的入口類放置在項目包下(Group Id + Artifact Id),這樣做的好處是,可以保證SpringBoot項目自動掃描到項目所有的包。

而main方法中這個SpringApplication實例所提供的run()方法只應用主程序開始的運行,SpringApplication這個類可用於從Java主方法引導和啓動Spring應用程序。


OK,繼續往下主程序啓動流程。

啓動主程序main方法,初始化SpringApplication實例對象:

/**
 * 創建一個新的{@link SpringApplication}實例。
 * 應用程序上下文將從指定的主要源加載bean。可以在調用{@link #run(String…)}之前定製實例。
 * @param resourceLoader 資源加載器使用
 * @param primarySources bean對象
 */
@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】設置servlet環境
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    //【2】獲取ApplicationContextInitializer,也是在這裏開始首次加載spring.factories文件
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    //【3】獲取監聽器
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
}

首先,來看一下判斷Web環境的deduceFromClassoath()方法:

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

【1】這裏主要是通過判斷REACTIVE相關的字節碼是否存在,如果不存在,則web環境即爲SERVLET類型。這裏設置好web環境類型,在後面會根據類型初始化對應環境。 

繼續往下看【2】getSpringFactoriesInstances()方法:它以ApplicationContextInitializer接口類爲入參,是spring組件spring-context組件中的一個接口,主要是spring ioc容器刷新之前的一個回調接口,用於處於自定義邏輯。

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
    ClassLoader classLoader = getClassLoader();
    //在這裏,將加載sprin.factories
    Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}

這裏看一下names集合的入參的loadFactoryNames()方法,這裏第一次加載META-INF/spring.factories文件,

在加載了spring.factories文件後,會將配置文件中的各個屬性設置加入緩存中。同時,這裏也加載了ApplicationListener監聽器,這10個監聽器會貫穿springBoot整個生命週期。

關於SpringApplication的實例化流程,限於篇幅,將於後面內容進行分析。隨後,SpringBoot將進入自動化啓動流程。

這裏,進入SpringApplication的run()方法:

/**
 * 運行這個Spring應用,創建併產生一個新的應用上下文
 * {@link ApplicationContext}.
 * @param args 來自於Java程序main方法中的參數
 * @return a running 
 */
public ConfigurableApplicationContext run(String... args) {
    //【1】初始化時間監控器
    StopWatch stopWatch = new StopWatch();
    //開始記錄啓動時間,啓動一個未命名的任務。如果在不調用該方法的情況下調用{@link #stop()}或計時方法,則結果是未定義的。
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    //【2】初始化Spring異常報告集合
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    //【3】java.awt.headless是J2SE的一種模式用於在缺少顯示屏、鍵盤或者鼠標時的系統配置
    // 很多監控工具如jconsole 需要將該值設置爲true,系統變量默認爲true
    configureHeadlessProperty();
    //【4】獲取spring.factories中的監聽器變量
    // 參數args:爲指定的參數數組,默認爲當前類SpringApplication
    //獲取並啓動監聽器,即初始化監聽器
    SpringApplicationRunListeners listeners = getRunListeners(args);
    //在run方法第一次啓動時立即調用。可以用於非常早期的初始化。
    listeners.starting();
    try {
        //【5】裝配參數
	ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
	//【6】初始化容器環境
	ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
	//【7】設置需要忽略的Bean信息
	configureIgnoreBeanInfo(environment);
	//【7.1】打印banner,啓動的Banner就是在這一步打印出來的。
	Banner printedBanner = printBanner(environment);
	//【8】創建容器
	context = createApplicationContext();
	//【9】實例化SpringBootExceptionReporter.class,用來支持報告關於啓動的錯誤
	exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
	//【10】準備容器,裝配參數:容器屬性、容器環境、監聽器屬性、應用對象
	prepareContext(context, environment, listeners, applicationArguments, printedBanner);
	//【11】刷新容器
	refreshContext(context);
	//【12】刷新容器後的拓展接口,在容器被刷新之後調用。
	afterRefresh(context, applicationArguments);
	//【13】停止監聽器
	stopWatch.stop();
	//【14】在啓動時記錄應用程序日誌信息。
	if (this.logStartupInfo) {
	    new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
	}
	//【15】使用廣播和回調機制通知監聽器springboot容器啓動成功(容器已經被刷新,應用程序已經啓動)
	listeners.started(context);
        【16】容器bean的回調。
	callRunners(context, applicationArguments);
    } catch (Throwable ex) {
	handleRunFailure(context, ex, exceptionReporters, listeners);
	throw new IllegalStateException(ex);
    }
    try {
        【17】使用廣播和回調機制通知監聽器springboot容器已成功running
        listeners.running(context);
    } catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, null);
	throw new IllegalStateException(ex);
    }
    【18】返回容器
    return context;
}

Spring中加載一個應用,主要是通過一些複雜的配置實現,這裏看來,SpringBoot幫我們將這些配置工作提前實現了。

從上面SpringApplication類中run()方法的源碼,我們基本上知道SpringBoot在主程序執行run()方法後,啓動如下關鍵流程:

  • 【1】初始化時間監聽器,開始記錄項目的啓動時間
  • 【2】初始化Spring異常報告集合
  • 【3】系統監控工具設置
  • 【4】獲取並啓動監聽器,即初始化監聽器。(獲取spring.factories中的監聽器變量)
  • 【5】裝配參數
  • 【6】初始化容器環境
  • 【7】設置需要忽略的Bean信息,包括打印Banner,啓動的Banner就是在這一步打印出來的。
  • 【8】創建容器
  • 【9】實例化SpringBootExceptionReporter.class,用來支持報告關於啓動的錯誤
  • 【10】準備容器,裝配參數
  • 【11】刷新容器
  • 【12】刷新容器後的擴展接口,在容器刷新後調用
  • 【13】停止監聽器
  • 【14】在啓動時記錄應用程序日誌信息。
  • 【15】使用廣播和回調機制通知監聽器springboot容器啓動成功(容器已經被刷新,應用程序已經啓動)
  • 【16】容器bean的回調。
  • 【17】使用廣播和回調機制通知監聽器springboot容器已成功running。
  • 【18】返回容器

接下來,逐步分析SpringBoot在主程序執行run()方法後的關鍵流程:


第一步:初始化時間監聽器(開始記錄項目的啓動時間)

初始化時間監聽器,開始記錄項目啓動時間:

StopWatch stopWatch = new StopWatch();
stopWatch.start();

這裏,初始化構造一個時間監聽器對象,進入start()方法,開始記錄啓動時間,並啓動一個未命名的任務:

/**
 * 開始記錄啓動時間,啓動一個未命名的任務。
 * 如果在不調用該方法的情況下調用{@link #stop()}或計時方法,則結果是未定義的。
 * @param taskName 要啓動的任務的名稱
 */
public void start(String taskName) throws IllegalStateException {
    if (this.currentTaskName != null) {
        throw new IllegalStateException("Can't start StopWatch: it's already running");
    }
    this.currentTaskName = taskName;
    this.startTimeMillis = System.currentTimeMillis();
}

 第二步:初始化Spring異常報告集合

初始化Spring異常報告集合,這裏將構造出一個SpringBootExceptionReporter接口類的集合對象:

Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();

OK,進入SpringBootExceptionReporter看一下:

/**
 * 該類是一個用於支持自定義報告{@link SpringApplication}啓動錯誤的回調接口類。
 * {@link SpringBootExceptionReporter}是通過{@link SpringFactoriesLoader}加載的,
 * 並且必須聲明一個帶有單個{@link ConfigurableApplicationContext}參數的公共構造函數。
 */
@FunctionalInterface
public interface SpringBootExceptionReporter {
	/**
	 * 啓動失敗,則上報失敗信息。
         * 
         * 如果報告失敗,返回true;
         * 如果發生默認報告,則返回false
	 */
	boolean reportException(Throwable failure);

}

第三步: 系統監控工具設置

java.awt.headless是J2SE的一種模式用於在缺少顯示屏、鍵盤或者鼠標時的系統配置。

configureHeadlessProperty();

很多監控工具如jconsole 需要將該值設置爲true,系統變量默認爲true。看下源碼: 

private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless";
private void configureHeadlessProperty() {
    System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
        System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, 
        Boolean.toString(this.headless)));
}

 第四步:獲取並啓動監聽器,即初始化監聽器(獲取spring.factories中的監聽器變量)

//【4】獲取spring.factories中的監聽器變量
// 參數args:爲指定的參數數組,默認爲當前類SpringApplication
//獲取並啓動監聽器,即初始化監聽器
SpringApplicationRunListeners listeners = getRunListeners(args);
//在run方法第一次啓動時立即調用。可以用於非常早期的初始化。
listeners.starting();

看上邊程序,首先獲取監聽器getRunListeners(),然後啓動監聽器starting()。逐步分析:

【1】獲取監聽器

//第一步:獲取並啓動監聽器,即初始化監聽器
SpringApplicationRunListeners listeners = getRunListeners(args);

進入getRunListeners()方法,

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

分析:

  • 返回一個容器監聽器對象,
  • 入參agrs是一個默認爲空的字符串數組;
  • getSpringFactoriesInstances()方法將args作爲入參,獲取Spring工廠實例。在啓動時最終將獲取spring.factories對應的監聽器:
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

進入getSpringFactoriesInstances(),最終將返回一個工廠實例。

/***
 * 獲取Spring工廠實例
 */
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
    //獲取類加載器
    ClassLoader classLoader = getClassLoader();
    //使用給定的類加載器,從{@value #FACTORIES_RESOURCE_LOCATION}("META-INF/spring.factories")裝入給定類型的工廠實現的完全限定類名。
    Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    //根據啓動類的類型、參數類型和類加載器創建工廠實例集合
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}

loadFactoryNames()方法,將取到工廠實例name的集合:

進入createSpringFactoriesInstances()方法,它展示了整個SpringBoot框架獲取factories的方式:

@SuppressWarnings("unchecked")
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,ClassLoader classLoader, Object[] args, Set<String> names) {
    List<T> instances = new ArrayList<>(names.size());
    for (String name : names) {
        try {
            //加載class類文件到內存中
            Class<?> instanceClass = ClassUtils.forName(name, classLoader);
	    Assert.isAssignable(type, instanceClass);
	    Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
            //通過反射創建實例
	    T instance = (T) BeanUtils.instantiateClass(constructor, args);
	    instances.add(instance);
        } catch (Throwable ex) {
	    throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
        }
    }
    return instances;
}

通過反射獲取實例,將觸發EventPublishingRunListener的構造方法:

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {

	private final SpringApplication application;

	private final String[] args;
        
        /**
	 * 將所有事件廣播給所有已註冊的監聽器,讓監聽器來忽略它們不感興趣的事件。
	 * 監聽器通常會對傳入的事件對象執行相應的{@code instanceof}檢查。
	 *
	 * 默認情況下,在調用線程中調用所有監聽器。
	 * 這允許流氓監聽器阻塞整個應用程序的危險,但只增加了最小的開銷。
	 * 指定一個可選的任務執行器,以便在不同的線程中(例如從線程池中)執行監聽器。
	 */
	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);
		}
	}
    ... ...
}

 這裏加載所有監聽器,進入addApplicationListener()方法看一下:

@Override
public void addApplicationListener(ApplicationListener<?> listener) {
    synchronized (this.retrievalMutex) {
    //如果已經註冊,則顯式刪除代理的目標
    //爲了避免對同一個監聽器的重複調用。
    Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
    if (singletonTarget instanceof ApplicationListener) {
        this.defaultRetriever.applicationListeners.remove(singletonTarget);
    }
    /**
     * 在靜態類AbstractApplicationEventMulticaster中定義了一個目標監聽器實例
     * private final ListenerRetriever defaultRetriever = new ListenerRetriever(false);
     *
     * 默認目標監聽器實例調用了一個AbstractApplicationEventMulticaster類的內部類
     * ListenerRetriever實例對象
     */
    this.defaultRetriever.applicationListeners.add(listener);
    this.retrieverCache.clear();
    }
}

OK,進入applicationListeners()方法,也就進入這個內部類:

/**
 * ListenerRetriever 是一個Helper類,它封裝了一組特定的目標監聽器偵聽器,允許有效地檢索預先過
 * 濾的監聽器。並且此幫助器的實例按事件類型和源類型緩存。
 */
private class ListenerRetriever {

    public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();

    public final Set<String> applicationListenerBeans = new LinkedHashSet<>();

    private final boolean preFiltered;

    public ListenerRetriever(boolean preFiltered) {
        this.preFiltered = preFiltered;
    }

    public Collection<ApplicationListener<?>> getApplicationListeners() {
        List<ApplicationListener<?>> allListeners = new ArrayList<>(
		this.applicationListeners.size() + this.applicationListenerBeans.size());
	allListeners.addAll(this.applicationListeners);
	if (!this.applicationListenerBeans.isEmpty()) {
                //Bean工廠實例化
		BeanFactory beanFactory = getBeanFactory();
                //遍歷當前的監聽器Bean實例集合
		for (String listenerBeanName : this.applicationListenerBeans) {
			try {
                                //從監聽器bean集合中獲取監聽器的實例Bean對象
				ApplicationListener<?> listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class);
				if (this.preFiltered || !allListeners.contains(listener)) {
                                        //裝載監聽器實例bean
					allListeners.add(listener);
				}
			} catch (NoSuchBeanDefinitionException ex) {
			// Singleton listener instance (without backing bean definition) disappeared -
			// probably in the middle of the destruction phase
			}
		}
	}
	if (!this.preFiltered || !this.applicationListenerBeans.isEmpty()) {
                //對監聽器排序
		AnnotationAwareOrderComparator.sort(allListeners);
	}
	return allListeners;
	}
}

 通過this.defaultRetriever.applicationListeners.add(listener),將監聽器spring.factories中的監聽器傳遞給SimpleApplicationEventMulticaster中。觸發EventPublishingRunListener的構造方法然後獲取到所有的監聽器實例。

內部類SimpleApplicationEventMulticaster繼承了AbstractApplicationEventMulticaster,然後由AbstractApplicationEventMulticaster實現三個接口。繼承關係如下:

 【2】啓動監聽器

下一步啓動監聽器,繼續看SpringApplication.java類:

//在run方法第一次啓動時立即調用。可以用於非常早期的初始化。
listeners.starting();

從獲取監聽器分析,可知這裏啓動EventPublishingRunListener監聽器,即啓動時間發佈監聽器,用來發布啓動事件。

EventPublishingRunListener作爲早期的監聽器,執行後邊的started()方法,將發佈監聽事件。這裏,我們進入該類的starting()

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

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

啓動監聽器時,調用starting()方法,這裏我們進入starting()方法,調用multicastEvent()方法:

@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    //解析事件類型
    ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
    //獲取線程池
    Executor executor = getTaskExecutor();
    for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
        //如果爲空則同步處理
        if (executor != null) {
            //異步發送監聽事件
	    executor.execute(() -> invokeListener(listener, event));
	} else {
            //同步發送監聽事件
	    invokeListener(listener, event);
	}
    }
}

以日誌監聽器爲例:

/**
 * 繼承自ApplicationListener接口的方法
 *
 * @param event
 */
@Override
public void onApplicationEvent(ApplicationEvent event) {
    //Springboot啓動時
    if (event instanceof ApplicationStartingEvent) {
        onApplicationStartingEvent((ApplicationStartingEvent) event);
    }
    //環境準備完成時
    else if (event instanceof ApplicationEnvironmentPreparedEvent) {
	onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
    }
    //容器環境配置完成後
    else if (event instanceof ApplicationPreparedEvent) {
        onApplicationPreparedEvent((ApplicationPreparedEvent) event);
    }
    //容器關閉時
    else if (event instanceof ContextClosedEvent
	&& ((ContextClosedEvent) event).getApplicationContext().getParent() == null) {
	onContextClosedEvent();
    }
    //容器啓動失敗時
    else if (event instanceof ApplicationFailedEvent) {
	onApplicationFailedEvent();
    }
}

第五步:裝配參數

裝配參數,構建一個默認的應用對象:

ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

進入DefaultApplicationArguments()方法:

public DefaultApplicationArguments(String[] args) {
    Assert.notNull(args, "Args must not be null");
    this.source = new Source(args);
    this.args = args;
}

第六步:初始化容器環境

//初始化容器環境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);

查看prepareEnvironment()方法:

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
    //獲取相應的配置環境
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    ConfigurationPropertySources.attach(environment);
    //監聽環境已準備事件,併發布
    listeners.environmentPrepared(environment);
    bindToSpringApplication(environment);
    if (!this.isCustomEnvironment) {
	environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
			deduceEnvironmentClass());
    }
    ConfigurationPropertySources.attach(environment);
    return environment;
}

這裏,環境信息配置,我們先進入getOrCreateEnvironment()方法:

private ConfigurableEnvironment getOrCreateEnvironment() {
    if (this.environment != null) {
        return this.environment;
    }
    //根據web環境類型判斷,返回對應的環境類型配置
    switch (this.webApplicationType) {
        case SERVLET:
            return new StandardServletEnvironment();
	case REACTIVE:
            return new StandardReactiveWebEnvironment();
	default:
	    return new StandardEnvironment();
	}
}

看下枚舉類WebApplicationType:

public enum WebApplicationType {
	/**
	 *應用程序不應作爲web應用程序運行,也不應啓動嵌入式web服務器。
	 */
	NONE,

	/**
	 * 應用程序應該作爲基於servlet的web應用程序運行,並且應該啓動嵌入式servlet web服務器。
	 */
	SERVLET,

	/**
	 *應用程序應該作爲反應性web應用程序運行,並應該啓動嵌入式反應性web服務器。
	 */
	REACTIVE;
    ... ...
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章