什麼是打破雙親委派機制呢?
那麼這裡第一步, 我們需要知道什麼是雙親委派機制?
前面已經說了什麼是雙親委派機制了, 那打破是怎麼回事呢?
比如, 我現在有一個自定義類加載器, 加載的是~/com/lxl/jvm/User1.class類, 而在應用程式的target目錄下也有一個com/lxl/jvm/User1.class, 那麼, 最終User1.class這個類将被哪個類加載器加載呢? 根據雙親委派機制, 我們知道, 他一定是被應用程式類加載器AppClassLoader加載, 而不是我們自定義的類加載器, 為什麼呢? 因為他要向上尋找, 向下委托. 當找到了以後, 便不再向後執行了.
我們要打破雙親委派機制, 就是要讓自定義類加載器來加載我們的User1.class, 而不是應用程式類加載器來加載
接下來分析, 如何打破雙親委派機制呢? 雙親委派機制是在哪裡實作的? 是在ClassLoader類的loadClass(...)方法實作的. 如果我們不想使用系統自帶的雙親委派模式, 隻需要重新實作ClassLoader的loadClass(...)方法即可. 下面是ClassLoader中定義的loadClass()方法. 裡面實作了雙親委派機制
下面給DefinedClassLoaderTest.java增加一個loadClass方法, 拷貝上面的代碼即可. 删除掉中間實作雙親委派機制的部分這裡需要注意的是, com.lxl.jvm是自定義的類包, 隻有我們自己定義的類才從這裡加載. 如果是系統類, 依然使用雙親委派機制來加載.
來看看運作結果:
調用了user1的sout方法
com.lxl.jvm.DefinedClassLoaderTest
package com.lxl.jvm;
import java.io.FileInputStream;
import java.lang.reflect.Method;
/**
* 自定義的類加載器
*/
public class DefinedClassLoaderTest extends ClassLoader{
private String classPath;
public DefinedClassLoaderTest(String classPath) {
this.classPath = classPath;
}
/**
* 重寫findClass方法
*
* 如果不會寫, 可以參考URLClassLoader中是如何加載AppClassLoader和ExtClassLoader的
* @param name
* @return
* @throws ClassNotFoundException
*/
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] data = loadBytes(name);
return defineClass(name, data, 0, data.length);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private byte[] loadBytes(String name) throws Exception {
// 我們需要讀取類的路徑
String path = name.replace('.', '/').concat(".class");
//String path = "";
// 去路徑下查找這個類
FileInputStream fileInputStream = new FileInputStream(classPath + "/" + path);
int len = fileInputStream.available();
byte[] data = new byte[len];
fileInputStream.read(data);
fileInputStream.close();
return data;
}
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
/**
* 直接執行findClass()...什麼意思呢? 首先會使用自定義類加載器加載類, 不在向上委托, 直接由
* 自己執行
*
* jvm自帶的類還是需要由引導類加載器自動加載
*/
if (!name.startsWith("com.lxl.jvm")) {
c = this.getParent().loadClass(name);
} else {
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
public static void main(String[] args) throws Exception {
DefinedClassLoaderTest classLoader = new DefinedClassLoaderTest("/Users/luoxiaoli");
Class<?> clazz = classLoader.loadClass("com.lxl.jvm.User1");
Object obj = clazz.newInstance();
Method sout = clazz.getDeclaredMethod("sout", null);
sout.invoke(obj, null);
System.out.println(clazz.getClassLoader().getClass().getName());
}
}