简体   繁体   English

如何加载与当前类不在同一个jar存档中的类?

[英]How can I load a class which is not in the same jar archive as the current class?

Situation: 情况:

I'm making a simulator for plotting the time complexity of algorithms. 我正在制作一个用于绘制算法时间复杂度的模拟器。 The students can add load their own .java file to run it. 学生可以添加自己的.java文件来运行它。 My program compiles (with 'JavaCompiler') the .java file. 我的程序编译(使用“ JavaCompiler”)。java文件。 Then it tries to load the .class file: 然后,它尝试加载.class文件:

loadedClass = loadClass("customAlgorithms."+this.getClassName()+"");

When running my program in Eclipse, everything works fine, and students can use the program flawlessly. 在Eclipse中运行我的程序时,一切正常,学生可以完美地使用该程序。

Problem: 问题:

But then I export my project to an executable jar file. 但是随后我将项目导出到可执行的jar文件。 The compiling part still works but loading the class fails because it searches it inside the jar file. 在编译部分仍然有效,但因为它搜索它的jar文件加载类失败。

I was wonderding why I couldn't just do this: (change . with /) 我想知道为什么我不能只这样做:(用/更改。)

loadedClass = loadClass("customAlgorithms/"+this.getClassName()+"");

What options are possible? 有哪些选择? Here what I can think of: 在这里我能想到的是:

  • adding the compiled .class file to the currently running .jar file. 将已编译的.class文件添加到当前正在运行的.jar文件中。 Can this even work? 这可以工作吗?

    I know it is possible to edit a jar archive. 我知道可以编辑jar档案。 But can this work while running and without having to restart the program? 但是在运行时无需重新启动程序就可以工作吗?

  • other way to use 'loadClass()' ? 其他使用'loadClass()'的方法? So that I can load a class file which is not included in my jar file 这样我就可以加载我的jar文件中不包含的类文件

Are there other ideas? 还有其他想法吗?

Java loads classes via ClassLoaders. Java通过ClassLoaders加载类。 When you start a JVM, you have to tell the JVM its "classpath", that is a list of folders or jar files where classes are. 启动JVM时,必须告诉JVM其“类路径”,即类所在的文件夹或jar文件的列表。 The JVM will create a ClassLoader (usually a subclass of URLClassLoader) and give this ClassLoader the list of folders and jar files, so that the classloader will be able to load the class. JVM将创建一个ClassLoader(通常是URLClassLoader的子类),并为该ClassLoader提供文件夹和jar文件的列表,以便该类加载器能够加载该类。

In your application, when you compile the java source file, it generates a class file, that is saved somewhere. 在您的应用程序中,当您编译Java源文件时,它将生成一个类文件,该文件保存在某处。 Probably in eclipse, the folder where the class file is saved is a folder included in the classpath, so that the JVM will be able to find the class. 可能在eclipse中,保存类文件的文件夹是classpath中包含的文件夹,以便JVM能够找到该类。

When you export the project to a running jar file, probably the JVM will be started having only that JAR file in its classpath, so that it does not search anywhere else for your compiled class. 当您将项目导出到正在运行的jar文件时,可能会启动JVM,并且其类路径中只有该JAR文件,这样它就不会在其他任何地方搜索您的已编译类。

You have several ways to fix this. 您有几种方法可以解决此问题。 In order of ascending complexity : 为了提高复杂度:

  1. Start your jar not as an executable jar, but using a .bat/.sh or any other java runner that makes it possible to configure the classpath, add a specific folder to the classpath, compile the java source files to that folder. 启动您的jar而不是将其作为可执行jar,而是使用.bat / .sh或任何其他可以配置类路径的Java运行程序,将特定的文件夹添加到类路径,然后将Java源文件编译到该文件夹​​中,以启动jar。
  2. When you compile the java file, create a folder and create a new instance of URLClassLoader pointing to that folder. 编译Java文件时,创建一个文件夹并创建指向该文件夹的URLClassLoader的新实例。 Compile the java file to that folder. 将Java文件编译到该文件夹​​。 Then use that URLCLassLoader to load the compiled class. 然后使用该URLCLassLoader加载已编译的类。
  3. Compile the java source to RAM, to a byte array for example, and implement a custom classloader that will return that byte array instead of loading from the disk. 将Java源代码编译为RAM,例如编译为字节数组,并实现一个自定义类加载器,该类加载器将返回该字节数组,而不是从磁盘加载。

Solution 1 is very simple, but has its drawbacks : it will not be possible to reload the compiled java file if not running the entire program again (once a class is loaded in a classloader, it cannot be loaded again, except using agents which are outside the scope of this question), it requires the program to write to disk which means permissions etc.. 解决方案1非常简单,但有其缺点:如果不再次运行整个程序,将无法重新加载已编译的Java文件(一旦在类加载器中加载了一个类,就不能再次加载它,除非使用超出此问题的范围),则需要程序将其写入磁盘,这意味着权限等。

Solution 2 and its evolution which is RAM only are much more elegant, but much more complicated : loading a single class from a classloader different thatn the one used by the rest of your entire application is tricky, you could get weird class exceptions (like ClassCastExceptions, ClassNotFoundException etc..) due to a mismatch between classloaders. 解决方案2及其演进(仅是RAM)要优雅得多,但要复杂得多:从不同于整个应用程序其余部分使用的类加载器加载类,这很棘手,您可能会得到奇怪的类异常(例如ClassCastExceptions ,ClassNotFoundException等。)由于类加载器之间的不匹配。

You can use a ClassLoader defineClass method . 您可以使用ClassLoader defineClass方法 It is protected, but it should be no trouble. 它受到保护,但是应该没有问题。

If the fact that the method is protected is a problem, probably somewhere in the internet you can define a custom subclass with that functionality freely available. 如果方法受到保护的事实是一个问题,那么可能是在Internet上的某个地方,您可以定义一个自定义子类,该子类可以免费使用。

您将必须创建另一个类加载器,例如URLClassLoader并从中加载类。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM