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. My program compiles (with 'JavaCompiler') the .java file. Then it tries to load the .class file:
loadedClass = loadClass("customAlgorithms."+this.getClassName()+"");
When running my program in Eclipse, 开发者_StackOverflow中文版everything works fine, and students can use the program flawlessly.
Problem:
But then I export my project to an executable jar file. The compiling part still works but loading the class fails because it searches it inside the jar file.
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. Can this even work?
I know it is possible to edit a jar archive. But can this work while running and without having to restart the program?
other way to use 'loadClass()' ? So that I can load a class file which is not included in my jar file
Are there other ideas?
Java loads classes via 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. 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.
In your application, when you compile the java source file, it generates a class file, that is saved somewhere. 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.
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.
You have several ways to fix this. In order of ascending complexity :
- 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.
- When you compile the java file, create a folder and create a new instance of URLClassLoader pointing to that folder. Compile the java file to that folder. Then use that URLCLassLoader to load the compiled class.
- 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.
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..
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.
You will have to create another class loader, for example an URLClassLoader and load the class from it.
You can use a ClassLoader
defineClass method. 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.
精彩评论