深入類載入器----執行緒上下文類載入器
深入類載入器(四)----執行緒上下文切換類載入器
執行緒上下文類載入器
我們知道在java中的載入器的載入模式是雙親委託模式,這種模式是遵從父類優先的原則。
但是在一些場合中,這種雙親委託機制反而是行不通的。
最典型的是JDBCApi:其實JDBCApi包括倆部分:一部分是oragle公司提供的介面類,一部分是具體的廠商提供的實現類,也就是所謂的driver驅動部分。比如是要使用mysql資料庫的話,那麼就需要載入mysql資料庫的驅動。如果還是按照,雙親委託機制的話,那麼對於oragle公司提供的介面部分就是由拓展類載入器來進行進行載入的。注意,介面也是一種型別,也需要被類載入器進行載入才可以。
我們要明白一點,一般來說,介面和具體的實現應該由同一個的類載入器進行載入。如果是由不同的載入器進行載入的話,那麼就會造成介面找不到具體的實現。這是不行的。
但是至於廠商提供的具體的實現的部分,也就是第三方的jar包,一般是由應用程式類載入器進行載入的。這樣的話,就會使介面和實現的類載入器是不一樣的。這樣是不行的。為了解決這個問題,就拋棄了雙親委託機制,而提出了執行緒上下文類載入器。在api+spi的模組框架下,很多的底層的實現都是使用了執行緒上下文類載入器。這是要注意的。線上程上下文類載入器中,
我們可以自定義類載入器,並且指定這種類載入器不適用雙親委託機制,這樣的話,就可以使介面和具體的實現的載入器是一樣的。。
從而就解決了這個問題。
下面看一個例子:
package com.lg.test;
/*這就是執行緒上下文切換類載入器這主要是為了防止Api+spi的錯誤而特定使用的*/
/*比如說,JDBC中,介面API是由oragle公司提供的,這些介面API一般是由Boot類載入器進行載入的*/
/*但是具體的實現是由App進行載入的。這樣的話,就會使介面找不到實現。就會出現錯誤。所以我們要
* 使用執行緒上下文切換來進行,使載入的類載入器都是相同的。這是要注意的*/
public class ThreadClassLoader {
public static void main(String[] args) {
ClassLoader loader=ThreadClassLoader.class.getClassLoader();
System.out.println(loader);
ClassLoader loader1 = Thread.currentThread().getContextClassLoader();
System.out.println(loader1);
Thread.currentThread().setContextClassLoader(new FileClassLoader("c://myjava"));
System.out.println(Thread.currentThread().getContextClassLoader());
try {
Class<?> c= Thread.currentThread().getContextClassLoader().loadClass("com.lg.test.Demo02");
System.out.println(c);
System.out.println(c.getClassLoader());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
最後的輸出的結果是:
sun.misc.Launcher$AppClassLoader@4e0e2f2a
sun.misc.Launcher$AppClassLoader@4e0e2f2a
com.lg.test.FileClassLoader@15db9742
class com.lg.test.Demo02
sun.misc.Launcher$AppClassLoader@4e0e2f2a
為什麼最後輸出的還是應用程式的類載入器呢?因為這個自定義的類載入器內部還是使用了雙親委託機制,
但是我們也可以自己通過程式碼來控制它不是雙親委託機制的。這部分可以根據具體的需要來進行實現。