您的问题是自定义类加载器用于加载Main,但是其loadClass只是委托给 父
类加载器来加载Main。因此。在Main中,如果您调用
Main.class.getClassLoader(),它将返回
sun.misc.Launcher$AppClassLoader,
而不是
MyLoader。
要查看自己的类将使用哪种类加载器进行
Class.forName(String)调用和符号引用,应进行打印
getClass().getClassLoader()(或
MyClass.class.getClassLoader()从静态方法进行打印)。使用的类加载器是定义当前正在执行其代码的类的加载器。这是所有地方的规则,但使用反射(
Class.forName(String,boolean, ClassLoader))时除外。
一旦从父类加载器加载了一个类,它加载的任何类也将使用该原始类加载器。因此,一旦从
sun.misc.Launcher$AppClassLoader类加载器加载Main
,它调用的所有类都将来自同一类加载器, 而不是
您自己的
MyLoader。同样,一旦
javax.crypto.Cypher从空(也称为Bootstrap)类加载器加载了该类,则它提及的任何类也将来自引导类加载器,但它使用反射(SPI)加载的类除外。
要停止从
sun.misc.Launcher$AppClassLoader类加载器加载类,请将
MyLoaderCLASSPATH
设置为
AppClassLoaderCLASSPATH,不要将classloading委托给
AppClassLoader。请注意,这将导致从MyLoader加载所有CLASSPATH类,但通常仍将从null(Bootstrap)类加载器加载JDK的类。
要停止从引导类加载器加载JDK类,必须显式将JDK放入类路径并修改loadClass以不先检查某些类的父类。从您自己的类加载器加载JDK类非常困难。有些类(例如java.lang.String)
必须
从boostrap类加载器加载。这不是我自己尝试过的事情,但是我读过OSGi从引导类加载器加载java。,但从其自己的类加载器图中加载其他JDK类(例如sun。和javax。*)。
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参数而不是猜测。



