当前位置: 首页 > 面试题库 >

为什么自定义系统类加载器不起作用?

顾靖
2023-03-14
问题内容

我正在尝试使用flag覆盖系统的类加载器-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");
    }
}

这是使用命令的输出(请参阅Eclipse run config):java -Djava.system.class.loader=MyLoader -verbose [-Xshare:off](https://stackoverflow.com/a/4393990/632951)

[打开的C:\ Program Files \ Java \ jre7 \ lib \ rt.jar]

[从C:\ Program Files \ Java \ jre7 \ lib \ rt.jar加载java.lang.Object]

[从C:\ Program Files \ Java \ jre7 \ lib \ rt.jar加载java.io.Serializable]

//等等等…由于太长而被省略

[从文件:/ C:/ Documents%20and%20Settings / Owner / Desktop / Programs /
Eclipse%20Workspace%202 / Test93 / bin /加载的MyLoader]

-– MyLoader —
在#constructor(sun.misc.Launcher$AppClassLoader@158046e)中

[从C:\ Program Files \ Java \ jre7 \ lib \
rt.jar加载sun.launcher.LauncherHelper]

[从C:\ Program Files \ Java \ jre7 \ lib \ rt.jar加载java.lang.StringCoding]

[从C:\ Program Files \ Java \ jre7 \ lib \ rt.jar加载java.lang.StringCoding $
StringDecoder]

-– MyLoader — 内部loadClass(Main,false)

[从文件加载的主文件:/ C:/ Documents%20and%20Settings / Owner / Desktop / Programs /
Eclipse%20Workspace%202 / Test93 / bin /]

[从C:\ Program Files \ Java \ jre7 \ lib \ rt.jar加载java.lang.Void]

-–主要—第一行

-–主— getSystemClassLoader():MyLoader @ 8697ce

-–主— getSystemClassLoader()的加载器:sun.misc.Launcher$AppClassLoader@158046e

-–主— 调用Class.forName(javax.crypto.Cipher)

[打开的C:\ Program Files \ Java \ jre7 \ lib \ jce.jar]

[从C:\ Program Files \ Java \ jre7 \ lib \ jce.jar加载javax.crypto.Cipher]

-–主要— 通话完成

可以看出,即使Main使用加载了MyLoaderjavax.crypto.Cipher也没有使用加载了MyLoader。输出显示MyLoader.loadClass仅被调用一次。

从jce.jar加载时,为什么MyLoader.loadClass甚至没有 调用javax.crypto.Cipher


问题答案:

您的问题是自定义类加载器用于加载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。*)。

/** 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参数而不是猜测。



 类似资料:
  • 问题内容: 我们知道我们可以使用以下方法覆盖System 类加载器: 那么,既然它本身是一个类,它是由谁加载的? 我们如何获得该“元”类加载器的类文件? 问题答案: 从Javadoc中获取: 如果在首次调用此方法时定义了系统属性“ java.system.class.loader”,则该属性的值将作为要作为系统类加载器返回的类的名称。 该类使用默认的系统类加载器加载, 并且必须定义一个公共构造函数

  • 本文向大家介绍java 类加载与自定义类加载器详解,包括了java 类加载与自定义类加载器详解的使用技巧和注意事项,需要的朋友参考一下 类加载 所有类加载器,都是ClassLoader的子类。 类加载器永远以.class运行的目录为准。 读取classpath根目录下的文件有以下几种方式: 1 在Java项目中可以通过以下方式获取classspath下的文件: 在Tomcat中tomcat又声明了

  • Home assistant 支持设备显示的高度自定义,你可以给设备更换昵称、图标、图标以及隐藏设备等。 设置方法 设备的个性化由 customize 模块定义,默认配置文件 configuration.yaml 中 customize 位于 homeassistant模块下: homeassistant: # 名称,可为中文 name: 家 # 纬度 latitude: !sec

  • 我试图修改几个类的字节代码,这些类的打包jar文件不在类路径中-它们是在给定URL的运行时由自定义的加载的。我尝试使用和来拦截那些类,但失败了。类加载器是遗留项目的一部分,因此我无法直接对其进行更改。 代理可以很好地处理AppClassLoader“本地”加载的类,但只会忽略那些由自定义类加载器加载的类。 CustomClassLoader: 我的代理中使用的ClassFileTransforme

  • 在parseConfiguration方法中我们通过propertiesElement和settingsAsProperties两个方法已经完成了对mybatis属性配置的解析和准备工作。 //issue #117 read properties first // 加载资源配置文件,并覆盖对应的属性[properties节点] propertiesElement(root.evalNode("

  • 我一直在Java玩简单的自定义类加载器,到目前为止,对于非模块相关的类来说,一切都按照预期工作。但是,我似乎找不到任何方法来使用我的类加载器从模块加载类,即使与模块相关的方法已经被重载。我尝试做的是从模块“HelloModularWorld”加载一个类,并运行它的main。然而,当我指定包所在的目录时,它将“正常”加载,并报告为在未命名模块中。我错过了什么? 类加载器只是从文件系统的其他地方加载类