gpt4 book ai didi

java - 为什么自定义系统类加载器不工作?

转载 作者:搜寻专家 更新时间:2023-10-31 08:13:29 24 4
gpt4 key购买 nike

我正在尝试使用标志 -Djava.system.class.loader=MyLoader 覆盖系统的类加载器.然而,MyLoader加载类时仍未使用。

MyLoader的代码:

public class MyLoader extends ClassLoader {
public MyLoader(ClassLoader parent) {
super(S(parent));
}

private static ClassLoader S(ClassLoader cl) {
System.out.println("---MyLoader--- inside #constructor(" + cl + ")...");
return cl;
}

@Override
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
System.out.println("---MyLoader--- inside loadClass(" + name + ", " + resolve + ")...");
return super.loadClass(name, resolve);
}
}

这是主要代码:

public class Main {
public static void main(final String args[]) throws Exception {
System.out.println("---Main--- first line");
System.out.println("---Main--- getSystemClassLoader(): " + ClassLoader.getSystemClassLoader());
System.out.println("---Main--- getSystemClassLoader()'s loader: " + ClassLoader.getSystemClassLoader().getClass().getClassLoader());
Call("javax.crypto.Cipher");
}

public static void Call(final String class_name) throws Exception {
System.out.println("---Main--- calling Class.forName(" + class_name + ")...");
Class.forName(class_name);
System.out.println("---Main--- call complete");
}
}

这是使用命令 java -Djava.system.class.loader=MyLoader -verbose <a href="https://stackoverflow.com/a/4393990/632951" rel="noreferrer noopener nofollow">-Xshare:off</a> Main 的输出(参见 Eclipse run config):

[Opened C:\Program Files\Java\jre7\lib\rt.jar]
[Loaded java.lang.Object from C:\Program Files\Java\jre7\lib\rt.jar]
[Loaded java.io.Serializable from C:\Program
Files\Java\jre7\lib\rt.jar]
// etc etc... omitted since it's too long
[Loaded MyLoader from file:/C:/Documents%20and%20Settings/Owner/Desktop/Programs/Eclipse%20Workspace%202/Test93/bin/]
---MyLoader--- inside #constructor(sun.misc.Launcher$AppClassLoader@158046e)...
[Loaded sun.launcher.LauncherHelper from C:\Program Files\Java\jre7\lib\rt.jar]
[Loaded java.lang.StringCoding from C:\Program Files\Java\jre7\lib\rt.jar]
[Loaded java.lang.StringCoding$StringDecoder from C:\Program Files\Java\jre7\lib\rt.jar]
---MyLoader--- inside loadClass(Main, false)...
[Loaded Main from file:/C:/Documents%20and%20Settings/Owner/Desktop/Programs/Eclipse%20Workspace%202/Test93/bin/]
[Loaded java.lang.Void from C:\Program Files\Java\jre7\lib\rt.jar]
---Main--- first line
---Main--- getSystemClassLoader(): MyLoader@8697ce
---Main--- getSystemClassLoader()'s loader: sun.misc.Launcher$AppClassLoader@158046e
---Main--- calling Class.forName(javax.crypto.Cipher)...
[Opened C:\Program Files\Java\jre7\lib\jce.jar]
[Loaded javax.crypto.Cipher from C:\Program Files\Java\jre7\lib\jce.jar]
---Main--- call complete

可以看出,即使Main使用 MyLoader 加载, javax.crypto.Cipher 未使用 MyLoader 加载.输出显示 MyLoader.loadClass只调用一次。

为什么是MyLoader.loadClass javax.crypto.Cipher 时甚至调用是从 jce.jar 加载的吗?

最佳答案

您的问题是您的自定义类加载器正用于加载 Main,但它的 loadClass 只是委托(delegate)给 parent 类加载器来加载 Main。所以。在 Main 中,如果您调用 Main.class.getClassLoader(),它将返回 sun.misc.Launcher$AppClassLoader不是 MyLoader.

要查看哪个类加载器将用于 Class.forName(String) 调用和您自己类的符号引用,您应该打印 getClass().getClassLoader()(或来自静态方法的 MyClass.class.getClassLoader())。使用的类加载器定义了当前正在执行其代码的类。除了使用反射 (Class.forName(String, boolean, ClassLoader)) 之外,这在任何地方都是规则。

一旦从父类加载器加载了一个类,它加载的任何类也将使用该原始类加载器。因此,一旦 Main 从 sun.misc.Launcher$AppClassLoader 类加载器加载,它调用的所有类都将来自同一个类加载器,不是您自己的 MyLoader。类似地,一旦 javax.crypto.Cypher 类从 null(又名 Bootstrap)类加载器加载,它提到的任何类也将来自引导类加载器,除了它使用反射加载的类( SPI)。

要停止从 sun.misc.Launcher$AppClassLoader 类加载器加载类,请将 MyLoader 的 CLASSPATH 设置为 AppClassLoader 的 CLASSPATH并且不要将类加载委托(delegate)给 AppClassLoader。请注意,这将导致所有 CLASSPATH 类从 MyLoader 加载,但来自 JDK 的类通常仍会从 null (Bootstrap) 类加载器加载。

要停止从引导类加载器加载 JDK 类,您必须明确地将 JDK 放入类路径并修改 loadClass 以不首先检查某些类的父类。从您自己的类加载器加载 JDK 类是很微妙的;某些类(例如 java.lang.String)必须从 boostrap 类加载器加载。这不是我自己尝试过的东西,但我读过 OSGi从引导类加载器加载 java.*,但从它自己的类加载器图表加载其他 JDK 类(例如 sun.* 和 javax.*)。

/** Run with -Djava.system.class.loader=MyLoader to use this class loader. */
public static class MyLoader extends URLClassLoader {
public MyLoader(ClassLoader launcherClassLoader) {
super(getUrls(launcherClassLoader), launcherClassLoader.getParent());
}

private static URL[] getUrls(ClassLoader cl) {
System.out.println("---MyLoader--- inside #constructor(" + cl + ")...");
return ((URLClassLoader) cl).getURLs();
}

@Override public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
System.out.println("---MyLoader--- inside loadClass(" + name + ", " + resolve + ")...");
return super.loadClass(name, resolve);
}
}

至于 JDK 中的 SPI 工厂(想想 XML 解析器和加密实现),它们使用反射从 ContextClassLoader 或 SystemClassLoader 或一个接一个地加载命名类,因为它们希望您能够定义自己的类实现,并且引导类加载器不加载用户定义的类。他们使用的两者之一似乎没有一致性,我希望他们只是采用 ClassLoader 参数而不是猜测。

关于java - 为什么自定义系统类加载器不工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25478223/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com