JavaFX + Spring4: fxml findet Controller nicht mehr

robinab

Mitglied
Hallo!
Ich habe (meine einwandfrei laufende JavaFX-Anwendung) umgestellt auf Spring4.
Jetzt läuft sie immer noch einwandfrei, bis zur Stelle, an der ich eine nested FXML habe.

Ich muss in dieser FXML nämlich per fx:controller="..." einen Controller angeben (programmatisch kann ich ja dem FXMLLoader ja nur den Controller meiner Main-FXML übergeben).

Und hier liegt der Hase im Pfeffer:
Vorher hatte das gut funktioniert - die per fx:controller="..." übergebenen Controller wurden gefunden. Seit ich auf Spring4 umgestellt habe, nicht mehr.

Ich habe alles schon probiert: Alles umstrukturiert, die Files (FXML und Controller) in ein Verzeichnis. Den Pfad verlängert, verkürzt, absolut, relativ - nix funktioniert.

Ich bin echt verzweifelt :confused: und wäre sehr dankbar, wenn mir jemand helfen könnte.
Danke + Grüße
Andrea.
 

looparda

Top Contributor
Ohne code kann man da jetzt nicht viel sagen. Vermutlich bekommst du eine "No such bean" - Exception (genaue Bezeichnung habe ich jetzt nicht im Kopf).
Aber den Stacktrace könntest du doch wenigstens mitteilen.
Sind deine Controller dem Dependency Injector bekannt?

Im nächsten Schritt würde ich an deiner Stelle ein Mini-Projekt stricken, welches das Problem reproduziert. Dann kann man da einfacher drüber sprechen und ausprobieren.
 

robinab

Mitglied
Vielen Dank!
Die FXML lade ich mit dieser Methode:
Java:
    private AnchorPane loadIntoPane(String fxml, AnchorPane controller) {
        AnchorPane pane = null;

        FXMLLoader loader = new FXMLLoader();
        URL url = getResourceURL(fxml);

        loader.setControllerFactory(application.getSpringContext()::getBean);
        loader.setBuilderFactory(new JavaFXBuilderFactory());
        loader.setLocation(url);
        loader.setController(controller);

        try {
            pane = (AnchorPane) loader.load();
        } catch (IOException e) {
            JavaFXMLTemplateHelperImpl.handleApplicationException("Could not load " + fxml, e);
        }

        return pane;
    }
Wie du siehst, setze ich hier gleich den Controller, was auch funktioniert.
Innerhalb der FXML habe ich aber eine "nested FXML":
Code:
...
<fx:include fx:id="embeddedTableView" source="ApplicationSceneTableView.fxml" />
...
Wie mache ich hier den dazugehörigen Controller bekannt?
Das geht doch nur innerhalb der eingebetteten FXML. Und hier bleibt er hängen:
Code:
...
<AnchorPane id="AnchorPane" onKeyPressed="#onKeyPressed" prefHeight="619.0" prefWidth="902.0" styleClass="background" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.controller.ApplicationSceneTableViewController">
...
Fehlermeldung:
"Could not load ApplicationScene.fxml.
ApplicationSceneTableView.fxml:16
ApplicationScene.fxml:32"

Wobei ApplicationScene.fxml diejenige FXML ist, die samt Controller erfolgreich geladen werden kann bis Zeile 32, in der die eingebettete FXML ApplicationSceneTableView.fxml aufgerufen wird. Dort wird in Zeile 16 der Controller bekannt gemacht (siehe oben), der aber nicht gefunden wird. Und ja, der ist dem Dependency Injector per @Component Annotation bekannt.
 

robinab

Mitglied
Code:
2018-04-17 13:00:20.594  WARN 14804 --- [lication Thread] javafx.css                               : Please report java.lang.NullPointerException at:
2018-04-17 13:01:31.372 ERROR 14804 --- [lication Thread] c.g.a.j.g.h.JavaFXMLTemplateHelperImpl   : com.gfk.atr.javafxmltemplate.JavaFXMLTemplateApp

javafx.fxml.LoadException:
/C:/Daten/Workspace_JavaFXMLTemplate/javafxmltemplate/src/main/java/com/gfk/atr/javafxmltemplate/application/view/ApplicationSceneTableView.fxml:16
/C:/Daten/Workspace_JavaFXMLTemplate/javafxmltemplate/src/main/java/com/gfk/atr/javafxmltemplate/application/view/ApplicationScene.fxml:32

    at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2601) ~[jfxrt.jar:na]
    at javafx.fxml.FXMLLoader.access$700(FXMLLoader.java:103) ~[jfxrt.jar:na]
    at javafx.fxml.FXMLLoader$ValueElement.processAttribute(FXMLLoader.java:922) ~[jfxrt.jar:na]
    at javafx.fxml.FXMLLoader$InstanceDeclarationElement.processAttribute(FXMLLoader.java:971) ~[jfxrt.jar:na]
    at javafx.fxml.FXMLLoader$Element.processStartElement(FXMLLoader.java:220) ~[jfxrt.jar:na]
    at javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:744) ~[jfxrt.jar:na]
    at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2707) ~[jfxrt.jar:na]
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2527) ~[jfxrt.jar:na]
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2441) ~[jfxrt.jar:na]
    at javafx.fxml.FXMLLoader.access$2700(FXMLLoader.java:103) ~[jfxrt.jar:na]
    at javafx.fxml.FXMLLoader$IncludeElement.constructValue(FXMLLoader.java:1143) ~[jfxrt.jar:na]
    at javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:746) ~[jfxrt.jar:na]
    at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2707) ~[jfxrt.jar:na]
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2527) ~[jfxrt.jar:na]
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2441) ~[jfxrt.jar:na]
    at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2409) ~[jfxrt.jar:na]
    at com.gfk.atr.javafxmltemplate.gfkui.helper.JavaFXMLTemplateHelperImpl.loadIntoPane(JavaFXMLTemplateHelperImpl.java:101) [classes/:na]
    at com.gfk.atr.javafxmltemplate.gfkui.helper.JavaFXMLTemplateHelperImpl.replaceSceneContent(JavaFXMLTemplateHelperImpl.java:67) [classes/:na]
    at com.gfk.atr.javafxmltemplate.gfkui.helper.JavaFXMLTemplateOpenHelperImpl.openApplicationScene(JavaFXMLTemplateOpenHelperImpl.java:90) [classes/:na]
    at com.gfk.atr.javafxmltemplate.login.controller.LoginSceneController.processLogin(LoginSceneController.java:187) [classes/:na]
    at com.gfk.atr.javafxmltemplate.login.controller.LoginSceneController.escapeKey(LoginSceneController.java:162) [classes/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_152]
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8.0_152]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8.0_152]
    at java.lang.reflect.Method.invoke(Unknown Source) ~[na:1.8.0_152]
    at sun.reflect.misc.Trampoline.invoke(Unknown Source) [na:1.8.0_152]
    at sun.reflect.GeneratedMethodAccessor7.invoke(Unknown Source) ~[na:na]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8.0_152]
    at java.lang.reflect.Method.invoke(Unknown Source) ~[na:1.8.0_152]
    at sun.reflect.misc.MethodUtil.invoke(Unknown Source) [na:1.8.0_152]
    at javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1769) [jfxrt.jar:na]
    at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1657) [jfxrt.jar:na]
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86) [jfxrt.jar:na]
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238) [jfxrt.jar:na]
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191) [jfxrt.jar:na]
    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59) [jfxrt.jar:na]
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58) [jfxrt.jar:na]
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) [jfxrt.jar:na]
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56) [jfxrt.jar:na]
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) [jfxrt.jar:na]
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56) [jfxrt.jar:na]
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) [jfxrt.jar:na]
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74) [jfxrt.jar:na]
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54) [jfxrt.jar:na]
    at javafx.event.Event.fireEvent(Event.java:198) [jfxrt.jar:na]
    at javafx.scene.Scene$KeyHandler.process(Scene.java:3964) [jfxrt.jar:na]
    at javafx.scene.Scene$KeyHandler.access$1800(Scene.java:3910) [jfxrt.jar:na]
    at javafx.scene.Scene.impl_processKeyEvent(Scene.java:2040) [jfxrt.jar:na]
    at javafx.scene.Scene$ScenePeerListener.keyEvent(Scene.java:2501) [jfxrt.jar:na]
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$KeyEventNotification.run(GlassViewEventHandler.java:217) [jfxrt.jar:na]
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$KeyEventNotification.run(GlassViewEventHandler.java:149) [jfxrt.jar:na]
    at java.security.AccessController.doPrivileged(Native Method) [na:1.8.0_152]
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleKeyEvent$352(GlassViewEventHandler.java:248) [jfxrt.jar:na]
    at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389) ~[jfxrt.jar:na]
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleKeyEvent(GlassViewEventHandler.java:247) [jfxrt.jar:na]
    at com.sun.glass.ui.View.handleKeyEvent(View.java:546) ~[jfxrt.jar:na]
    at com.sun.glass.ui.View.notifyKey(View.java:966) ~[jfxrt.jar:na]
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method) ~[jfxrt.jar:na]
    at com.sun.glass.ui.win.WinApplication.lambda$null$147(WinApplication.java:177) ~[jfxrt.jar:na]
    at java.lang.Thread.run(Unknown Source) ~[na:1.8.0_152]
Caused by: java.lang.ClassNotFoundException: application.controller.ApplicationSceneTableViewController
    at java.net.URLClassLoader.findClass(Unknown Source) ~[na:1.8.0_152]
    at java.lang.ClassLoader.loadClass(Unknown Source) ~[na:1.8.0_152]
    at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source) ~[na:1.8.0_152]
    at java.lang.ClassLoader.loadClass(Unknown Source) ~[na:1.8.0_152]
    at javafx.fxml.FXMLLoader$ValueElement.processAttribute(FXMLLoader.java:920) ~[jfxrt.jar:na]
    ... 57 common frames omitted
 

looparda

Top Contributor
Der Classloader kennt die Klasse ApplicationSceneTableViewController nicht mal. Jetzt musst du aber auch noch mehr über die Strukturierung deines Programms offenlegen.
 

robinab

Mitglied
Das liegt nicht daran, dass der ClassLoader die Klassen nicht finden kann - also grundsätzlich gesehen.
Alle Controller, Services usw. werden vom SpringContainer gefunden.
Auch die erste Scene, deren Controller ich direkt an den FXMLLoader übergebe, kann geladen und angezeigt werden. Die zweite auch - bis zu der Stelle, an der das eingebettete FXML ist. Das wird versucht, zu laden und bleibt an der Zeile hängen, an der ich den Controller mit fx-controller bekannt gebe.
Ein Kollege hat ein kleines Proggi geschrieben, um das Handling mit den eingebetteten FXMLs + Spring auszuprobieren und das funkt. Heute will er mal seine Klassen in mein Programm einbauen, um zu schauen, ob seine (eingebetteten) Scenes auch hier noch angezeigt werden können. Vielleicht kommen wir so auf die Schraube, die klemmt o_O.

Falls doch noch jemandem etwas hierzu einfällt bzw. dieses Problem auch schon hatte und das lösen konnte, würde ich mich über einen Post sehr freuen.

Falls wir das lösen können, werde ich das Ergebnis posten, kann ja vielleicht jemand anderes mal brauchen ;).
 

mrBrown

Super-Moderator
Mitarbeiter
Das liegt nicht daran, dass der ClassLoader die Klassen nicht finden kann - also grundsätzlich gesehen.
Wie würdest du denn dann eine java.lang.ClassNotFoundException: application.controller.ApplicationSceneTableViewController verstehen?


Falls doch noch jemandem etwas hierzu einfällt bzw. dieses Problem auch schon hatte und das lösen konnte, würde ich mich über einen Post sehr freuen.
Ich würde auf ein falsches Package tippen...
zeig doch mal die *ganze* Klasse inklusive Package-Statement.
 

robinab

Mitglied
Wir haben rausgefunden, wie es funktioniert!
Wie versprochen, hier die Lösung:

Der FXMLLoader darf nicht mit dem SpringContainer arbeiten:
Code:
loader.setControllerFactory(application.getSpringContext()::getBean);

loader.setBuilderFactory(new JavaFXBuilderFactory());
sondern muss mit dem Default arbeiten - wenn man mit nested FXMLs arbeitet!

Wenn man keine nested FXMLs im Projekt hat, dann funktioniert das schon.
In dem Fall muss man dem Loader für jede FXML mit setController(...) sagen, welcher Controller dazugehört.
Dann sind auch alle Controller-Klassen in den SpringContainer eingebunden und können mit ComponentScan und Dependency Injection arbeiten. Verwendet man aber nachträglich eine nested FXML (weil das Projekt gewachsen ist), gehört man der Katz :confused:.

Hat man aber nested FXMLs, sollte man den Controller mit der Standardkonfiguration arbeiten lassen und alle Controller innerhalb der FXMLs per fx:controller="..." bekannt geben.
Das hat leider den Nachteil, dass die Controller nicht bekannt sind im SpringContainer und ComponentScan und Dependency Injection für diese Klassen nicht verwendet werden kann.
Daher auch meine Aussage:
Das liegt nicht daran, dass der ClassLoader die Klassen nicht finden kann - also grundsätzlich gesehen.
Spring konnte alle Klassen finden, nur nicht die Controller-Klassen ;).

Grüße
Andrea.
 

mrBrown

Super-Moderator
Mitarbeiter
Ich bezweifle, dass dies das wirkliche Problem ist. Nested-Controller funktionieren im Normalfall auch mit Spring problemlos.
Hast du irgendwo mal ein kleines Beispielprogramm, an dem man den Fehler sieht?

Die Exception sagt ziemlich deutlich, dass die Klasse nicht gefunden wird - *bevor* Spring oder eine andere ControllerFactory damit überhaupt irgendwas zu tun hat (die bekommen die geladene Klasse übergeben).
 
Zuletzt bearbeitet:

denox

Neues Mitglied
Wie würdest du denn dann eine java.lang.ClassNotFoundException: application.controller.ApplicationSceneTableViewController verstehen?



Ich würde auf ein falsches Package tippen...
zeig doch mal die *ganze* Klasse inklusive Package-Statement.
Hey ja das stimmt, also, wenn man den Packagenamen eingibt gefolgt von der Klasse klappts. Also z.B. Package heißt Testpackage und die Klasse heißt Testclass, dann fx:id = "Testpackage.Testclass"
 

mrBrown

Super-Moderator
Mitarbeiter
Hey ja das stimmt, also, wenn man den Packagenamen eingibt gefolgt von der Klasse klappts. Also z.B. Package heißt Testpackage und die Klasse heißt Testclass, dann fx:id = "Testpackage.Testclass"
An welcher Stelle muss man den fully qualified class name als fx:id angeben?



Wie oben schon gesagt: der Fehler wird in diesem Fall einfach die angäbe der falschen Klasse als Controller gewesen sein.
Alle Controller lagen in com.gfk.atr.javafxmltemplate.login.controller, der bemängelte war mit application.controller.ApplicationSceneTableViewController angegeben – ist für mich relativ offfensichtlich, dass es kein Problem von Spring, sondern einfach nur die falsch angegebenen Klasse war.
 

Neue Themen


Oben