Java 使用URLClassLoader加载.class文件

Java 使用URLClassLoader加载.class文件,java,urlclassloader,dynamic-class-loaders,Java,Urlclassloader,Dynamic Class Loaders,我知道以前有人问过这个问题: 然而,由于缺乏示例,我并不真正理解。我目前正在处理一个项目,并尝试加载用户给定的.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 URLClassL

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

然而,由于缺乏示例,我并不真正理解。我目前正在处理一个项目,并尝试加载用户给定的.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,来自我的实验&理解用户给定的类函数必须在类路径中

注:第一次发帖时,如果我没有按照适当的方式提问,请随时纠正我

//解决方案

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

//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);
    }

在目录
/home/shackle/somedir/classes/pkg
中,我有一个文件Test.class,它是用
包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字符串中,但load类参数具有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());

您的
\u sHashFunctionFilePath
需要从中删除目标类的包名,因此类加载器将在
\u sHashFunctionFilePath
+
包中查找文件的路径。如果不这样做,类加载器将无法找到该文件


因此,如果目标类是
HashFunction.class
中的
my.great.HashFunction
,那么如果要使用URLClassLoader,它需要位于名为
my/great/
的目录中。然后,如果在
/path/to/my/great/HashFunction.class
URL.toURI().toul()中实际找到了.class文件,则可以使用
/path/to
作为URLClassLoader的
文件://
URL.toURI().toul()
的作用是什么。我们不知道什么是
\u sUserHashClassName
。或者
\u sHashFunctionFilePath
对于这个问题,可能是@blm的重复,这是已经发布的链接操作。很抱歉,它是针对file.toURI().toul()的,我混淆了它是toul()的想法straight是一种不推荐的方式。我理解,但这不意味着我需要明确了解类测试使用的包名吗。(因为我假设我的用户提供了类文件,所以我不应该知道他/她正在使用的包名,或者我对包的理解在什么地方被误导了?)Cheers将对此进行调查,谢谢,我可以从当前的两个答案中看出,这方面还有很多,然后再看出来。一旦我完成了我的研究,我会给你回复的。谢谢,但是我能检查一下这行代码是做什么的吗。getSystemClassLoader().loadClass(“jdk…ClassReader”);那么您认为这个JDK required是一个外部库,其中只需要“导入”一方的东西就可以作为外部库安装?如果需要在其他机器上运行,我是否需要在我的项目中添加该文件以供将来编译?另一种选择是不需要访问内部类,因此,一旦你有了类名,你如何实例化所述类的一个实例并使用它的方法?很抱歉,我看到很多例子提到了包,也使用了类似的符号,比如你的例子“my.great.HashFunction”,我恐怕不理解这些包的用途。(因为我假设我的用户提供了类文件,所以我不应该知道他/她正在使用的包名,或者我对包的理解在什么地方被误导了?),那么就不能使用
ClassLoader.loadClass
来获取类。相反,您必须调用
ClassLoader.defineClass
ClassLoader.resolveClass
,正如您可能注意到的那样,这是很棘手的,因为这些方法受到保护。您还必须事先知道类的名称,因为您必须知道类的名称才能定义它。您可以使用BCEL之类的工具从类的原始字节中获取元数据,然后将其提供给
类加载器。defineClass
。谢谢,我可以从当前的两个答案中看出,这方面的内容远远不止这些。一旦我完成了我的研究,我会给你回复。你最好的选择是从贡献者那里加载
.jar
文件,而不是直接加载
.class
文件。这样,JAR文件将包含所有必要的结构,您甚至可以扫描JAR文件中的类,然后。。。我不知道,也许可以决定加载哪个类。