上节自定义的类加载器,实现了加载指定目录的class,但是还是遵循了双亲委派机制
如何打破双亲委派机制 很简单,我们已经知道了双亲委派机制就是在ClassLoader的loadClass方法实现的,只要我们重写该方法就可以了呗
package study.wyy.jvm.classLoader; import java.io.FileInputStream; public class MyClassLoader extends ClassLoader { private String classPath; public MyClassLoader(String classPath) { this.classPath = classPath; } private byte[] loadByte(String name) throws Exception { name = name.replaceAll("\\.", "/"); FileInputStream fis = new FileInputStream(classPath + "/" + name + ".class"); int len = fis.available(); byte[] data = new byte[len]; fis.read(data); fis.close(); return data; } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { try { byte[] bytes = loadByte(name); return defineClass(name, bytes, 0, bytes.length); } catch (Exception e) { e.printStackTrace(); throw new ClassNotFoundException(); } } /** * @Description 重写类加载方法,实现自己的加载逻辑,不委派给双亲加载 * @Author wyaoyao * @Date 2020/9/3 9:33 下午 * @Param * @Return * @Exception */ @Override public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded Class<?> c = findLoadedClass(name); long t0 = System.nanoTime(); // 注释的代码就是现实双亲委派机制,这里直接注释掉不就可以了吗 // try { // if (parent != null) { // c = parent.loadClass(name, false); // } else { // c = findBootstrapClassOrNull(name); // } // } catch (ClassNotFoundException e) { // // ClassNotFoundException thrown if class not found // // from the non-null parent class loader // } if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } if (resolve) { resolveClass(c); } return c; } } }测试
package study.wyy.jvm.classLoader; public class MyClassLoaderTest { public static void main(String[] args) throws Exception { //初始化自定义类加载器,会先初始化父类ClassLoader,其中会把自定义类加载器的父加载器设置为应用程序类加载器AppClassLoader MyClassLoader classLoader = new MyClassLoader("/Users/wyaoyao/Documents/test"); //test目录创建 study.wyy.jvm.model 几级目录,将TargetClass类的class文件TargetClass.class丢入该目录 Class clazz = classLoader.loadClass("study.wyy.jvm.model.TargetClass"); System.out.println(clazz.getClassLoader().getClass().getName()); } }输出
/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/bin/java -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=57488:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8 -classpath /Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/ext/cldrdata.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/ext/dnsns.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/ext/jaccess.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/ext/localedata.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/ext/nashorn.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/ext/sunec.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/ext/zipfs.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/management-agent.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/lib/dt.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/lib/jconsole.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/lib/sa-jdi.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/lib/tools.jar:/Users/wyaoyao/IdeaProjects/jvm/target/classes study.wyy.jvm.classLoader.MyClassLoaderTest java.io.FileNotFoundException: /Users/wyaoyao/Documents/test/java/lang/Object.class (No such file or directory) at java.io.FileInputStream.open0(Native Method) at java.io.FileInputStream.open(FileInputStream.java:195) at java.io.FileInputStream.<init>(FileInputStream.java:138) at java.io.FileInputStream.<init>(FileInputStream.java:93) at study.wyy.jvm.classLoader.MyClassLoader.loadByte(MyClassLoader.java:14) at study.wyy.jvm.classLoader.MyClassLoader.findClass(MyClassLoader.java:25) at study.wyy.jvm.classLoader.MyClassLoader.loadClass(MyClassLoader.java:65) at java.lang.ClassLoader.loadClass(ClassLoader.java:351) at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:756) at java.lang.ClassLoader.defineClass(ClassLoader.java:635) at study.wyy.jvm.classLoader.MyClassLoader.findClass(MyClassLoader.java:26) at study.wyy.jvm.classLoader.MyClassLoader.loadClass(MyClassLoader.java:65) at java.lang.ClassLoader.loadClass(ClassLoader.java:351) at study.wyy.jvm.classLoader.MyClassLoaderTest.main(MyClassLoaderTest.java:11) Exception in thread "main" java.lang.NoClassDefFoundError: java/lang/Object at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:756) at java.lang.ClassLoader.defineClass(ClassLoader.java:635) at study.wyy.jvm.classLoader.MyClassLoader.findClass(MyClassLoader.java:26) at study.wyy.jvm.classLoader.MyClassLoader.loadClass(MyClassLoader.java:65) at java.lang.ClassLoader.loadClass(ClassLoader.java:351) at study.wyy.jvm.classLoader.MyClassLoaderTest.main(MyClassLoaderTest.java:11) Caused by: java.lang.ClassNotFoundException at study.wyy.jvm.classLoader.MyClassLoader.findClass(MyClassLoader.java:29) at study.wyy.jvm.classLoader.MyClassLoader.loadClass(MyClassLoader.java:65) at java.lang.ClassLoader.loadClass(ClassLoader.java:351)报错了,why呢 因为TargetClass会继承我们的Object类,加载TargetClass会去加载Object,但是我们自定义的ClassLoader加载的路径/Users/wyaoyao/Documents/test下面之后一个TargetClass,没有Object所以报错
如何解决 既然如此,我们可以复制一个Object类放到我们的自定义的ClassLoader加载的路径下,主要包名是java.lang
拷贝过去之后在运行
java.lang.SecurityException: Prohibited package name: java.lang at java.lang.ClassLoader.preDefineClass(ClassLoader.java:655) at java.lang.ClassLoader.defineClass(ClassLoader.java:754) at java.lang.ClassLoader.defineClass(ClassLoader.java:635) at study.wyy.jvm.classLoader.MyClassLoader.findClass(MyClassLoader.java:26) at study.wyy.jvm.classLoader.MyClassLoader.loadClass(MyClassLoader.java:65) at java.lang.ClassLoader.loadClass(ClassLoader.java:351) at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:756) at java.lang.ClassLoader.defineClass(ClassLoader.java:635) at study.wyy.jvm.classLoader.MyClassLoader.findClass(MyClassLoader.java:26) at study.wyy.jvm.classLoader.MyClassLoader.loadClass(MyClassLoader.java:65) at java.lang.ClassLoader.loadClass(ClassLoader.java:351) at study.wyy.jvm.classLoader.MyClassLoaderTest.main(MyClassLoaderTest.java:11) Exception in thread "main" java.lang.NoClassDefFoundError: java/lang/Object at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:756) at java.lang.ClassLoader.defineClass(ClassLoader.java:635) at study.wyy.jvm.classLoader.MyClassLoader.findClass(MyClassLoader.java:26) at study.wyy.jvm.classLoader.MyClassLoader.loadClass(MyClassLoader.java:65) at java.lang.ClassLoader.loadClass(ClassLoader.java:351) at study.wyy.jvm.classLoader.MyClassLoaderTest.main(MyClassLoaderTest.java:11) Caused by: java.lang.ClassNotFoundException at study.wyy.jvm.classLoader.MyClassLoader.findClass(MyClassLoader.java:29) at study.wyy.jvm.classLoader.MyClassLoader.loadClass(MyClassLoader.java:65) at java.lang.ClassLoader.loadClass(ClassLoader.java:351) ... 7 more还是错误,这里就是java的安全机制,Object这种基础类怎么可能让你随便加载和修改的,只会允许被引导类加载器加载
如何办呢 只好判断一下只有是我们自己的包下的类study.wyy.jvm交给我们自定义类加载器去加载,Object这种还是由java自己的去加载
package study.wyy.jvm.classLoader; import java.io.FileInputStream; public class MyClassLoader extends ClassLoader { private String classPath; public MyClassLoader(String classPath) { this.classPath = classPath; } private byte[] loadByte(String name) throws Exception { name = name.replaceAll("\\.", "/"); FileInputStream fis = new FileInputStream(classPath + "/" + name + ".class"); int len = fis.available(); byte[] data = new byte[len]; fis.read(data); fis.close(); return data; } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { try { byte[] bytes = loadByte(name); return defineClass(name, bytes, 0, bytes.length); } catch (Exception e) { e.printStackTrace(); throw new ClassNotFoundException(); } } /** * @Description 重写类加载方法,实现自己的加载逻辑,不委派给双亲加载 * @Author wyaoyao * @Date 2020/9/3 9:33 下午 * @Param * @Return * @Exception */ @Override public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded Class<?> c = findLoadedClass(name); long t0 = System.nanoTime(); // 注释的代码就是现实双亲委派机制,这里直接注释掉不就可以了吗 // try { // if (parent != null) { // c = parent.loadClass(name, false); // } else { // c = findBootstrapClassOrNull(name); // } // } catch (ClassNotFoundException e) { // // ClassNotFoundException thrown if class not found // // from the non-null parent class loader // } if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); if(!name.startsWith("study.wyy.jvm")){ // 自定义的类加载器的parent属性就是AppClassLoader c = this.getParent().loadClass(name); }else { c = findClass(name); } // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } if (resolve) { resolveClass(c); } return c; } } }运行结果
study.wyy.jvm.classLoader.MyClassLoader