什么是打破雙親委派機制呢?
那么這里第一步, 我們需要知道什么是雙親委派機制?
前面已經說了什么是雙親委派機制了, 那打破是怎么回事呢?
比如, 我現在有一個自定義類加載器, 加載的是~/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
現在User1方法確實是由自定義類加載器加載的了
源碼:
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()); } }