文章目录
1 打破双亲委派机制
上节自定义的类加载器,实现了加载指定目录的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