Wie kompilieren und laden Sie dynamisch externe Java-Klassen? [duplicate]

Lesezeit: 6 Minuten

Wie kompilieren und laden Sie dynamisch externe Java Klassen duplicate
Schattentrott

(Diese Frage ähnelt vielen Fragen, die ich gesehen habe, aber die meisten sind nicht spezifisch genug für das, was ich tue.)

Hintergrund:

Der Zweck meines Programms besteht darin, es den Benutzern meines Programms zu erleichtern, sozusagen benutzerdefinierte “Plugins” zu erstellen, diese dann zu kompilieren und zur Verwendung in das Programm zu laden (im Gegensatz zu einem unvollständigen, langsamen Parser, der in meinem Programm implementiert ist). Mein Programm ermöglicht es Benutzern, Code in eine vordefinierte Klasse einzugeben, die eine kompilierte Klasse erweitert, die mit meinem Programm gepackt ist. Sie geben den Code in Textfelder ein, dann kopiert mein Programm den Code in die zu überschreibenden Methoden. Es speichert dies dann als .java-Datei (fast) bereit für den Compiler. Das Programm führt javac (Java-Compiler) mit der gespeicherten .java-Datei als Eingabe aus.

Meine Frage ist, wie bekomme ich es hin, dass der Client (mit meinem kompilierten Programm) diese Java-Datei (die mein InterfaceExample erweitert) irgendwo auf seinem Computer speichern kann, mein Programm sie kompilieren lässt (ohne zu sagen “Symbol kann nicht gefunden werden: InterfaceExample” ) dann laden und die Methode doSomething() aufrufen?

Ich sehe immer wieder Fragen und Antworten, die Reflektion oder ClassLoader verwenden, und eine, die fast beschrieben hat, wie man es kompiliert, aber keine ist detailliert genug für mich / ich verstehe sie nicht vollständig.

  • Schau dir JANINO an. Ich verwende es, um zur Laufzeit Klassen aus Java-Quellen zu erstellen. Ich ziehe es vor, sie direkt in den Speicher und in einen Classloader kompilieren zu lassen. Sie müssen nicht einmal die .class-Dateien speichern. docs.codehaus.org/display/JANINO/Home

    – rutschfest

    4. Februar 2014 um 5:23 Uhr

  • Ich würde das verwenden, aber die Art und Weise, wie mein Programm funktioniert und direkt in den Speicher geladen wird, entspricht möglicherweise nicht den Wünschen der Benutzer, und die kompilierten Klassen müssen leicht zu finden und von Benutzern gemeinsam genutzt werden. Dies könnte jedoch für einige Leute eine nützliche Antwort sein.

    – Schattentrott

    8. Februar 2014 um 23:45 Uhr


  • kühl. Janino kann auch in Dateien ausgeben, aber wenn Sie das wollen, empfehle ich den JavaCompiler gemäß der akzeptierten Antwort.

    – rutschfest

    9. Februar 2014 um 8:10 Uhr

  • medium.com/@davutgrbz/the-need-history-c91c9d38ec9

    – Davut Gürbüz

    10. Mai 2020 um 11:27 Uhr

Wie kompilieren und laden Sie dynamisch externe Java Klassen duplicate
Verrückter Programmierer

Schauen Sie sich an JavaCompiler

Das Folgende basiert auf dem Beispiel in den JavaDocs

Das spart ein File in dem testcompile Verzeichnis (basierend auf der package Namensanforderungen) und das Kompilieren der File zu einer Java-Klasse …

package inlinecompiler;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

public class InlineCompiler {

    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder(64);
        sb.append("package testcompile;\n");
        sb.append("public class HelloWorld implements inlinecompiler.InlineCompiler.DoStuff {\n");
        sb.append("    public void doStuff() {\n");
        sb.append("        System.out.println(\"Hello world\");\n");
        sb.append("    }\n");
        sb.append("}\n");

        File helloWorldJava = new File("testcompile/HelloWorld.java");
        if (helloWorldJava.getParentFile().exists() || helloWorldJava.getParentFile().mkdirs()) {

            try {
                Writer writer = null;
                try {
                    writer = new FileWriter(helloWorldJava);
                    writer.write(sb.toString());
                    writer.flush();
                } finally {
                    try {
                        writer.close();
                    } catch (Exception e) {
                    }
                }

                /** Compilation Requirements *********************************************************************************************/
                DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
                JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
                StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);

                // This sets up the class path that the compiler will use.
                // I've added the .jar file that contains the DoStuff interface within in it...
                List<String> optionList = new ArrayList<String>();
                optionList.add("-classpath");
                optionList.add(System.getProperty("java.class.path") + File.pathSeparator + "dist/InlineCompiler.jar");

                Iterable<? extends JavaFileObject> compilationUnit
                        = fileManager.getJavaFileObjectsFromFiles(Arrays.asList(helloWorldJava));
                JavaCompiler.CompilationTask task = compiler.getTask(
                    null, 
                    fileManager, 
                    diagnostics, 
                    optionList, 
                    null, 
                    compilationUnit);
                /********************************************************************************************* Compilation Requirements **/
                if (task.call()) {
                    /** Load and execute *************************************************************************************************/
                    System.out.println("Yipe");
                    // Create a new custom class loader, pointing to the directory that contains the compiled
                    // classes, this should point to the top of the package structure!
                    URLClassLoader classLoader = new URLClassLoader(new URL[]{new File("./").toURI().toURL()});
                    // Load the class from the classloader by name....
                    Class<?> loadedClass = classLoader.loadClass("testcompile.HelloWorld");
                    // Create a new instance...
                    Object obj = loadedClass.newInstance();
                    // Santity check
                    if (obj instanceof DoStuff) {
                        // Cast to the DoStuff interface
                        DoStuff stuffToDo = (DoStuff)obj;
                        // Run it baby
                        stuffToDo.doStuff();
                    }
                    /************************************************************************************************* Load and execute **/
                } else {
                    for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) {
                        System.out.format("Error on line %d in %s%n",
                                diagnostic.getLineNumber(),
                                diagnostic.getSource().toUri());
                    }
                }
                fileManager.close();
            } catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException exp) {
                exp.printStackTrace();
            }
        }
    }

    public static interface DoStuff {

        public void doStuff();
    }

}

Jetzt aktualisiert, um einen Klassenpfad für den Compiler bereitzustellen und die kompilierte Klasse zu laden und auszuführen!

  • Ich habe mich gefragt, was die Sicherheitsprobleme sein können, wenn wir Benutzereingabecode kompilieren und wie wir unser System davor schützen können. Danke

    – c4k

    2. Januar 2017 um 15:12 Uhr

  • @c4k Da der Benutzer Zugriff auf die gesamte API hat, kann er so ziemlich tun, was er will, ohne so weit zu gehen, dass Sie eine eigene Bibliotheksimplementierung haben. Sie können den Benutzer in einer Sandbox ausführen und / oder den Code als nicht privilegierten Benutzer ausführen

    – Verrückter Programmierer

    2. Januar 2017 um 20:03 Uhr

  • @MadProgrammer danke für die Erklärung. Um weiter zu gehen, habe ich diesen Absatz auf Janinos Website gefunden janino-compiler.github.io/janino/#security. Es könnte sich lohnen, es zu lesen, bevor Sie tiefer darauf eingehen 😉

    – c4k

    3. Januar 2017 um 11:15 Uhr

Wie kompilieren und laden Sie dynamisch externe Java Klassen duplicate
Peter Lawrey

Ich schlage vor, die zu verwenden Java-Laufzeit-Compiler Bücherei. Sie können ihm einen String im Speicher geben und er wird die Klasse kompilieren und in den aktuellen Klassenlader (oder einen Ihrer Wahl) laden und die geladene Klasse zurückgeben. Auch verschachtelte Klassen werden geladen. Hinweis: Dies funktioniert standardmäßig vollständig im Speicher.

z.B

 // dynamically you can call
 String className = "mypackage.MyClass";
 String javaCode = "package mypackage;\n" +
                  "public class MyClass implements Runnable {\n" +
                  "    public void run() {\n" +
                  "        System.out.println(\"Hello World\");\n" +
                  "    }\n" +
                  "}\n";
 Class aClass = CompilerUtils.CACHED_COMPILER.loadFromJava(className, javaCode);
 Runnable runner = (Runnable) aClass.newInstance();
 runner.run();

  • Was ist, wenn MyClass eine Schnittstelle implementiert, die bereits zur Laufzeit vorhanden ist? A kann es in der Spring Boot-Anwendung nicht zum Laufen bringen!

    – CHEM_Eugen

    28. Januar 2020 um 8:33 Uhr

  • @CHEM_Eugene Sie können eine Klasse, die bereits auf diese Weise geladen wurde, nicht ändern. Sie müssen es zu einem neuen ClassLoader hinzufügen.

    – Peter Lawrey

    13. Februar 2020 um 18:43 Uhr

923050cookie-checkWie kompilieren und laden Sie dynamisch externe Java-Klassen? [duplicate]

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy