springboot(十四) 原始碼分析 —— SpringApplication的建立
SpringApplication的建立
基於 2.2.9.RELEASE的版本,啟動專案debug
@SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {return new SpringApplication(primarySources).run(args); }
run方法最後的走到SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) 來例項化我們的SpringApplication
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 1.1 this.resourceLoader = resourceLoader;
Assert.notNull(primarySources,"PrimarySources must not be null");
// 1.2 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 1.3 this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 1.4 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 1.5 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 1.6 this.mainApplicationClass = deduceMainApplicationClass(); }
1.1 resourceLoader
屬性,資源載入器
此處debug顯示為null;感興趣的小夥伴可以看一看:Spring 統一資源載入策略(https://blog.csdn.net/cuiwjava/article/details/108940768)
1.2 primarySources
屬性,主要的 Java Config 類的陣列
在此處就是對應我們的啟動類DemoApplication 類。
1.3 webApplicationType
屬性,呼叫WebApplicationType#deduceFromClasspath()
方法,通過 classpath ,判斷 Web 應用型別
static WebApplicationType deduceFromClasspath() {
// WebApplicationType.REACTIVE 型別 if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null) && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) { return WebApplicationType.REACTIVE; }
// WebApplicationType.NONE 型別 for (String className : SERVLET_INDICATOR_CLASSES) { if (!ClassUtils.isPresent(className, null)) { return WebApplicationType.NONE; } }
// WebApplicationType.SERVLET 型別。可以醬紫的判斷的原因是,引入 Spring MVC 時,如果是內嵌的 Web 應用,會引入 Servlet 類。 return WebApplicationType.SERVLET; }
1.4 initializers
屬性,ApplicationContextInitializer 陣列
通過#getSpringFactoriesInstances(Class<T> type)
方法,進行獲得 ApplicationContextInitializer 型別的物件陣列。
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); // 1.4.1 載入指定型別對應的,在 `META-INF/spring.factories` 裡的類名的陣列 Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 1.4.2 建立物件們 List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// 1.4.3 排序物件們 AnnotationAwareOrderComparator.sort(instances); return instances; }
1.4.1,呼叫SpringFactoriesLoader#loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader)
方法,載入指定型別對應的,在META-INF/spring.factories
裡的類名的陣列。在META-INF/spring.factories
檔案中,會以 KEY-VALUE 的格式,配置每個類對應的實現類們。關於 SpringFactoriesLoader 的該方法,我們就不去細看了。
1.4.2,呼叫#createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> 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 {
// 獲得 name 對應的類 Class<?> instanceClass = ClassUtils.forName(name, classLoader);
// 判斷類是否實現自 type 類 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; }
1.4.3,呼叫 AnnotationAwareOrderComparator#sort(List<?> list) 方法,排序物件們。例如說,類上有 @Order 註解。
當前Demo是在 Spring MVC 的環境下,initializers
屬性的結果如下圖:
1.5 listeners
屬性,ApplicationListener 陣列
也是通過#getSpringFactoriesInstances(Class<T> type)
方法,進行獲得 ApplicationListener 型別的物件陣列。listeners
屬性的結果如下圖:
1.6 mainApplicationClass
屬性
呼叫#deduceMainApplicationClass()
方法,獲得是呼叫了哪個#main(String[] args)
方法,程式碼如下:
private Class<?> deduceMainApplicationClass() { try {
// 獲得當前 StackTraceElement 陣列 StackTraceElement[] stackTrace = new RuntimeException().getStackTrace(); for (StackTraceElement stackTraceElement : stackTrace) {
// 判斷哪個執行了 main 方法 if ("main".equals(stackTraceElement.getMethodName())) { return Class.forName(stackTraceElement.getClassName()); } } } catch (ClassNotFoundException ex) { // Swallow and continue } return null; }
此處對應的就是我們Demo的啟動類,DemoApplication,到處我們SpringApplication建立完成。程式碼不是很複雜,主要部分是載入META-INF/spring.factories裡面的資源並例項化。