Как динамически загрузить документ fxml из строки И скомпилировать / создать экземпляр его контроллера из строки

#javafx #fxml #fxmlloader #dynamic-class-loaders

#javafx #fxml #fxmlloader #загрузчики динамических классов

Вопрос:

Я хочу создать приложение javafx, в котором документ fxml и его контроллер хранятся в базе данных. Благодаря jewelsea я использую этот код для динамической загрузки документа fxml :

 String fxmlDocument = "<?xml version="1.0" encoding="UTF-8"?>n"  
                "n"  
                "<?import javafx.scene.control.Button?>n"  
                "<?import javafx.scene.layout.BorderPane?>n"  
                "n"  
                "n"  
                "<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="main.ControllerClass">n"  
                "   <center>n"  
                "      <Button fx:id="button" mnemonicParsing="false" onAction="#onButtonClicked" text="Button" BorderPane.alignment="CENTER" />n"  
                "   </center>n"  
                "</BorderPane>";

FXMLLoader loader = new FXMLLoader();
BorderPane layout = (BorderPane) loader.load(
  new ByteArrayInputStream(fxmlDocument.getBytes())
);
  

Мой контроллер был впервые сохранен в ControllerClass.java файл

 package main;

import javafx.scene.control.Button;
import javafx.event.ActionEvent;

public class ControllerClass {
    public Button button;

    public void onButtonClicked(ActionEvent event){
        System.out.println("Button's clicked");
    }
}
  

и это сработало нормально.

но теперь мой контроллер хранится в строке, поэтому я использовал эту ссылку [в памяти]:https://github.com/trung/InMemoryJavaCompiler чтобы скомпилировать и создать экземпляр моего контроллера следующим образом:

         StringBuilder fxmlController = new StringBuilder();

        fxmlController.append("package main;n");
        fxmlController.append("import javafx.scene.control.Button;n");
        fxmlController.append("import javafx.event.ActionEvent;n");
        fxmlController.append("import java.util.ArrayList;n");
        fxmlController.append("import java.util.List;n");
        fxmlController.append("public class ControllerClass {n");
        fxmlController.append("   public Button button;n");
        fxmlController.append("   public void onButtonClicked(ActionEvent event){n");
        fxmlController.append("     System.out.println("ControllerClass called");");
        fxmlController.append("   }");
        fxmlController.append("   public List<String> test() {");
        fxmlController.append("     List<String> supplierNames = new ArrayList<String>();");
        fxmlController.append("     supplierNames.add("sup1");");
        fxmlController.append("     supplierNames.add("sup2");");
        fxmlController.append("     supplierNames.add("sup3");");
        fxmlController.append("     supplierNames.add("sup4");");
        fxmlController.append("     System.out.println(supplierNames.get(3));");
        fxmlController.append("     return supplierNames;");
        fxmlController.append("   }");
        fxmlController.append("}");
        Class<?> controllerClass = InMemoryJavaCompiler.newInstance().ignoreWarnings().compile("main.ControllerClass", fxmlController.toString());
        List<?> listSize = (List<?>) controllerClass.getMethod("test").invoke(controllerClass.newInstance());
        System.out.println("listSize.size()="   listSize.size());
  

Когда я запускаю код, результатом является:

 sup4
res.size()=4
  

Это означает, что контроллер правильно скомпилирован и создан, НО если я помещу документ fxml и контроллер, динамически скомпилированный / созданный / загруженный вместе, я получу ошибку

 Exception in Application start method
java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:389)
    at com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:328)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:767)
Caused by: java.lang.RuntimeException: Exception in Application start method
    at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:917)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$154(LauncherImpl.java:182)
    at java.lang.Thread.run(Thread.java:748)
Caused by: javafx.fxml.LoadException: 
unknown path:7

    at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2601)
    at javafx.fxml.FXMLLoader.access$700(FXMLLoader.java:103)
    at javafx.fxml.FXMLLoader$ValueElement.processAttribute(FXMLLoader.java:922)
    at javafx.fxml.FXMLLoader$InstanceDeclarationElement.processAttribute(FXMLLoader.java:971)
    at javafx.fxml.FXMLLoader$Element.processStartElement(FXMLLoader.java:220)
    at javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:744)
    at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2707)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2527)
    at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2425)
    at main.Main.start(Main.java:111)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$161(LauncherImpl.java:863)
    at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$174(PlatformImpl.java:326)
    at com.sun.javafx.application.PlatformImpl.lambda$null$172(PlatformImpl.java:295)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$173(PlatformImpl.java:294)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$null$147(WinApplication.java:177)
    ... 1 more
Caused by: java.lang.ClassNotFoundException: main.ControllerClass
    at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at javafx.fxml.FXMLLoader$ValueElement.processAttribute(FXMLLoader.java:920)
    ... 16 more
Exception running application main.Main

Process finished with exit code 1
  

Проблема в том, :

 Caused by: javafx.fxml.LoadException: 
unknown path:7
  

Я сделал что-то не так, поскольку мой контроллер правильно скомпилирован / создан, но он не настроен загрузчиком FXML?
Не могли бы вы помочь, пожалуйста?
Заранее спасибо.

Комментарии:

1. Примечание: Используйте StringBuilder вместо StringBuffer , если вы выполняете операции записи из нескольких потоков. Также в этом случае объединение всех строк, используемых в одном выражении (например, String s = a b ...; ), на самом деле было бы более производительным, поскольку объединение строк может быть выполнено во время компиляции.

2. @fabian : Спасибо за совет. Я изменю StringBuffer на StringBuilder.