【问题标题】:How to load a java class outside the classpath?如何在类路径之外加载java类?
【发布时间】:2021-06-18 03:04:54
【问题描述】:

我有一个程序,我希望用户能够从文件系统中选择一个 .java 类文件,然后将该类加载到程序中。

我正在使用 JFileChooser 来允许用户选择文件。然后,我尝试将该文件转换为 URL,并使用 URLClassLoader 加载类(如 these answers 所建议的那样)。

问题是,当我想使用loadClass() 方法时,我不知道该类的“完整类名”(例如java.lang.String)。所以,我不知道如何使这种方法起作用。有没有办法得到这个类名?还是有其他方法可以做到这一点?

这是我的代码示例:

// Open the file chooser
JFileChooser fileChooser = new JFileChooser();
fileChooser.showOpenDialog(null);
File obtainedFile = fileChooser.getSelectedFile();

// Create the class loader from the file
URL classPath = obtainedFile.toURI().toURL();
URLClassLoader loader = new URLClassLoader(new URL[] {classPath});

// Get the class from the loader
Class<?> theClassIWant = loader.loadClass("the file name");    // What do I put here??

【问题讨论】:

  • 问题,为什么需要加载文件?它是某种插件系统吗?许多其他应用程序管理它的方式是它们强制每个插件具有特定的结构或包含在 jar 中的元数据文件,其中包含有关 jar 的信息,例如类名和主应用程序需要了解的任何特定功能(请记住,jar 文件只是一个存档文件)。
  • 您遇到了循环问题。不加载类不能知道类名,不知道类名就不能加载类。您必须从用户那里获取包和类名信息。
  • 您没有命名全名这一事实不仅对于loadClass 的参数来说是一个问题,而且对于为classPath 选择正确的父目录也是一个问题。见Initial package of class from another directory...如果要从文件中获取类名,this answer提取限定名,但是,如你所见,并不简单。

标签: java reflection classpath classloader


【解决方案1】:

加载单个类文件通常是完全没用的。所述类文件并不孤单;它有更多相关的类文件。即使您认为“不,只有一个源文件,不用担心这个”,请注意单个 java 文件可以轻松生成多个类文件。

因此,有两种选择:

不要加载类文件。加载 jar 文件。

使用通常的机制(META-INF/servicesMETA-INF/MANIFEST.MF)在其中放置某种类名,以便您知道要加载什么。然后使用提供的 jar 创建一个新的类加载器,加载清单,找出主类,加载并运行它。

尝试确定已加载类文件的“根”并将其包含在类路径中。

这相当困难 - 问题是,要“加载”一个类文件,您需要在加载之前告诉加载器该类的完全限定名称是什么。。但是你怎么知道全名呢?您可以从文件中推测出类名(并非总是如此,但通常如此),但包是一个更困难的问题。

您可以自己将类文件作为二进制流打开,并编写一个基本的类文件格式解析器来获取完全限定的类名。对于有经验的 java 程序员来说很容易。对于刚接触 java 的人来说相当棘手(如果你认为这是一个好主意,我猜你就是这样)。

您也可以使用现有工具来执行此操作,例如 bytebuddy 或 asm。

最后,您可以尝试一种意大利面条式的方法:继续向上移动目录,直到它起作用为止。如果发生异常,您知道它不起作用。

例如,要加载C:\MyDir\Whatever\com\foo\MyApp.class,您首先尝试使用root dir C:\MyDir\Whatever\com\foo 创建一个新的类加载器(参见URLClassLoader 的API,它是核心java 的一部分),然后您要求它加载类MyApp.

如果这行得通,那就太好了(但通常尝试加载无包类只是一个初学者,你不应该这样做,CL API 可能不支持它,故意的,没有修复那个)。

如果没有,请尝试C:\MyDir\Whatever\com,并加载类foo.MyApp。如果这不起作用,请尝试C:\MyDir\Whatever 并加载类com.foo.MyApp,依此类推。

相当大的优势是,如果有另一个班级坐在MyApp.class 旁边,而 MyApp 需要它,这将正常工作。

你需要编写一个while循环(使用Paths.getp.getParent()遍历路径结构),捕获正确的异常,将路径操作到类名中(使用.replace+),当然,创建一个类加载器(URLClassLoader),用它加载类(调用loadClass),如果你打算运行它,比如thatClass.getConstructor().newInstance()然后thatClass.getMethod("someMethod", String.class, /* all the other args here */).invoke(theInstanceYouJustMade, "param1", /*all other params */)来实际“运行”它,更多内容请参见 java.lang.reflect 包。

【讨论】:

    猜你喜欢
    • 2014-08-08
    • 2015-09-10
    • 2021-04-21
    • 1970-01-01
    • 2015-07-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-01
    相关资源
    最近更新 更多