JDBC、Tomcat为什么要破坏双亲委派模型?
問題一:雙親委派模型是什么
如果一個類加載器收到了加載某個類的請求,則該類加載器并不會去加載該類,而是把這個請求委派給父類加載器,每一個層次的類加載器都是如此,因此所有的類加載請求最終都會傳送到頂端的啟動類加載器;只有當(dāng)父類加載器在其搜索范圍內(nèi)無法找到所需的類,并將該結(jié)果反饋給子類加載器,子類加載器會嘗試去自己加載。
雙親委派模型的好處
這樣做的好處就是:Java類隨著它的類加載器一起具備了一種帶有優(yōu)先級的層次關(guān)系。例如類java.lang.Object,它存放在rt.jar中,無論哪一個類加載器要加載這個類,最終都是委派給處于模型最頂端的啟動類加載器進(jìn)行加載,因此Object類在程序的各種類加載器環(huán)境中都是同一個類。相反,如果沒有使用雙親委派模型,由各個類加載器自行去加載的話,如果用戶自己編寫了一個稱為java.lang.object的類,并放在程序的ClassPath中,那系統(tǒng)中將會出現(xiàn)多個不同的Object類,Java類型體系中最基礎(chǔ)的行為也就無法保證,應(yīng)用程序也將會變得一片混亂。
其次是考慮到安全因素。假設(shè)通過網(wǎng)絡(luò)傳遞一個名為java.lang.Integer的類,通過雙親委托模式傳遞到啟動類加載器,而啟動類加載器在核心Java API發(fā)現(xiàn)這個名字的類,發(fā)現(xiàn)該類已被加載,并不會重新加載網(wǎng)絡(luò)傳遞的過來的java.lang.Integer,而直接返回已加載過的Integer.class,這樣便可以防止核心API庫被隨意篡改。
問題二:JDBC為什么要破壞雙親委派模型
問題背景
在JDBC 4.0之后實際上我們不需要再調(diào)用Class.forName來加載驅(qū)動程序了,我們只需要把驅(qū)動的jar包放到工程的類加載路徑里,那么驅(qū)動就會被自動加載。
這個自動加載采用的技術(shù)叫做SPI,數(shù)據(jù)庫驅(qū)動廠商也都做了更新。可以看一下jar包里面的META-INF/services目錄,里面有一個java.sql.Driver的文件,文件里面包含了驅(qū)動的全路徑名。
使用上,我們只需要通過下面一句就可以創(chuàng)建數(shù)據(jù)庫的連接:
Connection con = DriverManager.getConnection(url , username , password ) ;問題解答
因為類加載器受到加載范圍的限制,在某些情況下父類加載器無法加載到需要的文件,這時候就需要委托子類加載器去加載class文件。
JDBC的Driver接口定義在JDK中,其實現(xiàn)由各個數(shù)據(jù)庫的服務(wù)商來提供,比如MySQL驅(qū)動包。DriverManager 類中要加載各個實現(xiàn)了Driver接口的類,然后進(jìn)行管理,但是DriverManager位于 $JAVA_HOME中jre/lib/rt.jar 包,由BootStrap類加載器加載,而其Driver接口的實現(xiàn)類是位于服務(wù)商提供的 Jar 包,根據(jù)類加載機制,當(dāng)被裝載的類引用了另外一個類的時候,虛擬機就會使用裝載第一個類的類裝載器裝載被引用的類。也就是說BootStrap類加載器還要去加載jar包中的Driver接口的實現(xiàn)類。我們知道,BootStrap類加載器默認(rèn)只負(fù)責(zé)加載 $JAVA_HOME中jre/lib/rt.jar 里所有的class,所以需要由子類加載器去加載Driver實現(xiàn),這就破壞了雙親委派模型。
查看DriverManager類的源碼,看到在使用DriverManager的時候會觸發(fā)其靜態(tài)代碼塊,調(diào)用 loadInitialDrivers() 方法,并調(diào)用ServiceLoader.load(Driver.class) 加載所有在META-INF/services/java.sql.Driver 文件里邊的類到JVM內(nèi)存,完成驅(qū)動的自動加載。
static {loadInitialDrivers();println("JDBC DriverManager initialized");}private static void loadInitialDrivers() {AccessController.doPrivileged(new PrivilegedAction<Void>() {public Void run() {ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);Iterator<Driver> driversIterator = loadedDrivers.iterator();try{while(driversIterator.hasNext()) {driversIterator.next();}} catch(Throwable t) {// Do nothing}return null;}});} public static <S> ServiceLoader<S> load(Class<S> service) {ClassLoader cl = Thread.currentThread().getContextClassLoader();return ServiceLoader.load(service, cl);}這個子類加載器是通過 Thread.currentThread().getContextClassLoader() 得到的線程上下文加載器。
那么這個上下文類加載器又是什么呢?
public Launcher() {...try {this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);} catch (IOException var9) {throw new InternalError("Could not create application class loader", var9);}Thread.currentThread().setContextClassLoader(this.loader);... }可以看到,在 sun.misc.Launcher 初始化的時候,會獲取AppClassLoader,然后將其設(shè)置為上下文類加載器,所以線程上下文類加載器默認(rèn)情況下就是系統(tǒng)加載器。
問題三:Tomcat為什么要破壞雙親委派模型
Tomcat類加載器:
Tomcat如何破壞雙親委派模型的呢?
每個Tomcat的webappClassLoader加載自己的目錄下的class文件,不會傳遞給父類加載器。
事實上,tomcat之所以造了一堆自己的classloader,大致是出于下面三類目的:
- 對于各個 webapp中的 class和 lib,需要相互隔離,不能出現(xiàn)一個應(yīng)用中加載的類庫會影響另一個應(yīng)用的情況,而對于許多應(yīng)用,需要有共享的lib以便不浪費資源。
- 與 jvm一樣的安全性問題。使用單獨的 classloader去裝載 tomcat自身的類庫,以免其他惡意或無意的破壞;
- 熱部署。相信大家一定為 tomcat修改文件不用重啟就自動重新裝載類庫而驚嘆吧。
延伸閱讀
https://blog.csdn.net/u012129558/article/details/81540804
https://blog.csdn.net/liweisnake/article/details/8470285
轉(zhuǎn)載于:https://www.cnblogs.com/yueshutong/p/11430885.html
總結(jié)
以上是生活随笔為你收集整理的JDBC、Tomcat为什么要破坏双亲委派模型?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Redis与Zookeeper实现分布式
- 下一篇: 机器学习中为什么使用one-hot编码