Let’s suppose you have some string containing a source code for a class, something like “public class Test {}”. You want to compile it dynamically in memory and get an instance of that class. How can you accomplish that? We will try to present here a way to do it. We will use the javax.tools package that was added in Java 6, so be sure that you are using Java 6 or later to run the codes.
Although we will try to show the dynamic compilation it in the simplest way possible, we will still need to create four classes. You can get the full version of all the classes in a zipped archive here.
First let’s see the main method to get the feeling of what is going on:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: | |
As you see the code we want to compile is stored in the variable src. After we define it, we print it to the console, get an instance of the compiler, put the source code into an object representing a source file, create a file manager and a compilation task. The real compilation starts when we call the call() method of the compilation task. Then we get the Class representing our compiled class from the file manager, instantiate our class and print it to the console, using the toString() function that we implemented in the code.
There are three classes used in the code that are not available in the JDK and hence we have to implement them by ourselves – CharSequenceJavaFileObject,JavaClassObject and ClassFileManger. Let’s see them one by one to understand what they are doing.
CharSequenceJavaFileObject implements the SimpleJavaFileObject interface and represents the source code we want to compile. Normally instances ofSimpleJavaFileObject would point to a real file in the file system, but in our case we want it to represent a StringBuilder createdy by us dynamically. Let’s see how it goes:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: | |
It stores an object of type CharSequence which is an interface implemented byStringBuilder. As you see in the code of our main function, we create an instance of the CharSequenceJavaFile providing it with src variable, which is of typeStringBuilder. The constructor stores it in the content private variable. It will be used when the compiler calls the getCharContent() method to get the source code to compile.
Next we must define the class representing the output of the compilation – compiled byte code. It is needed by the ClassFileManager which we will describe later. Compiler takes the source code, compiles it and splits out a sequence of bytes which must be stored somewhere. Normally they would be stored in a .class file but in our case we just want to make a byte array out of it. Here is a class that fulfills our needs:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: | |
At some point of the compilation, compiler will call openOutputStream() method of our JavaClassObject class and write there the compiled byte code. Because theopenOutputStream() method returns a reference to the bos variable, everything will be written there, so that afterwards we will be able to get the byte code from it.
We will also need something like a “file manager” that will tell the compiler to put the compiled byte code into an instance of our JavaClassObject class instead of putting it to a file. Here it is:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: | |
Function getClassLoader() will be called by us to get a ClassLoader instance for instantiating our compiled class. It returns an instance of SecureClassLoader modified by the function findClass(), which in our case gets the compiled byte code stored in the instance of JavaClassObject, defines a class out of it with the functiondefineClass() and returns it.
Now that we have all our classes ready, lets compile them and run the program. We should get an output like this:
public class DynaClass { public String toString() { return "Hello, I am "+this.getClass().getSimpleName(); } } Hello, I am DynaClass |
As you see, the toString() method of our dynamically compiled class was invoked printing “Hello, I am DynaClass” to the screen. Nice, isn’t it?
One of the questions that appear now is – What is that all good for? Are there any real life uses of it or is it just a nice toy? Well, classes compiled in this way can contain dynamic expressions that are not known until runtime. At the same time those classes benefit from all the optimizations that the Java compiler provides. One pretty straightforward use of this is when the user types in the program an expression like “y=2*(sin(x)+4.0)” and expects to see some output of it – for example a graph. Using dynamic compilation you don’t have to parse it any more by yourself, you could just compile it and get a fast, optimized function representing this expression. You can read about it (and much more about dynamic compilation generally) here.
Some other usage is creating dynamic classes for accessing data stored in JavaBeans. Normally you would have to use reflection for it, but reflection is very slow and its generally better to avoid using it when possible. Dynamic compilation allows you to minimize the use of reflection in a library that handles JavaBeans. How? We will try to show it in one of our next posts, so stay tuned!