本章內容是對《深刻理解Java虛擬機:JVM高級特性和最佳實踐》的理解和歸納。java
在上文中咱們已經講了類的加載機制,這一章的主角就是類加載器和雙親委派模型了。mysql
在Java虛擬機中,類加載器十分重要。每個類的加載,都須要經過一個類的加載器。可是若是咱們建立一個屬於本身的類加載器,這個時候會出現一個什麼樣的狀況呢? 接下來,咱們用代碼來進行驗證測試。sql
public class ClassLoaderTest {
public static void main(String[] args) throws Exception {
ClassLoader myLoader = new ClassLoader() {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
try {
String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
InputStream is = getClass().getResourceAsStream(fileName);
if (is == null) {
return super.loadClass(name);
}
byte[] b = new byte[is.available()];
is.read(b);
return defineClass(name, b, 0, b.length);
} catch (IOException e) {
throw new ClassNotFoundException(name);
}
}
};
// 由本身的類加載器建立對象
Object obj = myLoader.loadClass("XuNiJi.ClassLoaderTest").newInstance();
// 由系統提供的類加載器加載建立對象
Object obj1 = ClassLoader.getSystemClassLoader().loadClass("XuNiJi.ClassLoaderTest").newInstance();
System.out.println(obj instanceof ClassLoaderTest);
System.out.println(obj1 instanceof ClassLoaderTest);
}
}
複製代碼
從這裏想來已經可以看出了,由一個類加載器統一建立的類,才存在可比性。由於類加載器是擁有獨立的類名稱空間的。更簡單的說,就像上面的例子,若是不使用Java虛擬機提供的類加載器,你就會失去一大部分功能,好比
equals()
、
isAssignableFrom()
、
isInstance()
、
instanceof
。若是要相同,除非你直接在java源碼上動手腳。
第一個問題:爲何須要這個模型? 其實這個模型的提出,就是爲了解決類加載器可能不出現不一樣的問題。由於即使是相同的class
,由不一樣的類加載器加載時,結果就是不一樣的。api
雙親委派的工做流程很是簡單,這就跟以前文章裏的Android的事件分發機制同樣,向上傳遞,由上一層的加載器先行嘗試消費,若是上一層沒法完成這個任務,那麼子加載器就要由本身動手完成。ide
- 啓動類加載器:負責加載/lib下的類。
- 擴展類加載器:負責加載/lib/ext下的類。
- 系統類加載器/應用程序類加載器:
ClassLoader.getSystemClassLoader
返回的就是它。
經過上圖咱們能夠知道,子加載器不斷的給上一層加載器傳遞加載請求,那麼這個時候啓動類加載器勢必是接受到過所有的加載請求的。若是不信,咱們就用源碼來證實。函數
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 判斷Class是否被加載過
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// 若是父類拋出ClassNotFoundException
// 說明父類沒法完成加載
}
// 這個時候c依舊爲null,說明父類加載不了
// 那沒有辦法,只能子加載器本身效勞了
if (c == null) {
long t1 = System.nanoTime();
c = findClass(name);
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
複製代碼
講完了他的工做原理,天然就要知道,他可以如何被破壞的了。post
第二個問題,爲何要破壞雙親委派? 拿最簡單的例子,在上文中咱們,提到過各個資源的加載範圍,可是Driver
做爲後來才加入的一個接口,他的不少api是由第三方服務商開發的。那麼這個時候,破壞雙親委派就有了他的用武之地了,固然這只是他的用處之一。學習
下面來介紹,他是如何破壞雙親委派的。 先看看咱們平時都是怎麼用的。(固然這是很基礎的寫法了,由於如今池的概念加深,因此不少事情都已經被封裝了。)測試
String url = "jdbc:mysql://localhost:3306/db";
Connection conn = DriverManager.getConnection(url, "root", "root");
複製代碼
上面很明顯就能看出這件事情就是關於DriverManager
展開的了。ui
static {
loadInitialDrivers();
println("JDBC DriverManager initialized");
}
複製代碼
這裏根據前一章的內容先要對DriverManager
進行初始化,也就是調用了一個loadInitialDrivers()
函數。
private static void loadInitialDrivers() {
.....
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);// 1
Iterator<Driver> driversIterator = loadedDrivers.iterator();
try{
while(driversIterator.hasNext()) {
driversIterator.next();
}
} catch(Throwable t) {
// Do nothing
}
return null;
}
});
.....
}
複製代碼
從這一小段中,咱們關注註釋1
可以知道他專門去訪問了一個ServiceLoader
的類,點進去以後咱們可以發現這麼三段代碼。
// 1
public static <S> ServiceLoader<S> load(Class<S> service) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service, cl);
}
// 2
public static <S> ServiceLoader<S> load(Class<S> service, ClassLoader loader){
return new ServiceLoader<>(service, loader);
}
// 3
private ServiceLoader(Class<S> svc, ClassLoader cl) {
service = Objects.requireNonNull(svc, "Service interface cannot be null");
loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
reload();
}
複製代碼
由1 --> 2 --> 3的順序按部就班,你是否已經和我關注到一個問題了!! ClassLoader.getSystemClassLoader()
,看到這個函數了嗎,我在上文提到過,這個函數咱們得到的類加載器將會是應用程序類加載器。也就是說咱們的任務不會再向上傳遞了,到頭就是到了應用程序類加載器這個位置,那麼雙親委派模型也就破壞了。
以上就是打破雙親委派的方法之一的介紹了。
爲何說咱們調用的是應用程序類加載器呢? 接下來直接從源碼來解析了。 首先就是調用getSystemClassLoader()
這個函數了
這張圖裏咱們只用關注圈紅的函數。
而後在initSystemClassLoader()
函數中調用了一個Launcher
的類。
而Launcher
整個類的建立,想來讀者也已經看到loader
這個變量了,經過getAppClassLoader()
這個函數所建立的loader
也就是咱們口中所說的應用程序類加載器了。
以上就是個人學習成果,若是有什麼我沒有思考到的地方或是文章內存在錯誤,歡迎與我分享。
相關文章推薦: