天天看點

【 SpringBoot 】- [SpringApplication] -- 初始化

·康忙,先跟着下圖走一遍靜靜心...

【 SpringBoot 】- [SpringApplication] -- 初始化
  • 建立SpringBoot項目 -> main 啟動

@SpringBootApplication
public class DemoApplication {

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

}
           
  • -> SpringApplication.run(DemoApplication.class, args)

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);
    }
           
  • -> new SpringApplication,這裡隻分析初始化,run啟動後面分析

public SpringApplication(Class<?>... primarySources) {
        this((ResourceLoader)null, primarySources);
    }

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.sources = new LinkedHashSet();
        this.bannerMode = Mode.CONSOLE;
        this.logStartupInfo = true;
        this.addCommandLineProperties = true;
        this.addConversionService = true;
        this.headless = true;
        this.registerShutdownHook = true;
        this.additionalProfiles = new HashSet();
        this.isCustomEnvironment = false;
        // 資源加載器
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        // Set<Class<?>> primarySources:意義在于存放 Java Config 類,這裡就是  DemoApplication
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
// 1.通過 classpath ,判斷 Web 應用類型
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 2.設定 List<ApplicationContextInitializer<?>> initializers
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 3.設定 List<ApplicationListener<?>> listeners
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
// 4.擷取調用 main 方法的啟動類:Class<?> mainApplicationClass
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }
           

目錄.  第1. --> WebApplicationType.deduceFromClasspath()

 第2與3. --> 都調用 SpringApplication.getSpringFactoriesInstances(),這裡拿 type=ApplicationContextInitializer.class 做分析

第4. --> SpringApplication.deduceMainApplicationClass()

第1. --> WebApplicationType.deduceFromClasspath()

static WebApplicationType deduceFromClasspath() {
        if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {
            return REACTIVE;
        } else {
            String[] var0 = SERVLET_INDICATOR_CLASSES;
            int var1 = var0.length;

            for(int var2 = 0; var2 < var1; ++var2) {
                String className = var0[var2];
                if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
                    return NONE;
                }
            }
//private static final String[] SERVLET_INDICATOR_CLASSES = new String[]
//{"javax.servlet.Servlet","org.springframework.web.context.ConfigurableWebApplicationContext"} 
// 判斷classpath下是否存在該數組裡面的class,存在則傳回 SERVLET
            return SERVLET;
        }
    }
           

第2與3. --> 都調用 SpringApplication.getSpringFactoriesInstances(),這裡拿 type=ApplicationContextInitializer.class 做分析

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
        return this.getSpringFactoriesInstances(type, new Class[0]);
    }

    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = this.getClassLoader();
// 1. 加載配置檔案 'META-INF/spring.factories',并且從中擷取指定類型的類名的數組,放入set去重
        Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 2. 建立對象執行個體
        List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// 3. 排序對象集合
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }
           

 ·這裡隻分析介紹 --> SpringFactoriesLoader.loadFactoryNames()

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
        // 獲得factoryClassName,我們在這裡的 factoryClassName =org.springframework.context.ApplicationContextInitializer.
        String factoryClassName = factoryClass.getName();
        // 擷取 factoryClassName 為key下的 className 集合
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
    }

// 加載 META-INF/spring.factories 檔案,資訊以 key-value 形式存入 map
    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        // 首先從緩存 Map<ClassLoader, MultiValueMap<String, String>> cache 中擷取
        MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
        if (result != null) {
            return result;// 緩存中存在,傳回
        } else {
            try {
                // 加載 META-INF/spring.factories 檔案
                Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                LinkedMultiValueMap result = new LinkedMultiValueMap();

                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    // 把resource 轉為 properties
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();

                    while(var6.hasNext()) {
                        Entry<?, ?> entry = (Entry)var6.next();
                        String factoryClassName = ((String)entry.getKey()).trim();
                        String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                        int var10 = var9.length;

                        for(int var11 = 0; var11 < var10; ++var11) {
                            String factoryName = var9[var11];
                            // 把 factoryClassName 下的所有 className 存入 result中
                            result.add(factoryClassName, factoryName.trim());
                        }
                    }
                }
                // 放入緩存
                cache.put(classLoader, result);
                return result;
            } catch (IOException var13) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
            }
        }
    }
           

斷點調試檢視 result

·以 org.springframework.context.ApplicationContextInitializer 為key的集合中共有6個元素

【 SpringBoot 】- [SpringApplication] -- 初始化

因為 /Users/~/.m2/repository/org/springframework/boot/spring-boot/2.1.6.RELEASE/spring-boot-2.1.6.RELEASE.jar!/META-INF/spring.factories 中有4個,/Users/~/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.1.6.RELEASE/spring-boot-autoconfigure-2.1.6.RELEASE.jar!/META-INF/spring.factories有2個

# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
           
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
           

·以 org.springframework.context.ApplicationListener 為key的集合中共有10個元素

【 SpringBoot 】- [SpringApplication] -- 初始化

因為 /Users/~/.m2/repository/org/springframework/boot/spring-boot/2.1.6.RELEASE/spring-boot-2.1.6.RELEASE.jar!/META-INF/spring.factories 中有9個,/Users/~/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.1.6.RELEASE/spring-boot-autoconfigure-2.1.6.RELEASE.jar!/META-INF/spring.factories有1個

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
           
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
           

· 然後拿着目前class類型下的 names集合,進行執行個體化(利用反射,就不詳細介紹),最後進行排序。

--> classType=ApplicationContextInitializer.class 時擷取到的執行個體如下

【 SpringBoot 】- [SpringApplication] -- 初始化

--> classType=ApplicationListener.class 時擷取到的執行個體如下

【 SpringBoot 】- [SpringApplication] -- 初始化

stop......... 從第2、3步中的思緒中跳出來,分析初始化 SpringApplication 的最後一步

第4. --> SpringApplication.deduceMainApplicationClass()

private Class<?> deduceMainApplicationClass() {
        try {
            // 擷取目前方法調用棧元素
            StackTraceElement[] stackTrace = (new RuntimeException()).getStackTrace();
            StackTraceElement[] var2 = stackTrace;
            int var3 = stackTrace.length;

            for(int var4 = 0; var4 < var3; ++var4) {
                StackTraceElement stackTraceElement = var2[var4];
                // 找到 main 函數的類
                if ("main".equals(stackTraceElement.getMethodName())) {
                // 擷取擷取啟動類
                    return Class.forName(stackTraceElement.getClassName());
                }
            }
        } catch (ClassNotFoundException var6) {
        }

        return null;
    }
           

 debug --> 通過尋找 main 方法找到啟動類 DemoApplication

【 SpringBoot 】- [SpringApplication] -- 初始化

賦與私有屬性 private Class<?> mainApplicationClass ;主要用于日志的列印

【 SpringBoot 】- [SpringApplication] -- 初始化

至此,SpringApplication 的初始化就完成啦!

繼續閱讀