AVt天堂网 手机版,亚洲va久久久噜噜噜久久4399,天天综合亚洲色在线精品,亚洲一级Av无码毛片久久精品

當前位置:首頁 > 科技  > 軟件

類加載機制的源碼解讀

來源: 責編: 時間:2024-09-10 09:49:55 93觀看
導讀前言繼前文深入剖析雙親委派機制之后,本文將引直接走進具體的代碼實現,一探其真正的實現思路。源碼閱讀Tomcat 啟動的起點在于 Bootstrap 類的 main()方法。在 main()方法執行之前,其靜態代碼塊(static{})會率先被執行。因

前言

繼前文深入剖析雙親委派機制之后,本文將引直接走進具體的代碼實現,一探其真正的實現思路。teE28資訊網——每日最新資訊28at.com

源碼閱讀

Tomcat 啟動的起點在于 Bootstrap 類的 main()方法。在 main()方法執行之前,其靜態代碼塊(static{})會率先被執行。因此,我們將首先深入探討靜態代碼塊的運行機制,然后再分析 main()方法的執行流程。teE28資訊網——每日最新資訊28at.com

Bootstrap.static{}

teE28資訊網——每日最新資訊28at.com

static {    // 獲取用戶目錄    String userDir = System.getProperty("user.dir");    // 優先從環境變量獲取 CATALINA_HOME    String home = System.getProperty(Globals.CATALINA_HOME_PROP);    File homeFile = null;    if (home != null) {        File f = new File(home);        try {            homeFile = f.getCanonicalFile();        } catch (IOException ioe) {            homeFile = f.getAbsoluteFile();        }    }    // 若環境變量中未獲取到,則嘗試從 bootstrap.jar 所在目錄的上一級目錄獲取    if (homeFile == null) {        File bootstrapJar = new File(userDir, "bootstrap.jar");        if (bootstrapJar.exists()) {            File f = new File(userDir, "..");            try {                homeFile = f.getCanonicalFile();            } catch (IOException ioe) {                homeFile = f.getAbsoluteFile();            }        }    }    // 若以上兩種方式均未獲取到,則使用用戶目錄作為 CATALINA_HOME    if (homeFile == null) {        File f = new File(userDir);        try {            homeFile = f.getCanonicalFile();        } catch (IOException ioe) {            homeFile = f.getAbsoluteFile();        }    }    // 設置 CATALINA_HOME 屬性    catalinaHomeFile = homeFile;    System.setProperty(Globals.CATALINA_HOME_PROP, catalinaHomeFile.getPath());    // 獲取 CATALINA_BASE,若未設置,則使用 CATALINA_HOME 作為 CATALINA_BASE    String base = System.getProperty(Globals.CATALINA_BASE_PROP);    if (base == null) {        catalinaBaseFile = catalinaHomeFile;    } else {        File baseFile = new File(base);        try {            baseFile = baseFile.getCanonicalFile();        } catch (IOException ioe) {            baseFile = baseFile.getAbsoluteFile();        }        catalinaBaseFile = baseFile;    }    // 設置 CATALINA_BASE 屬性    System.setProperty(Globals.CATALINA_BASE_PROP, catalinaBaseFile.getPath());}

在啟動的初始階段,Tomcat 需要確定自身的根目錄(catalina.home)和工作目錄(catalina.base)。 這段代碼描述了尋找這兩個關鍵路徑的邏輯:teE28資訊網——每日最新資訊28at.com

  1. 探尋根目錄: 首先,代碼會嘗試從系統環境變量中獲取 catalina.home 的值。 若未找到,則會進一步探尋 bootstrap.jar 所在目錄的上一級目錄,并將該目錄作為 catalina.home。 最終,若仍無法確定 catalina.home,則將當前用戶目錄(user.dir)作為默認值。
  2. 定位工作目錄: 隨后,代碼會嘗試從系統環境變量中獲取 catalina.base 的值。 若未找到,則將 catalina.home 的值作為 catalina.base。

最后,代碼會將最終確定的 catalina.home 和 catalina.base 路徑信息設置為系統屬性,以便在后續的啟動流程中使用。teE28資訊網——每日最新資訊28at.com

這段代碼如同為 Tomcat 構建一座穩固的基石,它負責加載并設置 catalina.home 和 catalina.base 相關的信息,為后續的啟動流程奠定基礎。teE28資訊網——每日最新資訊28at.com

main()

Tomcat 的 main 方法可概括為兩個主要階段:**初始化 (**init) 和 加載與啟動 (load+start);。teE28資訊網——每日最新資訊28at.com

public static void main(String args[]) {    // 初始化階段  main方法第一次執行的時候,daemon肯定為null,所以直接new了一個Bootstrap對象,然后執行其init()方法    if (daemon == null) {        Bootstrap bootstrap = new Bootstrap();        try {            bootstrap.init();        } catch (Throwable t) {            handleThrowable(t);            t.printStackTrace();            return;        }        //daemon守護對象設置為bootstrap        daemon = bootstrap;    } else {        // 當作為服務運行時,對stop的調用將在新線程上進行,        // 因此確保使用正確的類加載器以防止一系列類未找到異常。        Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);    }    // 加載與啟動階段    // 執行守護對象的load方法和start方法    try {        String command = "start";        if (args.length > 0) {            command = args[args.length - 1];        }        if (command.equals("startd")) {            args[args.length - 1] = "start";            daemon.load(args);            daemon.start();        } else if (command.equals("stopd")) {            args[args.length - 1] = "stop";            daemon.stop();        } else if (command.equals("start")) {            daemon.setAwait(true);            daemon.load(args);            daemon.start();            if (null == daemon.getServer()) {                System.exit(1);            }        } else if (command.equals("stop")) {            daemon.stopServer(args);        } else if (command.equals("configtest")) {            daemon.load(args);            if (null == daemon.getServer()) {                System.exit(1);            }            System.exit(0);        } else {            log.warn("Bootstrap: command /"" + command + "/" does not exist.");        }    } catch (Throwable t) {        // 展開異常以獲得更清晰的錯誤報告        if (t instanceof InvocationTargetException &&                t.getCause() != null) {            t = t.getCause();        }        handleThrowable(t);        t.printStackTrace();        System.exit(1);    }}

我們點到init()里面去看看~teE28資訊網——每日最新資訊28at.com

public void init() throws Exception {    // 非常關鍵的地方,初始化類加載器s,后面我們會詳細具體地分析這個方法    initClassLoaders();    // 設置上下文類加載器為catalinaLoader,這個類加載器負責加載Tomcat專用的類    Thread.currentThread().setContextClassLoader(catalinaLoader);    // 暫時略過,后面會講    SecurityClassLoad.securityClassLoad(catalinaLoader);    // 使用catalinaLoader加載我們的Catalina類    // Load our startup class and call its process() method    if (log.isDebugEnabled())        log.debug("Loading startup class");    Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");    Object startupInstance = startupClass.getConstructor().newInstance();    // 設置Catalina類的parentClassLoader屬性為sharedLoader    // Set the shared extensions class loader    if (log.isDebugEnabled())        log.debug("Setting startup class properties");    String methodName = "setParentClassLoader";    Class<?> paramTypes[] = new Class[1];    paramTypes[0] = Class.forName("java.lang.ClassLoader");    Object paramValues[] = new Object[1];    paramValues[0] = sharedLoader;    Method method =        startupInstance.getClass().getMethod(methodName, paramTypes);    method.invoke(startupInstance, paramValues);    // catalina守護對象為剛才使用catalinaLoader加載類、并初始化出來的Catalina對象    catalinaDaemon = startupInstance;}

initClassLoaders 方法是 Tomcat 類加載機制的核心,它負責初始化 Tomcat 的各個類加載器,構建起 Tomcat 獨特的類加載體系。通過分析這個方法,我們可以清晰地驗證上一節提到的 Tomcat 類加載圖。teE28資訊網——每日最新資訊28at.com

private void initClassLoaders() {    try {        // 創建commonLoader,如果未創建成果的話,則使用應用程序類加載器作為commonLoader        commonLoader = createClassLoader("common", null);        if( commonLoader == null ) {            // no config file, default to this loader - we might be in a 'single' env.            commonLoader=this.getClass().getClassLoader();        }        // 創建catalinaLoader,父類加載器為commonLoader        catalinaLoader = createClassLoader("server", commonLoader);        // 創建sharedLoader,父類加載器為commonLoader        sharedLoader = createClassLoader("shared", commonLoader);    } catch (Throwable t) {        // 如果創建的過程中出現異常了,日志記錄完成之后直接系統退出        handleThrowable(t);        log.error("Class loader creation threw exception", t);        System.exit(1);    }}

createClassLoader 方法是 Tomcat 類加載器創建的底層方法,它負責根據配置文件 catalina.properties 中的配置信息來創建具體的類加載器實例。 CatalinaProperties.getProperty("xxx") 方法用于從 conf/catalina.properties 文件中獲取指定屬性的值,為 createClassLoader 方法提供必要的配置信息。teE28資訊網——每日最新資訊28at.com

private ClassLoader createClassLoader(String name, ClassLoader parent)    throws Exception {    // 獲取類加載器待加載的位置,如果為空,則不需要加載特定的位置,使用父類加載返回回去。    String value = CatalinaProperties.getProperty(name + ".loader");    if ((value == null) || (value.equals("")))        return parent;    // 替換屬性變量,比如:${catalina.base}、${catalina.home}    value = replace(value);    List<Repository> repositories = new ArrayList<>();   // 解析屬性路徑變量為倉庫路徑數組    String[] repositoryPaths = getPaths(value);    // 對每個倉庫路徑進行repositories設置。我們可以把repositories看成一個個待加載的位置對象,可以是一個classes目錄,一個jar文件目錄等等    for (String repository : repositoryPaths) {        // Check for a JAR URL repository        try {            @SuppressWarnings("unused")            URL url = new URL(repository);            repositories.add(                    new Repository(repository, RepositoryType.URL));            continue;        } catch (MalformedURLException e) {            // Ignore        }        // Local repository        if (repository.endsWith("*.jar")) {            repository = repository.substring                (0, repository.length() - "*.jar".length());            repositories.add(                    new Repository(repository, RepositoryType.GLOB));        } else if (repository.endsWith(".jar")) {            repositories.add(                    new Repository(repository, RepositoryType.JAR));        } else {            repositories.add(                    new Repository(repository, RepositoryType.DIR));        }    }    // 使用類加載器工廠創建一個類加載器    return ClassLoaderFactory.createClassLoader(repositories, parent);}

我們來分析一下ClassLoaderFactory.createClassLoader--類加載器工廠創建類加載器。teE28資訊網——每日最新資訊28at.com

public static ClassLoader createClassLoader(List<Repository> repositories,                                            final ClassLoader parent)    throws Exception {    if (log.isDebugEnabled())        log.debug("Creating new class loader");    // Construct the "class path" for this class loader    Set<URL> set = new LinkedHashSet<>();    // 遍歷repositories,對每個repository進行類型判斷,并生成URL,每個URL我們都要校驗其有效性,有效的URL我們會放到URL集合中    if (repositories != null) {        for (Repository repository : repositories)  {            if (repository.getType() == RepositoryType.URL) {                URL url = buildClassLoaderUrl(repository.getLocation());                if (log.isDebugEnabled())                    log.debug("  Including URL " + url);                set.add(url);            } else if (repository.getType() == RepositoryType.DIR) {                File directory = new File(repository.getLocation());                directory = directory.getCanonicalFile();                if (!validateFile(directory, RepositoryType.DIR)) {                    continue;                }                URL url = buildClassLoaderUrl(directory);                if (log.isDebugEnabled())                    log.debug("  Including directory " + url);                set.add(url);            } else if (repository.getType() == RepositoryType.JAR) {                File file=new File(repository.getLocation());                file = file.getCanonicalFile();                if (!validateFile(file, RepositoryType.JAR)) {                    continue;                }                URL url = buildClassLoaderUrl(file);                if (log.isDebugEnabled())                    log.debug("  Including jar file " + url);                set.add(url);            } else if (repository.getType() == RepositoryType.GLOB) {                File directory=new File(repository.getLocation());                directory = directory.getCanonicalFile();                if (!validateFile(directory, RepositoryType.GLOB)) {                    continue;                }                if (log.isDebugEnabled())                    log.debug("  Including directory glob "                        + directory.getAbsolutePath());                String filenames[] = directory.list();                if (filenames == null) {                    continue;                }                for (int j = 0; j < filenames.length; j++) {                    String filename = filenames[j].toLowerCase(Locale.ENGLISH);                    if (!filename.endsWith(".jar"))                        continue;                    File file = new File(directory, filenames[j]);                    file = file.getCanonicalFile();                    if (!validateFile(file, RepositoryType.JAR)) {                        continue;                    }                    if (log.isDebugEnabled())                        log.debug("    Including glob jar file "                            + file.getAbsolutePath());                    URL url = buildClassLoaderUrl(file);                    set.add(url);                }            }        }    }    // Construct the class loader itself    final URL[] array = set.toArray(new URL[set.size()]);    if (log.isDebugEnabled())        for (int i = 0; i < array.length; i++) {            log.debug("  location " + i + " is " + array[i]);        }    // 從這兒看,最終所有的類加載器都是URLClassLoader的對象~~    return AccessController.doPrivileged(            new PrivilegedAction<URLClassLoader>() {                @Override                public URLClassLoader run() {                    if (parent == null)                        return new URLClassLoader(array);                    else                        return new URLClassLoader(array, parent);                }            });}

對 initClassLoaders 分析完,Tomcat 還會進行一項至關重要的安全措施,即 SecurityClassLoad.securityClassLoad 方法。 讓我們深入探究這個方法,看看它在 Tomcat 的安全體系中扮演著怎樣的角色。teE28資訊網——每日最新資訊28at.com

public static void securityClassLoad(ClassLoader loader) throws Exception {    securityClassLoad(loader, true);}static void securityClassLoad(ClassLoader loader, boolean requireSecurityManager) throws Exception {    if (requireSecurityManager && System.getSecurityManager() == null) {        return;    }    loadCorePackage(loader);    loadCoyotePackage(loader);    loadLoaderPackage(loader);    loadRealmPackage(loader);    loadServletsPackage(loader);    loadSessionPackage(loader);    loadUtilPackage(loader);    loadValvesPackage(loader);    loadJavaxPackage(loader);    loadConnectorPackage(loader);    loadTomcatPackage(loader);} private static final void loadCorePackage(ClassLoader loader) throws Exception {    final String basePackage = "org.apache.catalina.core.";    loader.loadClass(basePackage + "AccessLogAdapter");    loader.loadClass(basePackage + "ApplicationContextFacade$PrivilegedExecuteMethod");    loader.loadClass(basePackage + "ApplicationDispatcher$PrivilegedForward");    loader.loadClass(basePackage + "ApplicationDispatcher$PrivilegedInclude");    loader.loadClass(basePackage + "ApplicationPushBuilder");    loader.loadClass(basePackage + "AsyncContextImpl");    loader.loadClass(basePackage + "AsyncContextImpl$AsyncRunnable");    loader.loadClass(basePackage + "AsyncContextImpl$DebugException");    loader.loadClass(basePackage + "AsyncListenerWrapper");    loader.loadClass(basePackage + "ContainerBase$PrivilegedAddChild");    loadAnonymousInnerClasses(loader, basePackage + "DefaultInstanceManager");    loader.loadClass(basePackage + "DefaultInstanceManager$AnnotationCacheEntry");    loader.loadClass(basePackage + "DefaultInstanceManager$AnnotationCacheEntryType");    loader.loadClass(basePackage + "ApplicationHttpRequest$AttributeNamesEnumerator");}

這段代碼使用 catalinaLoader 加載了 Tomcat 源代碼中各個專用類,這些類主要分布在以下幾個包中:teE28資訊網——每日最新資訊28at.com

  1. org.apache.catalina.core.*
  2. org.apache.coyote.*
  3. org.apache.catalina.loader.*
  4. org.apache.catalina.realm.*
  5. org.apache.catalina.servlets.*
  6. org.apache.catalina.session.*
  7. org.apache.catalina.util.*
  8. org.apache.catalina.valves.*
  9. javax.servlet.http.Cookie
  10. org.apache.catalina.connector.*
  11. org.apache.tomcat.*

至此,我們已經逐一分析了 init 方法中的關鍵方法,包括 initClassLoaders、SecurityClassLoad.securityClassLoad 等,了解了 Tomcat 初始化階段的各個步驟。teE28資訊網——每日最新資訊28at.com

WebApp 類加載器

在深入探究 Tomcat 啟動流程的過程中,我們發現似乎遺漏了一個關鍵角色——WebApp 類加載器。teE28資訊網——每日最新資訊28at.com

WebApp 類加載器是每個 Web 應用獨有的,而每個 Web 應用本質上就是一個 Context。 因此,我們理所當然地將目光投向了 Context 的實現類。 Tomcat 中,StandardContext 是 Context 的默認實現,而 WebApp 類加載器正是誕生于 StandardContext 類的 startInternal() 方法中。teE28資訊網——每日最新資訊28at.com

protected synchronized void startInternal() throws LifecycleException {    if (getLoader() == null) {        WebappLoader webappLoader = new WebappLoader(getParentClassLoader());        webappLoader.setDelegate(getDelegate());        setLoader(webappLoader);    }}

這段代碼的邏輯非常簡潔,它在 WebApp 類加載器不存在的情況下,會創建一個新的 WebApp 類加載器,并將其設置為當前 Context 的加載器(setLoader)。teE28資訊網——每日最新資訊28at.com

到這里,我們已經完成了對 Tomcat 啟動過程和類加載機制的全面解析,從 Bootstrap 類開始,一步步深入,最終揭開了 WebApp 類加載器的面紗。teE28資訊網——每日最新資訊28at.com

通過這個分析過程,我們不僅了解了 Tomcat 啟動和類加載的具體步驟,更深刻地理解了 Tomcat 采用這種獨特的多層級類加載機制的深層原因,以及這種設計帶來的種種優勢。teE28資訊網——每日最新資訊28at.com

本文鏈接:http://www.tebozhan.com/showinfo-26-112754-0.html類加載機制的源碼解讀

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 使用 SpringBoot3.3 + SpEL 讓復雜權限控制變得很簡單!

下一篇: 曾經非常厭惡 SSR,現在終于不再需要它了 | 如何在考慮 SEO 的情況下構建 CSR 應用

標簽:
  • 熱門焦點
  • JavaScript 混淆及反混淆代碼工具

    介紹在我們開始學習反混淆之前,我們首先要了解一下代碼混淆。如果不了解代碼是如何混淆的,我們可能無法成功對代碼進行反混淆,尤其是使用自定義混淆器對其進行混淆時。什么是混
  • 分布式系統中的CAP理論,面試必問,你理解了嘛?

    對于剛剛接觸分布式系統的小伙伴們來說,一提起分布式系統,就感覺高大上,深不可測。而且看了很多書和視頻還是一臉懵逼。這篇文章主要使用大白話的方式,帶你理解一下分布式系統
  • 讓我們一起聊聊文件的操作

    文件【1】文件是什么?文件是保存數據的地方,是數據源的一種,比如大家經常使用的word文檔、txt文件、excel文件、jpg文件...都是文件。文件最主要的作用就是保存數據,它既可以保
  • 使用LLM插件從命令行訪問Llama 2

    最近的一個大新聞是Meta AI推出了新的開源授權的大型語言模型Llama 2。這是一項非常重要的進展:Llama 2可免費用于研究和商業用途。(幾小時前,swyy發現它已從LLaMA 2更名為Lla
  • 當家的盒馬,加速謀生

    來源 | 價值星球Planet作者 | 歸去來自己&ldquo;當家&rdquo;的盒馬,開始加速謀生了。據盒馬官微消息,盒馬計劃今年開放生鮮供應鏈,將其生鮮商品送往食堂。目前,盒馬在上海已經與
  • 小米公益基金會捐贈2500萬元馳援北京、河北暴雨救災

    8月2日消息,今日小米科技創始人雷軍在其微博上發布消息稱,小米公益基金會宣布捐贈2500萬元馳援北京、河北暴雨救災。攜手抗災,京冀安康!以下為公告原文
  • 三星Galaxy Z Fold5官方渲染圖曝光:13.4mm折疊厚度依舊感人

    據官方此前宣布,三星將于7月26日在韓國首爾舉辦Unpacked活動,屆時將帶來帶來包括Galaxy Buds 3、Galaxy Watch 6、Galaxy Tab S9、Galaxy Z Flip 5、
  • 2299元起!iQOO Pad明晚首銷:性能最強天璣平板

    5月23日,iQOO如期舉行了新品發布會,除了首發安卓最強旗艦處理器的iQOO Neo8系列新機外,還在發布會上推出了旗下首款平板電腦——iQOO Pad,其最大的賣點
  • 中關村論壇11月25日開幕,15位諾獎級大咖將發表演講

    11月18日,記者從2022中關村論壇新聞發布會上獲悉,中關村論壇將于11月25至30日在京舉行。本屆中關村論壇由科學技術部、國家發展改革委、工業和信息化部、國務
Top