Es gibt keine Möglichkeit, ClassLoader
als Referenz zu verwenden, es sei denn, es ist in der Lage, die Klassenbytes seiner definierten Klassen bereitzustellen. Das heißt, wenn Sie eine Class
Instanz haben, die eine Klasse der obersten Ebene darstellt, können Sie classInstance.getResourceAsStream(classInstance.getSimpleName()+".class")
verwenden, um zu versuchen, die Klassenbytes in die Hände zu bekommen. Wenn Sie Zugriff auf die Bytes haben, aus denen die dynamische Klasse besteht, können Sie sie dem Java-Compiler über eine -Implementierung zur Verfügung stellen.
Die Compiler-API ist Teil der Standard-API und benötigt keine Bibliotheken von Drittanbietern. Der folgende Code zeigt dies durch eine Testklasse zuerst kompiliert, dann die notwendige Umgebung Einstellung auf der Klasse eine zweite Klasse zu kompilieren, abhängig nur im vorherigen Schritt erstellt:
// customize these, if you want, null triggers default behavior
DiagnosticListener<JavaFileObject> diagnosticListener = null;
Locale locale = null;
// the first class, to be present at runtime only
String class1 = "package test;\npublic class Class1 {}";
JavaCompiler c = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fm
= c.getStandardFileManager(diagnosticListener, locale, Charset.defaultCharset());
// define where to store compiled class files - use a temporary directory
fm.setLocation(StandardLocation.CLASS_OUTPUT, Collections.singleton(
Files.createTempDirectory("compile-test").toFile()));
JavaCompiler.CompilationTask task = c.getTask(null, fm,
diagnosticListener, Collections.emptySet(), Collections.emptySet(),
Collections.singleton(new SimpleJavaFileObject(
URI.create("string:///Class1.java"), Kind.SOURCE) {
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return class1;
}
}));
if(task.call()) {
FileObject fo = fm.getJavaFileForInput(
StandardLocation.CLASS_OUTPUT, "test.Class1", Kind.CLASS);
// these are the class bytes of the first class
byte[] class1bytes = Files.readAllBytes(Paths.get(fo.toUri()));
// the actual task: define a class dependent on the first class
String class2 = "package test;\npublic class Class2 { Class1 variable; }";
// create a file object representing the dynamic class
JavaFileObject jo = new SimpleJavaFileObject(
URI.create("runtime:///test/Class1.class"), Kind.CLASS) {
@Override public InputStream openInputStream() throws IOException {
return new ByteArrayInputStream(class1bytes);
}
};
// and a custom file manager knowing how to locate that class
JavaFileManager myFM = new ForwardingJavaFileManager(fm) {
@Override
public JavaFileObject getJavaFileForInput(
JavaFileManager.Location location, String className, Kind kind)
throws IOException {
if(location==StandardLocation.CLASS_PATH&&className.equals("test.Class1")) {
return jo;
}
return super.getJavaFileForInput(location, className, kind);
}
@Override
public boolean hasLocation(JavaFileManager.Location location) {
return location==StandardLocation.CLASS_PATH || super.hasLocation(location);
}
@Override
public Iterable list(JavaFileManager.Location location,
String packageName, Set kinds, boolean recurse) throws IOException {
if(location==StandardLocation.CLASS_PATH
&& (packageName.equals("test") || recurse&&packageName.isEmpty())) {
return Collections.singleton(jo);
}
return super.list(location, packageName, kinds, recurse);
}
@Override
public String inferBinaryName(
JavaFileManager.Location location, JavaFileObject file) {
if(file==jo) return "test.Class1";
return super.inferBinaryName(location, file);
}
};
// compile the second class using the custom file manager to locate dependencies
task = c.getTask(null, myFM,
diagnosticListener, Collections.emptySet(), Collections.emptySet(),
Collections.singleton(new SimpleJavaFileObject(
URI.create("string:///Class2.java"), Kind.SOURCE) {
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return class2;
}
}));
if(task.call()) {
fo = fm.getJavaFileForInput(
StandardLocation.CLASS_OUTPUT, "test.Class2", Kind.CLASS);
// there we have the compiled second class
byte[] class2bytes = Files.readAllBytes(Paths.get(fo.toUri()));
}
}
Natürlich ist dies nur zur Demonstration des Prinzips . Sie möchten sicher Factory-Methoden für die Dateiobjekte erstellen und Map
s für das Erinnern von ihnen usw. verwenden
Es ist auch möglich, das temporäre Verzeichnis durch einen benutzerdefinierten In-Memory-Speicher zu ersetzen. Der entscheidende Punkt bleibt jedoch, dass der Compiler auf die Klassenbytes zugreifen muss. Es wird keine geladenen Laufzeitklassen verwenden.
Sie können sich die [Java Compiler API] (http://docs.oracle.com/javase/8/docs/api/javax/tools/JavaCompiler.html) ansehen, die intern von beiden verwendet wird "Compiler", die Sie vorgestellt haben. Das sind nur ein paar Codezeilen, die um Aufrufe der JavaAPI gewickelt sind (noch deutlicher sichtbar im zweiten verknüpften Repo). – Paul
Mögliche Duplikate von [Wie kompilieren und laden Sie externe Java-Klassen dynamisch?] (Https: // stackoverflow.com/questions/21544446/How-Do-Sie-dynamisch-kompilieren-und-laden-externe-Java-Klassen) – ldmtwo