我已经阅读了https://frankkieviet.blogspot.com/2009/03/javalanglinkageerror-loader-constraint.html帖子,并使用演示代码来模拟LinkageError。
/**
* A self-first delegating classloader. It only loads specified classes self-first; other
* classes are loaded from the parent.
*/
private static class CustomCL extends ClassLoader {
private Set<String> selfFirstClasses;
private String label;
public CustomCL(String name, ClassLoader parent, String... selfFirsNames) {
super(parent);
this.label = name;
this.selfFirstClasses = new HashSet<String>(Arrays.asList(selfFirsNames));
}
public Class<?> findClass(String name) throws ClassNotFoundException {
if (selfFirstClasses.contains(name)) {
try {
byte[] buf = new byte[100000];
String loc = name.replace('.', '/') + ".class";
InputStream inp = Demo.class.getClassLoader().getResourceAsStream(loc);
int n = inp.read(buf);
inp.close();
System.out.println(label + ": Loading " + name + " in custom classloader");
return defineClass(name, buf, 0, n);
} catch (Exception e) {
throw new ClassNotFoundException(name, e);
}
}
// Is never executed in this test
throw new ClassNotFoundException(name);
}
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (findLoadedClass(name) != null) {
System.out.println(label + ": already loaded(" + name + ")");
return findLoadedClass(name);
}
// Override parent-first behavior into self-first only for specified classes
if (selfFirstClasses.contains(name)) {
return findClass(name);
} else {
System.out.println(label + ": super.loadclass(" + name + ")");
return super.loadClass(name, resolve);
}
}
public String toString() {
return label;
}
}
public static class User {
}
public static class LoginEJB {
static {
System.out.println("LoginEJB loaded");
}
public static void login(User u) {
}
}
public static class Servlet {
public static void doGet() {
User u = new User();
System.out.println("Logging in with User loaded in " + u.getClass().getClassLoader());
LoginEJB.login(u);
}
}
private static class EjbCL extends CustomCL {
public EjbCL(ClassLoader parent, String... selfFirsNames) {
super("Ejb", parent, selfFirsNames);
}
}
private static class SfWebCL extends CustomCL {
public SfWebCL(ClassLoader parent, String... selfFirsNames) {
super("SFWeb", parent, selfFirsNames);
}
}
public static void test1() throws Exception {
CustomCL ejbCL = new EjbCL(Demo.class.getClassLoader(), "com.test.zim.Demo$User",
"com.test.zim.Demo$LoginEJB");
CustomCL sfWebCL = new SfWebCL(ejbCL, "com.test.zim.Demo$User",
"com.test.zim.Demo$Servlet");
System.out.println("Logging in, self-first");
sfWebCL.loadClass("com.test.zim.Demo$Servlet", false).getMethod("doGet").invoke(null);
// sfWebCL.loadClass("com.test.zim.Demo$Servlet", false).getMethod("doGet");
// sfWebCL.loadClass("com.test.zim.Demo$User", false);
// sfWebCL.loadClass("com.test.zim.Demo$LoginEJB", false).getMethods();
System.out.println("Examining methods of LoginEJB");
ejbCL.loadClass("com.test.zim.Demo$LoginEJB", false).getMethods();
}
public static void main(String[] args) {
try {
test1();
} catch (Throwable e) {
e.printStackTrace(System.out);
}
}
运行测试代码后,输出为:
Logging in, self-first
SFWeb: Loading com.test.zim.Demo$Servlet in custom classloader
SFWeb: super.loadclass(java.lang.Object)
Ejb: super.loadclass(java.lang.Object)
SFWeb: Loading com.test.zim.Demo$User in custom classloader
SFWeb: super.loadclass(java.lang.System)
Ejb: super.loadclass(java.lang.System)
SFWeb: super.loadclass(java.lang.StringBuilder)
Ejb: super.loadclass(java.lang.StringBuilder)
SFWeb: super.loadclass(java.lang.Class)
Ejb: super.loadclass(java.lang.Class)
SFWeb: super.loadclass(java.io.PrintStream)
Ejb: super.loadclass(java.io.PrintStream)
Logging in with User loaded in SFWeb
SFWeb: super.loadclass(com.test.zim.Demo$LoginEJB)
Ejb: Loading com.test.zim.Demo$LoginEJB in custom classloader
Ejb: super.loadclass(java.lang.Object)
Ejb: super.loadclass(java.lang.System)
Ejb: super.loadclass(java.io.PrintStream)
LoginEJB loaded
Examining methods of LoginEJB
Ejb: already loaded(com.test.zim.Demo$LoginEJB)
Ejb: Loading com.test.zim.Demo$User in custom classloader
java.lang.LinkageError: loader constraint violation: loader (instance of com/test/zim/Demo$EjbCL) previously initiated loading for a different type with name "com/test/zim/Demo$User"
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
at com.test.zim.Demo$CustomCL.findClass(Demo.java:39)
at com.test.zim.Demo$CustomCL.loadClass(Demo.java:57)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
at java.lang.Class.privateGetPublicMethods(Class.java:2902)
at java.lang.Class.getMethods(Class.java:1615)
at com.test.zim.Demo.test1(Demo.java:117)
at com.test.zim.Demo.main(Demo.java:146)
我真的不明白为什么它会显示链接错误,以及当我更改代码时
sfWebCL.loadClass("com.test.zim.Demo$Servlet", false).getMethod("doGet").invoke(null);
到
sfWebCL.loadClass("com.test.zim.Demo$Servlet", false).getMethod("doGet");
sfWebCL.loadClass("com.test.zim.Demo$User", false);
sfWebCL.loadClass("com.test.zim.Demo$LoginEJB", false).getMethods();
错误消失了,我有一些疑问:
在更改代码之前,linkageError是如何发生的?我认为“ com / test / zim / Demo $ User”类应该共存于两个不同的类加载器中,因为两个类加载器都调用了自己的defineClass方法?为什么错误提示ejbClassLoader用不同的名称加载User类,我认为这是EJBClassLoader第一次加载User类?
更改代码后,我编写了一些代码来手动加载“ User”和“ LoginEJB”类,而不是调用“ doGet()”方法,这两个代码段之间有什么区别,为什么后面的代码没有错误吗?
该错误发生在ClassLoader.defineClass()阶段,defineClass()的真正含义是什么?
ClassLoader.findLoadedClass()方法,是否意味着找到ClassLoader曾经加载过的类(例如Foo.class)?如果其父ClassLoader之前已加载Foo.class,则应返回true。但是,如果子类加载器首先加载Foo.class,则父类加载器应再次加载它,并且会有两个Foo.class,它们将共存而不会出现问题?