自定义Java类加载器未用于加载依赖项?
我一直在尝试设置一个自定义类加载器,该加载器拦截类,以打印出哪些类正在加载到应用程序中。类加载器如下所示自定义Java类加载器未用于加载依赖项?,java,class,jvm,metaprogramming,classloader,Java,Class,Jvm,Metaprogramming,Classloader,我一直在尝试设置一个自定义类加载器,该加载器拦截类,以打印出哪些类正在加载到应用程序中。类加载器如下所示 public class MyClassLoader extends ClassLoader { @Override public Class<?> loadClass(String name) throws ClassNotFoundException { System.out.println("Loading: " + name);
public class MyClassLoader extends ClassLoader {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
System.out.println("Loading: " + name);
return super.loadClass(name);
}
}
通过
它打印出来了
Loading: Scripts
Loading: java.lang.Object
Loading: java.lang.String
Loading: org.python.util.PythonInterpreter
Python build/
.idea/*
*.iml
RESULT: Success! Nothing broke
这似乎很奇怪org.python.util.PythonInterpreter
不是一个简单的类,它依赖于org.python.util
包中的一大堆其他类。这些类显然正在被加载,因为exec
'd python代码能够做一些事情并读取我的文件。但是,由于某些原因,那些类没有被加载了PythonInterpreter
的类加载器加载
为什么呢?我的印象是,用于加载类C
的类加载器将用于加载C
所需的所有其他类,但这里显然没有发生这种情况。这个假设错了吗?如果是,我如何设置它,使C
的所有可传递依赖项都由我的类加载器加载
编辑:
建议使用URLClassLoader
进行一些实验。我在loadClass()
中修改了委托:
以及使MyClassLoader子类URLClassLoader而非普通ClassLoader,通过以下方式获取URL:
super(((URLClassLoader)ClassLoader.getSystemClassLoader()).getURLs());
但这似乎不是一件正确的事情。特别是,
getResourceAsStream()
对我请求的所有类,甚至是像Jython lib这样的非系统类,都返回空值。如果重写另一个loadClass()方法怎么办
受保护类loadClass(字符串名称,布尔解析)
在实例化PythonInterpreter之前,可以使用PySystemState
对象指定自定义类加载器
PySystemState state = new PySystemState();
state.setClassLoader(classLoader);
PythonInterpreter interp = new PythonInterpreter(table, state);
如果你这样做
System.out.println( p.getClass().getClassLoader() );
您将看到p
的类加载器不是您的MyClassLoader-bcl
。它实际上是由bcl
的父级系统类加载器加载的
当PythonInterpreter
加载其依赖类时,它将使用其实际的类加载器,即系统类加载器,而不是您的bcl
,因此不会达到您的拦截
要解决这个问题,类加载器不能委托给其父类,它必须自己实际加载类
为此,您可以子类化
URLClassLoader
(从系统类加载器中窃取URL)。如果您想在加载类时打印它们,那么在JVM上打开verbose:class选项如何
java -verbose:class your.class.name.here
要回答您的直接问题:
为什么呢?我的印象是,用于加载类C的类加载器将用于加载C所需的所有其他类,但这显然不是在这里发生的。这个假设错了吗?如果是,我如何设置它,使C的所有可传递依赖项都由我的类加载器加载
在搜索类加载器时,搜索从叶类加载器执行到根,当Java计算出必须加载一个新类时,搜索从类加载器树的根向下执行到启动类解析的叶
为什么??考虑一下您的自定义类是否想从java标准库中加载一些东西。正确的答案是,这应该由系统类加载器加载,以便最大限度地共享类。特别是当你认为类被加载时,可能会加载更多的类。
这还解决了这样一个问题,即可能会在不同的类加载器中加载多个系统类实例,每个实例都具有相同的完全限定名EDIT类将在其类加载器中正确解析。然而,有两个问题
a
和b
<如果在不同的类加载器中实例化了a
和b
,则code>a.getClass().isInstance(b)和a.getClass()==b.getClass()
不为真。这将导致可怕的问题李>
- findClass(字符串名称)-您可以在需要时重写此方法 查找具有通常的父级优先委派的类
- loadClass(字符串名称,布尔解析)-如果要更改,请重写此方法 类加载委托的方式
protected Class<?> loadClass(String name, boolean resolve)
PySystemState state = new PySystemState();
state.setClassLoader(classLoader);
PythonInterpreter interp = new PythonInterpreter(table, state);
System.out.println( p.getClass().getClassLoader() );
java -verbose:class your.class.name.here
import java.io.IOException;
import java.io.InputStream;
import static org.apache.commons.io.IOUtils.toByteArray;
import static org.apache.commons.io.IOUtils.closeQuietly;
...
public class MyClassLoader
extends ClassLoader {
MyClassLoaderListener listener;
MyClassLoader(ClassLoader parent, MyClassLoaderListener listener) {
super(parent);
this.listener = listener;
}
@Override
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
// respect the java.* packages.
if( name.startsWith("java.")) {
return super.loadClass(name, resolve);
}
else {
// see if we have already loaded the class.
Class<?> c = findLoadedClass(name);
if( c != null ) return c;
// the class is not loaded yet. Since the parent class loader has all of the
// definitions that we need, we can use it as our source for classes.
InputStream in = null;
try {
// get the input stream, throwing ClassNotFound if there is no resource.
in = getParent().getResourceAsStream(name.replaceAll("\\.", "/")+".class");
if( in == null ) throw new ClassNotFoundException("Could not find "+name);
// read all of the bytes and define the class.
byte[] cBytes = toByteArray(in);
c = defineClass(name, cBytes, 0, cBytes.length);
if( resolve ) resolveClass(c);
if( listener != null ) listener.classLoaded(c);
return c;
} catch (IOException e) {
throw new ClassNotFoundException("Could not load "+name, e);
}
finally {
closeQuietly(in);
}
}
}
}
public interface MyClassLoaderListener {
public void classLoaded( Class<?> c );
}
MyClassLoader classLoader = new MyClassLoader(this.getClass().getClassLoader(), new MyClassLoaderListener() {
public void classLoaded(Class<?> c) {
System.out.println(c.getName());
}
});
classLoader.loadClass(...);