1. 程式人生 > 實用技巧 >springboot(十四) 原始碼分析 —— SpringApplication的建立

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裡面的資源並例項化。