1. 程式人生 > >探索 SpringBoot (二) 啟動流程詳解(上)

探索 SpringBoot (二) 啟動流程詳解(上)

探索 SpringBoot (二) 啟動流程詳解

文章目錄

1 緣起

從 SpringApplication.run(xxx.class, args); 開始,看看 SpringBoot 為我們做了什麼

2 環境配置

  • 1 本文原始碼基於 spring-boot-2.1.0.RELEASE
  • 2 本文將從兩個階段來講解 SpringBoot 的具體啟動流程,分別是 準備階段 和 執行階段
  • 3 因為內容比較多 打算分成兩篇,上篇將 準備階段 下篇將執行階段

3 SpringApplication 準備階段

new SpringApplication(primarySources)

  • 1 呼叫 run 方法 傳入配置主類,和執行時候引數
public static ConfigurableApplicationContext run(Class<?> primarySource,
		String... args) {
	return run(new Class<?>[] { primarySource }, args);
}
  • 2 進入準備階段並且執行,我們接著往下看看 SpringApplication 的構造方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
		String[] args) {
	return new SpringApplication(primarySources).run(args);
}
  • 3 這裡主要做了兩件事情
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();
	setInitializers((Collection) getSpringFactoriesInstances(
			ApplicationContextInitializer.class));
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	this.mainApplicationClass = deduceMainApplicationClass();
}
3.1 推斷
    // 推斷 web app 型別
    WebApplicationType.deduceFromClasspath();
    
    // 推斷執行主類
    this.mainApplicationClass = deduceMainApplicationClass();
    
3.2 設定  

    // 根據 Spring 工廠方法載入 Initializers
    setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
				
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

3.1 推斷 web app 型別

WebApplicationType.deduceFromClasspath();
  • 我們接著往下看看
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;
}
- 這裡就只做了 3 件事情
1  判斷 常量 WEBFLUX_INDICATOR_CLASS 所表示的類 是否存在並且可以載入 如果 true 則返回 WebApplicationType.REACTIVE

2 判斷 常量 SERVLET_INDICATOR_CLASSES 所表示的類 是否存在並且可以載入
如果有一個 false 返回 WebApplicationType.NONE

3 如果上面條件都沒滿足 返回 WebApplicationType.SERVLET

3.2 推斷 執行主類

this.mainApplicationClass = 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” 方法的類並且返回

3.3 根據 (order )採用 SpringFactory 的方式初始化 ApplicationContextInitializer 的實現類,並且排序

setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));

...

public void setInitializers(
			Collection<? extends ApplicationContextInitializer<?>> initializers) {
	this.initializers = new ArrayList<>();
	this.initializers.addAll(initializers);
}
  • 這個方法就是把初始化得到的實現類設定成成員變數,我們關鍵看下面這個方法
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
	return getSpringFactoriesInstances(type, new Class<?>[] {});
}
	
	
....

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, Object... args) {
	ClassLoader classLoader = getClassLoader();
	// 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;
}
 這裡主要做了 2 件事
 
 1 從 classpath 下面獲取 spring.facoty 中, ApplicationContextInitializer 獲取 該介面實現類的全類名
 2 根據獲取的 全類名 初始化 實現類 並且排序
  • 好吧 結合程式碼再看看
Set<String> names = new LinkedHashSet<>(
				SpringFactoriesLoader.loadFactoryNames(type, classLoader));
				
				
...


public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
	String factoryClassName = factoryClass.getName();
	return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
	MultiValueMap<String, String> result = cache.get(classLoader);
	if (result != null) {
		return result;
	}

	try {
		Enumeration<URL> urls = (classLoader != null ?
				classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
				ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
		result = new LinkedMultiValueMap<>();
		while (urls.hasMoreElements()) {
			URL url = urls.nextElement();
			UrlResource resource = new UrlResource(url);
			Properties properties = PropertiesLoaderUtils.loadProperties(resource);
			for (Map.Entry<?, ?> entry : properties.entrySet()) {
				String factoryClassName = ((String) entry.getKey()).trim();
				for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
					result.add(factoryClassName, factoryName.trim());
				}
			}
		}
		cache.put(classLoader, result);
		return result;
	}
	catch (IOException ex) {
		throw new IllegalArgumentException("Unable to load factories from location [" +
				FACTORIES_RESOURCE_LOCATION + "]", ex);
	}
}
  • 這裡的主要作用就是 載入 classpath 下面所有叫 META-INF/spring.factories 的檔案,並且解析裡面的 key 和 value 放入 map 中返回

  • 我們再來看看第二點

List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
				classLoader, args, names);
				
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<?> 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;
}
  • 這裡無非就是通過反射 初始化 上面的實現類

3.4 根據 (order )採用 SpringFactory 的方式初始化 ApplicationListener 的實現類,並且排序

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
  • 這個的實現就和上面類似了 參照上面吧。

到此為止 SpringApplicaiton 的準備階段就結束啦,我們一起看看 執行階段做了啥吧。