Eine Klasse dynamisch generieren, kompilieren, instanziieren und benutzenMotivationIn letzter Zeit habe ich viel darüber nachgedacht, wie man die besseren Eigenschaften der Technologien JSP und Templates zu etwas Neuem und Besserem verschmelzen könnte. Die Details dazu möchte ich mir für einen zukünftigen Artikel aufsparen. Jedenfalls kam ich mit meinen Gedanken irgendwann an den Punkt, wo ich mich fragte, ob es möglich sei, aus einem laufenden Java Programm den source code fuer eine Java Klasse dynamisch erzeugen, kompilieren, eine Instanz davon erzeugen und benutzen zu können. Bei JSPs wird so etwas hinter den Kulissen nämlich auch gemacht. Ich könnte also zum Beispiel im Tomcat code nachschauen, wie das dort gemacht wird. Ich habe mich für den Anfang dann aber doch entschieden, erst mal google anzuwerfen und wurde dann auch relativ schnell fündig. Einfache DemoklasseDie folgende Klasse zeigt, wie einfach es ist so etwas zu machen. Da es mir darum geht die Technik zu demonstrieren enthält sie nur den nötigsten Code. Die Klasse exp.Main erzeugt eine Klasse exp.DynamicallyGeneratedClass. Beide source Dateien leben im Verzeichnis src. Danach kompiliert Main diese dynamisch erzeugte Quelldatei wobei das Zielverzeichnis output benutzt wird. Danach erzeugt Main eine Instanz dieser dynamisch erzeugten Klasse und ruft die Funktion doit() dieser Instanz auf. package exp; import java.io.*; import java.lang.reflect.*; /** * Create a java source dynamically, compile and call it. * * Found at: * * http://www.rgagnon.com/javadetails/java-0039.html * * See also: * * http://www.javaworld.com/javatips/jw-javatip131.html */ public class Main { String dynClassClassname = "DynamicallyGeneratedClass"; String dynClassSourceFilename = dynClassClassname + ".java"; public static void main(String args[]) throws Exception { Main main = new Main(); main.createSourceFile(); if (main.compileSourceFile()) { System.out.println("Running " + main.dynClassClassname + ":\n\n"); main.runDynClass(); } else { System.out.println(main.dynClassSourceFilename + " could not be compiled."); } } public void createSourceFile() { try { String filename = "src/exp/" + dynClassSourceFilename; File f = new File(filename); if (f.exists()) { f.delete(); } FileWriter aWriter = new FileWriter(filename, true); aWriter.write("package exp;\n"); aWriter.write("public class " + dynClassClassname + "\n"); aWriter.write("{\n"); aWriter.write("\tpublic void doit()\n"); aWriter.write("\t{\n"); aWriter.write("\t\tSystem.out.printf(\"Hello world from '\" + getClass().getName() + \"'\");\n"); aWriter.write("\t}\n"); aWriter.write("}\n"); aWriter.flush(); aWriter.close(); } catch (Exception e) { e.printStackTrace(); } } public boolean compileSourceFile() throws Exception { String[] javacCmdLineArgs = {"-d", "output", "src/exp/" + dynClassSourceFilename}; PrintWriter pw = new PrintWriter(System.out); int status = com.sun.tools.javac.Main.compile(javacCmdLineArgs, pw); System.out.println("Compilation done. Status: " + status);System.out.flush(); return status == 0; } public void runDynClass() { try { Class params[] = {}; Object paramsObj[] = {}; System.out.println("trying to load class");System.out.flush(); Class clazz = Class.forName("exp." + dynClassClassname); System.out.println("loaded");System.out.flush(); Object instance = clazz.newInstance(); Method thisMethod = clazz.getDeclaredMethod("doit", params); thisMethod.invoke(instance, paramsObj); } catch (Exception e) { e.printStackTrace(); } } } Ein paar Dinge gibt es noch zu beachten, wenn man diese Idee weitertreiben möchte:
|