什么是類加載器?
- 這是官方給的定義
在 Java 虛擬機的實現中,初始類可以作為命令行參數提供。 或者,該實現可以提供一個初始類,該類設置一個類加載器,該類加載器依次加載應用程序。 初始類的其他選擇也是可能的,只要它們與上一段中給出的規范一致。
所謂類加載器,就是用於加載Java類到Java虛擬機中的組件,它負責讀取Java字節碼,並轉換成java.lang.Class類的一個實例,使字節碼.class 文件得以運行。一般類加載器負責根據一個指定的類找到對應的字節碼,然后根據這些字節碼定義一個Java類。
類加載器在實際使用中給我們帶來的好處是,它可以使Java類動態地加載到JVM中並運行,即可在程序運行時再加載類,提供了很靈活的動態加載方式。
站在程序員的角度來看,Java 類加載器可以分為三種。
- 啟動類加載器(Bootstrap ClassLoader):加載對象是Java核心庫,把一些核心的Java類加載進JVM中,這個加載器使用原生代碼(C/C++)實現,並不是繼承java.lang.ClassLoader,它是所有其他類加載器的最終父加載器,負責加載<JAVA_HOME>/jre/lib目錄下JVM指定的類庫。其實它屬於JVM整體的一部分,JVM一啟動就將這些指定的類加載到內存中,避免以后過多的I/O操作,提高系統的運行效率。啟動類加載器無法被Java程序直接使用。
- 擴展類加載器(Extension ClassLoader):加載的對象為Java的擴展庫,即加載<JAVA_HOME>/jre/lib/ext目錄里面的類。這個類由啟動類加載器加載,但因為啟動類加載器並非用Java 實現,已經脫離了Java體系,所以如果嘗試調用擴展類加載器的getParent()方法獲取父加載器會得到null。然而,它的父類加載器是啟動類加載器。
- 應用程序類加載器((Application ClassLoader):亦叫系統類加載器(System ClassLoader),它負責加載用戶類路徑(CLASSPATH)指定的類庫,如果程序沒有自己定義類加載器,就默認使用應用程序類加載器。它也由啟動類加載器加載,但它的父加載類被設置成了擴展類加載器。如果要使用這個加載器,可通過ClassLoader.getSystemClassLoader()獲取。
雙親委派
雙親委派模型會在類加載器加載類時首先委托給父類加載器加載,除非父類加載器不能加載才自己加載。
使用雙親委派模型,Java類隨着它的加載器一起具備了一種帶有優先級的層次關系,通過這種層次模型,可以避免類的重復加載,也可以避免核心類被不同的類加載器加載到內存中造成沖突和混亂,從而保證了Java核心庫的安全。
- 示范
最頂級的類加載器Bootstrap是由c++語言寫的,所以打印出來是Null
"C:\Program Files\Java\jdk1.8.0_181\bin\java.exe" "-javaagent:D:\IntelliJ IDEA 2020.1.1\lib\idea_rt.jar=2921:D:\IntelliJ IDEA 2020.1.1\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_181\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar;
后面的這些就是應用類加載器
D:\original\apache-tomcat-8.5.70-src\target\classes;D:\software\maven_repository\org\easymock\easymock\3.4\easymock-3.4.jar;D:\software\maven_repository\org\objenesis\objenesis\2.2\objenesis-2.2.jar;D:\software\maven_repository\org\apache\ant\ant\1.7.0\ant-1.7.0.jar;D:\software\maven_repository\org\apache\ant\ant-launcher\1.7.0\ant-launcher-1.7.0.jar;D:\software\maven_repository\wsdl4j\wsdl4j\1.6.2\wsdl4j-1.6.2.jar;D:\software\maven_repository\javax\xml\jaxrpc-api\1.1\jaxrpc-api-1.1.jar;D:\software\maven_repository\org\eclipse\jdt\core\compiler\ecj\4.5.1\ecj-4.5.1.jar" org.apache.catalina.startup.Test
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@29453f44
null
Process finished with exit code 0
為什么TOMCAT要破壞雙親委派?
我們知道,Tomcat是web容器,那么一個web容器可能需要部署多個應用程序。
不同的應用程序可能會依賴同一個第三方類庫的不同版本,但是不同版本的類庫中某一個類的全路徑名可能是一樣的。
如多個應用都要依賴hollis.jar,但是A應用需要依賴1.0.0版本,但是B應用需要依賴1.0.1版本。這兩個版本中都有一個類是com.hollis.Test.class。
如果采用默認的雙親委派類加載機制,那么是無法加載多個相同的類。
所以,Tomcat破壞雙親委派原則,提供隔離的機制,為每個web容器單獨提供一個WebAppClassLoader加載器。
Tomcat是怎么破壞雙親委派的呢?
- 通過實現自定義加載器
Tomcat 本身有commonclassloader來管理,不去跟應用程序所沖突
每一個應用都會生成一個類加載器的實例 webclassloader
管理一個目錄 這個目錄 就是web-inf
這個目錄由findclassloader來指定
webclassloader的上一級類加載器就是commonclassloader
整個層級關系就是這樣的
雙親委派變五親委派(不是
webclassloader-->commonclassloader-->appclassloader-->extclassloader-->bootstrapclassloader
Tomcat的類加載機制:為了實現隔離性,優先加載 Web 應用自己定義的類,所以沒有遵照雙親委派的約定,每一個應用自己的類加載器——WebAppClassLoader負責加載本身的目錄下的class文件,加載不到時再交給CommonClassLoader加載,這和雙親委派剛好相反。
怎么實現一個自定義加載器
- 繼承Classloder接口
- 重寫構造方法