我有一个java-agent,它依赖于一些依赖库。我们有一个定制的类加载器,它不执行全局绑定,并提供确定性的类加载,并通过-Djava.system.class.loader=指定。然而,java代理类似乎仍然是通过AppClassLoader加载的,导致了一些ClassNotFoundException,在这上面浪费了几个小时后,我偶然发现了这个JDK bug,它概述了这个问题,现在已经关闭,但修复只针对JDK 9+。
我想知道在Java中是否有一种方法可以强制java- <=8类由指定的类加载器加载。
定制的类加载器看起来像这样,
public class CustomClassloader extends URLClassLoader{
public CustomClassloader(ClassLoader parent) throws Exception {
//getJarPaths has the path of the java-agent jar as well as
//all dependency jar it needs.
super(getJarPaths(), parent);
}
private static URL[] getJarPaths() throws Exception {
//custom implementation
}
public void appendToClassPathForInstrumentation(String path) throws MalformedURLException {
assert(Thread.holdsLock(this));
super.addURL(Paths.get(path).toUri().toURL());
}
}上面的类加载器可以很好地与JDK9+一起工作,而javaagent实际上是由自定义的类加载器加载的。
发布于 2020-06-07 20:08:51
JDK8和JDK的不同之处在于,JDK8将-javaagent 9+添加到系统类路径中,而JDK 9+则调用自定义ClassLoader的appendToClassPathForInstrumentation。
因此,在JDK8中,如果在尝试查找类本身之前将自定义ClassLoader委托给父ClassLoader (即系统类加载器),则代理将由系统类加载器加载。为了解决这个问题,您的自定义ClassLoader可能会颠倒委托顺序:首先尝试查找类本身,然后委托给父类。
例如,可以通过覆盖loadClass方法来实现,如下所示:
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
c = findClass(name);
} catch (ClassNotFoundException e) {
c = super.loadClass(name, false);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
@Override
public URL getResource(String name) {
URL url = findResource(name);
if (url != null) {
return url;
}
return super.getResource(name);
}
@Override
public Enumeration<URL> getResources(String name) throws IOException {
List<URL> urls = Collections.list(findResources(name));
urls.addAll(Collections.list(getParent().getResources(name)));
return Collections.enumeration(urls);
}https://stackoverflow.com/questions/62229975
复制相似问题