【问题标题】:Using URLClassLoader to load .class file使用 URLClassLoader 加载 .class 文件
【发布时间】:2015-12-22 15:09:57
【问题描述】:

我知道以前有人问过这个问题:

How to use URLClassLoader to load a *.class file?

但是由于缺乏示例,我不太了解。我目前正在做一个项目并尝试加载用户给定的 .class 对象,这些对象可以位于机器上的任何目录中。

//Create URL to hash function class file
URL url_hashFunctionPath = new URL("file:///" + _sHashFunctionFilePath);

//Packet URL to a URL array to be used by URLClassLoader
URL[] urlA_hashFunctionPath = {url_hashFunctionPath};

//Load URL for hash function via a URL class loader
URLClassLoader urlCl_hashFunctionClassLoader = new URLClassLoader(urlA_hashFunctionPath);

//Load user hash function into class to initialize later (TEST: HARD CODE FOR NOW)
m_classUserHashClass = urlCl_hashFunctionClassLoader.loadClass(_sUserHashClassName);

最后一行给了我一个 ClassNotFoundException,从我的实验和理解用户给定的类函数必须在类路径中?

PS:第一次发布问题,如果我没有遵循适当的方式,请随时纠正我。

//解决方案

我在 [WillShackleford][1] 的慷慨帮助下得出的解决方案,该解决方案可以在给定的文件路径中加载 .class 文件。有关详细信息,请参阅代码及其给定的 cmets。

//The absolute file path to the class that is to be loaded (_sHashFunctionFilePath = absolute file path)
String pathToClassFile = _sHashFunctionFilePath;
System.out.println("File to class: " + _sHashFunctionFilePath);

//Declare the process builder to execute class file at run time (Provided filepath to class)
ProcessBuilder pb = new ProcessBuilder("javap", pathToClassFile);
try
{
    //Start the process builder
    Process p = pb.start();

    //Declare string to hold class name
    String classname = null;
    //Declare buffer reader to read the class file & get class name
    try(BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream())))
    {
        String line;
        while(null != (line = br.readLine()))
        {
            if(line.startsWith("public class"))
            {
                classname = line.split(" ")[2];
                break;
            }
        }
        System.out.println("classname = " + classname);
    }
    catch(IOException _error)
    {

    }

    //Declare file path to directory containing class to be loaded
    String pathToPackageBase = pathToClassFile.substring(0, pathToClassFile.length() - (classname + ".class").length());
    System.out.println("pathToPackageBase = " + pathToPackageBase);

    try
    {
        //Create class to hold the class to be loaded via a URL class loader
        Class clss = new URLClassLoader(
                new URL[]{new File(pathToPackageBase).toURI().toURL()}
        ).loadClass(classname);

        //Create ab object/instance of said class
        Object test = clss.newInstance();

        //Declare & create requested method from user hash function class (Method is work & takes no arguments)
        Method method = clss.getMethod("work", null);
        method.invoke(test, null);
    }

【问题讨论】:

  • URL.toURI().toURL() 的意义何在?
  • 帮不了你。我们不知道_sUserHashClassName 是什么。或者_sHashFunctionFilePath
  • @blm 这是 OP 已经发布的链接。
  • 抱歉,它是为 file.toURI().toURL() 设计的,我误以为直接使用 toURL() 是一种已弃用的方式。

标签: java urlclassloader dynamic-class-loaders


【解决方案1】:

/home/shackle/somedir/classes/pkg 目录中,我有一个文件 Test.class 从带有package pkg; 的 java 文件创建,例如:

package pkg;

public class Test {

    public String toString() {
        return "secret_string";
    }
}

然后我加载它:

System.out.println(new URLClassLoader(
        new URL[]{new File("/home/shackle/somedir/classes").toURI().toURL()}
).loadClass("pkg.Test").newInstance().toString());

请注意,我没有将 pkg/Test 放在 URL 字符串中,但加载类参数有 pkg​​。前缀。

您可以像这样直接从文件中获取类名:

Class clsReaderClss = ClassLoader.getSystemClassLoader().loadClass("jdk.internal.org.objectweb.asm.ClassReader");
System.out.println("clsReaderClss = " + clsReaderClss);
Constructor con = clsReaderClss.getConstructor(InputStream.class);
Object reader = con.newInstance(new FileInputStream(directFile));
Method m = clsReaderClss.getMethod("getClassName");
String name = m.invoke(reader).toString().replace('/', '.');
System.out.println("name = " + name);

不需要访问内部类的替代方法。

String pathToClassFile = "/home/shackle/somedir/classes/pkg/Test.class";
ProcessBuilder pb = new ProcessBuilder("javap",pathToClassFile);
Process p = pb.start();
String classname = null;
try(BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()))) {
   String line;
   while(null != (line = br.readLine())) {
       if(line.startsWith("public class")) {
           classname = line.split(" ")[2];
           break;
       }
   }
}
System.out.println("classname = " + classname);

然后可以加载类:

String pathToPackageBase = pathToClassFile.substring(0, pathToClassFile.length() - (classname + ".class").length());
System.out.println("pathToPackagBase = " + pathToPackageBase);
Class clss = new URLClassLoader(
        new URL[]{new File(pathToPackageBase).toURI().toURL()}
).loadClass(classname);
System.out.println(clss.newInstance().toString());

【讨论】:

  • 我理解,但这并不意味着我需要明确知道类 Test 使用的包名称。 (因为我假设我的用户提供了类文件,因此我不应该知道他/她正在使用的包名称,还是我对包的理解在某个地方被误导了?)
  • Cheers 将对此进行调查,谢谢,我可以从 2 个当前答案中看到,这还有很多其他内容。完成研究后会回复您。
  • 谢谢,但我可以检查一下这条线是做什么的 .getSystemClassLoader().loadClass("jdk....ClassReader");
  • 所以你需要的这个 JDK 是一个外部库,其中只需要“导入”的一面就可以安装为外部库吗?如果需要在其他机器上运行,我是否需要将文件添加到我的项目中以供将来编译?
  • 不需要访问内部类的替代方案,因此一旦你有了类名,你如何实例化所述类的实例并使用它的方法?
【解决方案2】:

您的_sHashFunctionFilePath 需要从中删除目标类的包名,因此ClassLoader 将查找_sHashFunctionFilePath + package.name + HashFunction.class 作为文件的路径。如果不这样做,ClassLoader 将无法找到该文件。

所以如果目标类是HashFunction.class中的my.great.HashFunction,那么如果要使用URLClassLoader,它需要在一个名为my/great/的目录中。然后,如果 .class 文件实际上在 /path/to/my/great/HashFunction.class 中找到,您将使用 /path/to 作为 URLClassLoader 的 file:/// URL。

【讨论】:

  • 对不起,我看到很多例子都提到了数据包,也使用了类似的符号,比如你的例子“my.great.HashFunction”,恐怕我不明白这些数据包的目的。 (因为我假设我的用户提供了类文件,因此我不应该知道他/她正在使用的包名称,还是我对包的理解在某个地方被误导了?)
  • 如果你无法预测包名(package这个词在Java中有明确的含义),那么你就不能使用ClassLoader.loadClass来获取类。相反,您必须调用ClassLoader.defineClassClassLoader.resolveClass,正如您可能注意到的那样,这很棘手,因为这些方法受到保护。您还必须提前知道类的名称,因为您必须知道类的名称才能定义它。您可以使用 BCEL 之类的东西从类的原始字节中获取元数据,然后将其提供给 ClassLoader.defineClass
  • Cheers 将对此进行调查,谢谢,我可以从当前的 2 个答案中看到,这还有很多其他内容。我完成研究后会回复您
  • 最好的办法是从贡献者那里加载.jar 文件,而不是尝试直接加载.class 文件。这样,JAR 文件将包含所有必要的结构,您甚至可以扫描 JAR 文件以查找其中的类,然后……我不知道,也许可以决定要加载哪个类。
猜你喜欢
  • 1970-01-01
  • 2019-02-08
  • 2014-09-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多