java class 识别 classloader
java class identify about classloader
我对Class的身份有疑问,Class的身份应该是The same classloader instance
+ The same class full path
。但是我做了一些测试用例,它不起作用。
我有一个自定义的ClassLoader:
import java.io.*;
public class FileSystemClassLoader extends ClassLoader {
private String rootDir;
public FileSystemClassLoader(String rootDir) {
this.rootDir = rootDir;
}
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] classData = getClassData(name);
if (classData == null) {
throw new ClassNotFoundException();
} else {
return defineClass(name, classData, 0, classData.length);
}
}
private byte[] getClassData(String className) {
String path = classNameToPath(className);
try {
InputStream ins = new FileInputStream(path);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int bufferSize = 4096;
byte[] buffer = new byte[bufferSize];
int bytesNumRead = 0;
while ((bytesNumRead = ins.read(buffer)) != -1) {
baos.write(buffer, 0, bytesNumRead);
}
return baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private String classNameToPath(String className) {
return rootDir + File.separatorChar
+ className.replace('.', File.separatorChar) + ".class";
}
}
Sample
class:
public class Sample {
private Sample instance;
public void setSample(Object instance) {
this.instance = (Sample) instance;
}
}
还有一个测试用例:
String classDataRootPath = "/Users/haolin/Github/jvm/target/classes";
FileSystemClassLoader fscl1 = new FileSystemClassLoader(classDataRootPath);
FileSystemClassLoader fscl2 = new FileSystemClassLoader(classDataRootPath);
String className = "me.hao0.jvm.classloader.Sample";
try {
Class<?> class1 = fscl1.loadClass(className);
Object obj1 = class1.newInstance();
Class<?> class2 = fscl2.loadClass(className);
Object obj2 = class2.newInstance();
Method setSampleMethod = class1.getMethod("setSample", Object.class);
setSampleMethod.invoke(obj1, obj2);
} catch (Exception e) {
e.printStackTrace();
}
在setSampleMethod.invoke(obj1, obj2)
的时候应该出现ClassCastException
,因为obj1和obj2不一样Class(他们的ClassLoader不同),但是代码运行良好,不会抛出ClassCastException
。
有人可以推荐吗?
除了一个重要的细节,你是对的。类加载器存在于层次结构中,每个 ClassLoader
在尝试加载 class 本身之前委托给其父级。因此,如果您的 classloader 的共同祖先找到请求的 class,那么您的 classloader 将 return 是 class 的同一实例。考虑这个例子:
public class Foo {
public void isFoo(Object obj) {
System.out.println("object is a Foo: " + (obj instanceof Foo));
Foo foo = (Foo) obj;
}
}
测试:
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
public class Test {
public static void main(String[] args) throws Exception {
File path = new File(".");
URL[] urls = new URL[] { path.toURI().toURL() };
URLClassLoader cl1 = new URLClassLoader(urls);
URLClassLoader cl2 = new URLClassLoader(urls);
Class c1 = cl1.loadClass("Foo");
Class c2 = cl2.loadClass("Foo");
System.out.println("same class instance: " + (c1 == c2));
Object o1 = c1.newInstance();
Object o2 = c2.newInstance();
Method m = c1.getDeclaredMethod("isFoo", Object.class);
m.invoke(o1, o2);
}
}
输出为:
same class instance: true
object is a Foo: true
发生这种情况是因为当前目录是默认 class 路径的一部分,因此父 classloader 查找并加载 Foo
,并且两个自定义 classloader return 来自父级的实例。
现在,对测试进行此更改 class 并重新编译:
File path = new File("foo");
创建 foo/
目录并将 Foo.class
移动到那里。现在输出是:
same class instance: false
object is a Foo: false
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at Test.main(Test.java:21)
Caused by: java.lang.ClassCastException: Foo cannot be cast to Foo
at Foo.isFoo(Foo.java:4)
... 5 more
这是您所期望的。系统 classloader 无法再找到 Foo
,因此自定义 classloader 加载它的单独实例。 JVM 将两个 Class
实例视为不同的 classes,即使它们是相同的,并且转换失败。
我对Class的身份有疑问,Class的身份应该是The same classloader instance
+ The same class full path
。但是我做了一些测试用例,它不起作用。
我有一个自定义的ClassLoader:
import java.io.*;
public class FileSystemClassLoader extends ClassLoader {
private String rootDir;
public FileSystemClassLoader(String rootDir) {
this.rootDir = rootDir;
}
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] classData = getClassData(name);
if (classData == null) {
throw new ClassNotFoundException();
} else {
return defineClass(name, classData, 0, classData.length);
}
}
private byte[] getClassData(String className) {
String path = classNameToPath(className);
try {
InputStream ins = new FileInputStream(path);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int bufferSize = 4096;
byte[] buffer = new byte[bufferSize];
int bytesNumRead = 0;
while ((bytesNumRead = ins.read(buffer)) != -1) {
baos.write(buffer, 0, bytesNumRead);
}
return baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private String classNameToPath(String className) {
return rootDir + File.separatorChar
+ className.replace('.', File.separatorChar) + ".class";
}
}
Sample
class:
public class Sample {
private Sample instance;
public void setSample(Object instance) {
this.instance = (Sample) instance;
}
}
还有一个测试用例:
String classDataRootPath = "/Users/haolin/Github/jvm/target/classes";
FileSystemClassLoader fscl1 = new FileSystemClassLoader(classDataRootPath);
FileSystemClassLoader fscl2 = new FileSystemClassLoader(classDataRootPath);
String className = "me.hao0.jvm.classloader.Sample";
try {
Class<?> class1 = fscl1.loadClass(className);
Object obj1 = class1.newInstance();
Class<?> class2 = fscl2.loadClass(className);
Object obj2 = class2.newInstance();
Method setSampleMethod = class1.getMethod("setSample", Object.class);
setSampleMethod.invoke(obj1, obj2);
} catch (Exception e) {
e.printStackTrace();
}
在setSampleMethod.invoke(obj1, obj2)
的时候应该出现ClassCastException
,因为obj1和obj2不一样Class(他们的ClassLoader不同),但是代码运行良好,不会抛出ClassCastException
。
有人可以推荐吗?
除了一个重要的细节,你是对的。类加载器存在于层次结构中,每个 ClassLoader
在尝试加载 class 本身之前委托给其父级。因此,如果您的 classloader 的共同祖先找到请求的 class,那么您的 classloader 将 return 是 class 的同一实例。考虑这个例子:
public class Foo {
public void isFoo(Object obj) {
System.out.println("object is a Foo: " + (obj instanceof Foo));
Foo foo = (Foo) obj;
}
}
测试:
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
public class Test {
public static void main(String[] args) throws Exception {
File path = new File(".");
URL[] urls = new URL[] { path.toURI().toURL() };
URLClassLoader cl1 = new URLClassLoader(urls);
URLClassLoader cl2 = new URLClassLoader(urls);
Class c1 = cl1.loadClass("Foo");
Class c2 = cl2.loadClass("Foo");
System.out.println("same class instance: " + (c1 == c2));
Object o1 = c1.newInstance();
Object o2 = c2.newInstance();
Method m = c1.getDeclaredMethod("isFoo", Object.class);
m.invoke(o1, o2);
}
}
输出为:
same class instance: true
object is a Foo: true
发生这种情况是因为当前目录是默认 class 路径的一部分,因此父 classloader 查找并加载 Foo
,并且两个自定义 classloader return 来自父级的实例。
现在,对测试进行此更改 class 并重新编译:
File path = new File("foo");
创建 foo/
目录并将 Foo.class
移动到那里。现在输出是:
same class instance: false
object is a Foo: false
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at Test.main(Test.java:21)
Caused by: java.lang.ClassCastException: Foo cannot be cast to Foo
at Foo.isFoo(Foo.java:4)
... 5 more
这是您所期望的。系统 classloader 无法再找到 Foo
,因此自定义 classloader 加载它的单独实例。 JVM 将两个 Class
实例视为不同的 classes,即使它们是相同的,并且转换失败。