什么是打破双亲委派机制呢?
那么这里第一步, 我们需要知道什么是双亲委派机制?
前面已经说了什么是双亲委派机制了, 那打破是怎么回事呢?
比如, 我现在有一个自定义类加载器, 加载的是~/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()); } }