91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

[SpringBoot]深入淺出剖析SpringBoot的應用類型識別機制

發布時間:2020-07-18 19:58:44 來源:網絡 閱讀:1802 作者:GitShare 欄目:軟件技術

微信號:GitShare
微信公眾號:愛折騰的稻草
如有問題或建議,請在公眾號留言[1]

前續

為幫助廣大SpringBoot用戶達到“知其然,更需知其所以然”的境界,作者將通過SpringBoot系列文章全方位對SpringBoot2.0.0.RELEASE版本深入分解剖析,讓您深刻的理解其內部工作原理。

  • 1、[SpringBoot]利用SpringBoot快速構建并啟動項目

  • 2、[SpringBoot]詳解SpringBoot應用的啟動過程

推斷應用的類型

SpringBoot啟動時,在創建SpringApplication對象時,會自動去識別并設置當前應用是普通web應用、響應式web應用還是非web應用,其內部實現原理是什么?  
首先看看源代碼

/**
* 推斷應用的類型
*/

private WebApplicationType deduceWebApplicationType() {
    if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
            && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {
        return WebApplicationType.REACTIVE;
    }
    for (String className : WEB_ENVIRONMENT_CLASSES) {
        if (!ClassUtils.isPresent(className, null)) {
            return WebApplicationType.NONE;
        }
    }
    return WebApplicationType.SERVLET;
}
  • ClassUtils.isPresent()方法:  
    其作用是判斷所提供的類名的類是否存在,且可以被加載。源代碼如下:

public static boolean isPresent(String className, @Nullable ClassLoader classLoader) {
    try {
        forName(className, classLoader);
        return true;
    }
    catch (Throwable ex) {
        // Class or one of its dependencies is not present...
        return false;
    }
}

調用了forName()方法,如果出現異常,則返回false,也就是提供目標類不存在。

  • forName()方法的源碼剖析:

public static Class<?> forName(String name, @Nullable ClassLoader classLoader)
        throws ClassNotFoundException, LinkageError {

    Assert.notNull(name, "Name must not be null");
    //根據基本類的JVM命名規則(如果合適的話),將給定的類名name解析為基本類型的包裝類
    Class<?> clazz = resolvePrimitiveClassName(name);
    if (clazz == null) {
        //commonClassCache是包含java.lang包下所有類,將類的類名作為鍵,對應類作為值的一個Map集合。
        clazz = commonClassCache.get(name); //根據類名,獲取commonClassCache集合中的值,如果為空,表示目標類不是java.lang包的下類,即不是原始類型。
    }
    if (clazz != null) {
        //如果根據類名,已經獲取到了類,則直接返回該類。
        return clazz;
    }

    //判斷clas屬性值是否為數組對象。比如:java.lang.String[]
    // "java.lang.String[]" style arrays
    if (name.endsWith(ARRAY_SUFFIX)) { 
        //如果是,則將類名后的“[]”方括號截去,返回java.lang.String,遞歸查找類名,找到后,將Class類型轉換為數組。
        String elementClassName = name.substring(0, name.length() - ARRAY_SUFFIX.length());
        Class<?> elementClass = forName(elementClassName, classLoader);
        return Array.newInstance(elementClass, 0).getClass();
    }

    //class屬性值是否為數組對象的二進制表示。比如:[Ljava.lang.String
    // "[Ljava.lang.String;" style arrays
    if (name.startsWith(NON_PRIMITIVE_ARRAY_PREFIX) && name.endsWith(";")) {
        //如果是,則將值的“[L”部分截去,遞歸查找類名,找到后,將對應的Class類型轉換為數組
        String elementName = name.substring(NON_PRIMITIVE_ARRAY_PREFIX.length(), name.length() - 1);
        Class<?> elementClass = forName(elementName, classLoader);
        return Array.newInstance(elementClass, 0).getClass();
    }

    //class屬性值是否為二維數組
    // "[[I" or "[[Ljava.lang.String;" style arrays
    if (name.startsWith(INTERNAL_ARRAY_PREFIX)) {
        String elementName = name.substring(INTERNAL_ARRAY_PREFIX.length());
        Class<?> elementClass = forName(elementName, classLoader);
        return Array.newInstance(elementClass, 0).getClass();
    }

    //獲取classLoader
    ClassLoader clToUse = classLoader;
    if (clToUse == null) {
         //如果classLoader為空,則獲取默認的classLoader對象。
        clToUse = getDefaultClassLoader();
    }
    try {
        //返回加載后的類
        return (clToUse != null ? clToUse.loadClass(name) : Class.forName(name));
    }
    catch (ClassNotFoundException ex) {
        //用于處理內部類的情況。
        int lastDotIndex = name.lastIndexOf(PACKAGE_SEPARATOR);
        if (lastDotIndex != -1) {
            //拼接內部類的名字。
            String innerClassName = name.substring(0, lastDotIndex) + INNER_CLASS_SEPARATOR + name.substring(lastDotIndex + 1);
            try {
                return (clToUse != null ? clToUse.loadClass(innerClassName) : Class.forName(innerClassName));
            }
            catch (ClassNotFoundException ex2) {
                // Swallow - let original exception get through
            }
        }
        throw ex;
    }
}
  • 再來看看使用到的常量值:

    • REACTIVE_WEB_ENVIRONMENT_CLASS:org.springframework.web.reactive.DispatcherHandler

    • MVC_WEB_ENVIRONMENT_CLASS:org.springframework.web.servlet.DispatcherServlet

    • WEB_ENVIRONMENT_CLASSES:{"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext" }
      也就是說,

  • 1、如果應用程序中存在org.springframework.web.reactive.DispatcherHandler這個類,則表示是一個響應式web應用,項目在啟動時,需要去
    加載啟動內嵌的響應式web服務器

  • 2、如果應用程序中既不存在javax.servlet.Servlet類,也不存在org.springframework.web.context.ConfigurableWebApplicationContext這個類,則
    表示當前應用不是一個web應用,啟動時無需加載啟動內嵌的web服務器。

  • 3、除上述兩種情況外,則表示當前應用是一個servlet的web應用,啟動時需要加載啟動內嵌的servlet的web服務器(比如Tomcat)。

推斷并設置main方法的定義類(啟動類)

SpringBoot啟動時,在創建SpringApplication對象時,最后會推斷并設置main方法的定義類(啟動類),其實現原理是什么呢?
先看看源代碼;

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;
}
  • java.lang.StackTraceElement是什么?
    類元素代表一個堆棧幀。除了一個在堆棧的頂部所有的棧幀代表一個方法調用。在堆棧頂部的幀表示在將其生成的堆棧跟蹤的執行點。

    • stackTraceElement.getMethodName() 返回一個包含由該堆棧跟蹤元素所表示的執行點的方法的名稱。

    • stackTraceElement.getClassName() 返回一個包含由該堆棧跟蹤元素所表示的執行點類的完全限定名。

  • java.lang.Class.forName()的作用是什么?
    java.lang.Class.forName(String name, boolean initialize, ClassLoader loader) 方法返回與給定字符串名的類或接口的Class對象,使用給定的類加載器。

參數說明
    - name :這是所需類的完全限定名稱。
    - initialize : 這說明這個類是否必須初始化。
    - loader : 這是必須加載的類的類加載器。
異常說明
    - LinkageError : 如果聯動失敗。
    - ExceptionInInitializerError : 如果這種方法所引發的初始化失敗。
    - ClassNotFoundException : 如果類不能位于由指定的類加載器。
參數使用
    - ClassLoader loader:如果該參數加載器loader 為空,通過引導類加載器加載類。
    - boolean initialize:如果它沒有被初始化,則initialize參數為true

通過上面知識點的講解,deduceMainApplicationClass的作用就非常清晰了,主要是獲取當前方法調用棧,遍歷調用堆棧信息找到main函數的類,并返回該類。

總結
  • 1、SpringBoot是通過調用ClassUtils類的isPresent方法,檢查classpath中是否存在org.springframework.web.reactive.DispatcherHandler類、
    javax.servlet.Servlet類和org.springframework.web.context.ConfigurableWebApplicationContext類來判斷當前應用是響應式Web應用,還是普通的Servlet的Web應用,還是非Web應用。

  • 2、SpringBoot是通過獲取當前方法的調用棧信息,來判斷當前main函數所在的類。

后記

為幫助廣大SpringBoot用戶達到“知其然,更需知其所以然”的境界,作者將通過SpringBoot系列文章全方位對SpringBoot2.0.0.RELEASE版本深入分解剖析,讓您深刻的理解其內部工作原理。 

敬請關注[愛折騰的稻草(GitShare)]公眾號,為您提供更多更優質的技術教程。


[SpringBoot]深入淺出剖析SpringBoot的應用類型識別機制圖注:愛折騰的稻草


向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

常宁市| 淮安市| 文水县| 利津县| 渑池县| 左贡县| 木兰县| 泰兴市| 澜沧| 慈利县| 玉龙| 渑池县| 贵港市| 玛纳斯县| 崇仁县| 乐山市| 子长县| 乐东| 鄂托克前旗| 色达县| 宾阳县| 云南省| 长宁县| 浏阳市| 青田县| 乌海市| 芒康县| 改则县| 桦甸市| 滦南县| 资中县| 襄汾县| 定日县| 密山市| 丰城市| 嘉义县| 方城县| 壤塘县| 桃园市| 浮山县| 宿迁市|