Я прочитал пост 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 (), значит ли это найти класс (например, Foo.class), который когда-либо загружал ClassLoader?Если его родительский ClassLoader загружал Foo.class ранее, он должен вернуть true.Но если дочерний загрузчик классов сначала загрузит Foo.class, родительский загрузчик классов должен загрузить его снова, и будет два Foo.class, и они будут сосуществовать без проблем?