从Java SE 规范和“ Java 虚拟机内部”等许多地方,他们都指出,当常量池条目的解析需要加载类型时,虚拟机使用加载引用类型的相同类加载器来加载引用类型。
但是,我发现这条规则并不总是成立:
package com.alaneuler.test;
import java.io.InputStream;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class LinkageMain {
static class MyClassLoader extends ClassLoader {
private Set<String> selfFirstClasses;
private String name;
public MyClassLoader(String name, ClassLoader parent, String... selfFirstNames) {
super(parent);
this.name = name;
selfFirstClasses = new HashSet<>(Arrays.asList(selfFirstNames));
}
@Override
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (selfFirstClasses.contains(name)) {
String filename = name.substring(name.lastIndexOf(".") + 1) + ".class";
try (InputStream is = getClass().getResourceAsStream(filename)) {
byte[] buf = new byte[is.available()];
int len = is.read(buf);
System.out.println(this.name + ": loading " + name);
return defineClass(name, buf, 0, len);
} catch (Exception e) {
e.printStackTrace();
}
}
if (!name.startsWith("java.")) {
System.out.println(this.name + ": super.loadClass(" + name + ")");
}
return super.loadClass(name, resolve);
}
@Override
public String toString() {
return name;
}
}
public static class User {}
public static class Login {
public void login(User u) {
System.out.println("--- login called with user loaded by " + u.getClass().getClassLoader() + " ---");
}
}
public static class Main {
public static void process() {
User u = new User();
new Login().login(u);
}
}
public static void main(String[] args) throws Exception {
MyClassLoader baseCL = new MyClassLoader("Base", LinkageMain.class.getClassLoader(),
"com.alaneuler.test.LinkageMain$User",
"com.alaneuler.test.LinkageMain$Login");
MyClassLoader specialCL = new MyClassLoader("specialCL", baseCL,
"com.alaneuler.test.LinkageMain$User",
"com.alaneuler.test.LinkageMain$Main");
specialCL.loadClass("com.alaneuler.test.LinkageMain$Main").getMethod("process").invoke(null);
}
}
我定义了一个自定义的ClassLoader
,它在它的构造函数中接受一个应该由它自己定义的类名列表。基于此,baseCL
配置为加载类和加载和Login
(我User
知道这个设置破坏了类加载器委托模型)。specialCl
User
Main
运行此示例会产生:
specialCL: loading com.alaneuler.test.LinkageMain$Main
specialCL: loading com.alaneuler.test.LinkageMain$User
specialCL: super.loadClass(com.alaneuler.test.LinkageMain$Login)
Base: loading com.alaneuler.test.LinkageMain$Login
--- login called with user loaded by specialCL ---
它说login
方法是用notu
加载的对象调用的。这种现象与一开始所说的概念相反,这真的很奇怪,我不知道哪里出了问题。specialCl
baseCL
此外,使用baseCl
to load classUser
总是失败并显示LinkageError
: loader constraint violation: loader (instance of com/alaneuler/test/LinkageMain$MyClassLoader) previously initiated loading for a different type with name "com/alaneuler/test/LinkageMain$User"
。
我的问题是:
- 不应该调用
login
立即引发错误吗? - 为什么以下
baseCL
加载的用法User
失败了LinkageError
?
任何帮助,将不胜感激!
归功于 Frank Kieviet 的博客,因为示例是从他对LinkageError
.