Falsches Inject von FXML und Controller

Hey Leute,
also bei mir ist gerade der Wurm drin, oder ich bin blind.

Bitte um Hilfe bei folgender Exception:

Caused by: javafx.fxml.LoadException: 
$path/resources/main/fxml/user-box.fxml
$path/resources/main/fxml/detail-box.fxml:17
$path/resources/main/fxml/main-window.fxml:9

usw. usf.

Caused by: java.lang.IllegalArgumentException: Can not set javafx.scene.control.Label field $package.controller.UserBoxController.location to java.net.URL

Meldung ist mir an sich klar, im UserBoxController ist ein Feld “location” als Label deklariert. Allerdings scheint er in der FXML eine URL vorzufinden und das Injecten schlägt fehl?
Hier noch einmal die komplette Exception. Vorweg ich hab den kompletten Package-Pfad abgekürzt ($package), bzw. Datei-Pfade mit $path und MyApplication ist halt der Entry Point
der JavaFX Anwendung.

[spoiler]


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:497)
	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:497)
	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$152(LauncherImpl.java:182)
	at com.sun.javafx.application.LauncherImpl$$Lambda$50/1323468230.run(Unknown Source)
	at java.lang.Thread.run(Thread.java:745)
Caused by: javafx.fxml.LoadException: 
$path/resources/main/fxml/user-box.fxml
$path/resources/main/fxml/detail-box.fxml:17
$path/resources/main/fxml/main-window.fxml:9

	at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2605)
	at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2583)
	at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2445)
	at javafx.fxml.FXMLLoader.access$2700(FXMLLoader.java:104)
	at javafx.fxml.FXMLLoader$IncludeElement.constructValue(FXMLLoader.java:1139)
	at javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:742)
	at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2711)
	at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2531)
	at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2445)
	at javafx.fxml.FXMLLoader.access$2700(FXMLLoader.java:104)
	at javafx.fxml.FXMLLoader$IncludeElement.constructValue(FXMLLoader.java:1139)
	at javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:742)
	at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2711)
	at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2531)
	at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2445)
	at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2413)
	at MyApplication.start(MyApplication.java:58)
	at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$159(LauncherImpl.java:863)
	at com.sun.javafx.application.LauncherImpl$$Lambda$53/703873231.run(Unknown Source)
	at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$172(PlatformImpl.java:326)
	at com.sun.javafx.application.PlatformImpl$$Lambda$45/186276003.run(Unknown Source)
	at com.sun.javafx.application.PlatformImpl.lambda$null$170(PlatformImpl.java:295)
	at com.sun.javafx.application.PlatformImpl$$Lambda$48/1660695354.run(Unknown Source)
	at java.security.AccessController.doPrivileged(Native Method)
	at com.sun.javafx.application.PlatformImpl.lambda$runLater$171(PlatformImpl.java:294)
	at com.sun.javafx.application.PlatformImpl$$Lambda$47/237061348.run(Unknown Source)
	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$145(WinApplication.java:101)
	at com.sun.glass.ui.win.WinApplication$$Lambda$36/2117255219.run(Unknown Source)
	... 1 more
Caused by: java.lang.IllegalArgumentException: Can not set javafx.scene.control.Label field $package.controller.UserBoxController.location to java.net.URL
	at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
	at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
	at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:81)
	at java.lang.reflect.Field.set(Field.java:764)
	at javafx.fxml.FXMLLoader.injectFields(FXMLLoader.java:1159)
	at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2558)
	... 29 more
Exception running application MyApplication

[/spoiler]

Hier der UserBoxController
[spoiler]

package $package.controller;

import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.text.Text;
import $package.model.User;

public class UserBoxController {

    private static final int AVATAR_SIZE = 250;

    @FXML private Label username;
    @FXML private Label index;
    @FXML private Label infection;

    @FXML private ImageView avatar;
    @FXML private Label location;
    @FXML private Text bio;

    public final void updateUser(final User user) {
        username.setText(user.getName());
        index.setText(String.valueOf(user.getKarma()));
        infection.setText("You can infect " + user.getCanInfect() + " people.");

        if (user.getAvatar() != null && !user.getAvatar().isEmpty()) {
            avatar.setImage(new Image(user.getAvatar(),
                    AVATAR_SIZE, AVATAR_SIZE, true, true, true));
        }
        location.setText(user.getCity() + ", " + user.getCountry());
        bio.setText(user.getBio());
    }
}

[/spoiler]

Und hier die zugehörige FXML:
[spoiler]
[xml]

<?xml version="1.0" encoding="UTF-8"?> <?import javafx.geometry.Insets?> <?import javafx.scene.control.Label?> <?import javafx.scene.control.ScrollPane?> <?import javafx.scene.control.Separator?> <?import javafx.scene.image.Image?> <?import javafx.scene.image.ImageView?> <?import javafx.scene.layout.VBox?> <?import javafx.scene.text.Text?>


    <ScrollPane styleClass="scrollpane-custom">
            <VBox spacing="20">
                <padding>
                    <Insets left="10" right="10" bottom="10" top="10"/>
                </padding>
                <VBox spacing="10" styleClass="block">
                        <Label fx:id="username" styleClass="big-centered-label, blue-label" text="Name of the User"/>
                        <Label fx:id="index" styleClass="big-centered-label" text="10 000"/>
                        <Label fx:id="infection" styleClass="centered-label" text="Their/You can infext X people."/>
                </VBox>

                <VBox alignment="CENTER" spacing="10" styleClass="block">
                    <padding>
                        <Insets bottom="10" left="10" right="10" top="10"/>
                    </padding>
                        <Separator prefHeight="1"/>
                        <ImageView fx:id="avatar" fitHeight="250" fitWidth="250" preserveRatio="true">
                            <Image url="@../images/user-default.png" />
                        </ImageView>
                        <Label fx:id="location" styleClass="right-align-label, blue-label" text="City, Country" />
                        <Separator prefHeight="1"/>
                </VBox>

                <VBox spacing="10" styleClass="block">
                    <padding>
                        <Insets bottom="10" left="10" right="10" top="10"/>
                    </padding>
                        <Label styleClass="big-left-label, blue-label" text="Bio:"/>
                        <Text fx:id="bio" fill="#9B9B93" fontSmoothingType="lcd"
                              text="Lorem ipsum dolor sit amet."
                              wrappingWidth="250">
                        </Text>
                </VBox>
            </VBox>
    </ScrollPane>
[/xml] [/spoiler]

Wäre super, wenn da mal jemand rübergucken kann. Irgendwas entgeht mir wohl oder ich schaue an der falschen Stelle.

Also zunächst einmal, das Problem ist gelöst, nur die Ursache ist noch nicht gefunden und die würde ich ganz gerne rausfinden.

Im UserBoxController, hatte ich das

implements Initializable

entfernt, weil die Methode leer war. Soweit ich weiß ist
das Interface auch nicht zwingend notwendig für die Funktionsfähigkeit des Controllers, sondern nur dafür gedacht wichtige Aufgaben nach
der Erzeugung (und dem injecten der FXML Controls) zu übernehmen.

Hier taucht jetzt auch die ominöse URL auf, die nirgendwo zu finden war (und die auch “location” heißt):


    @Override
    public void initialize(URL location, ResourceBundle resources) {
        
    }

Da werde ich mich mal morgen genauer in der API belesen, evtl. weiß da jemand ja vorher schon was zu und möchte antworten.
Vielleicht ist es auch notwendig, weil der Controller selbst Part eines ParentController ist und dort injected wird, sieht dann so aus:

@FXML private UserBoxController userBoxController;

Wichtig ist dabei, dass der erste Part vor Controller mit dem gleichnamigen fx:id aus dem Parent-Node des FXML übereinstimmt.

So, habe heute mal das Interface Initializable geprüft:

NOTE This interface has been superseded by automatic injection of location and resources properties into the controller. FXMLLoader will now automatically call any suitably annotated no-arg initialize() method defined by the controller. It is recommended that the injection approach be used whenever possible.

Dazu die Methoden-Signatur:

void initialize(URL location, ResourceBundle resources)
Called to initialize a controller after its root element has been completely processed.

Somit ist die Ursache also auch gefunden. Wenn das Interface nicht implementiert wird, wird eine unsichtbare initialize() Methode aufgerufen und URL, sowie ResourceBundle automatisch injected (soweit entsprechende Felder existieren).
Durch mein Löschen der Methode und des Interfaces, wurde also automatisch versucht auf mein Label zu injected, weil es eben auch location heißt.
Man sollte sich also hier merken, dass in einem JavaFX Controller die beiden Namen location und resources nicht belegt werden sollten (oder die initialize-Methode implemenentiert werden sollte).

Ich lasse es mal so stehen, vielleicht hat irgendjemand, irgendwann das gleiche Problem (mich hat es gestern fast wahnsinnig gemacht „Woher kommt die URL?“).
Merke 1. Schritt Stelle überprüfen, 2. Schritt API :smiley:

Ich arbeite selber seit Einführung von FXML mit FXML und kann gerade die Situation irgendwie nicht nachvollziehen.

Das Problem ist ganz einfach dass du aus irgendeinem Grund ein veraltetes Interface verwendet hast. Das geht so nicht mehr und hat auch nichts mit der Namensgebung zu schaffen.
So sieht der Code im gesunden Zustand aus:

@FXML
private void initialize() {
	location.setText( "Hello Label" );
}

Also auf die selbe Weise wie du auch Methoden aus der FXML Datei aufrufen kannst ist die Methode bereits so vorgegeben :wink:

Ich hatte die Methode nicht verwendet, nicht einmal das Interface in o. g. Controller implementiert. Trotzdem wird im Hintergrund versucht die URL zu injecten (war mir bis dato nicht bekannt). Was dann natürlich zu Überschneidungen mit meinem location Field führte. Ich brauche in diesem Controller nämlich an sich gar keine eigenen Initialisierungsvorgänge

Edit: Hatte o. g. Interface aber an anderer Stelle schon einmal verwendet. Kommt einfach daher, dass ich das wohl auf diese Weise irgendwo gelesen/gesehen habe.

Mist, kann nicht mehr bearbeiten.

Also @TMII ,
danke auf jeden Fall für deine Antwort. Immerhin werde ich das Interface jetzt nicht mehr benutzen, habe es aber gerade ausprobiert, ohne besagte Methode von diesem Interface wird
versucht zu injecten. Man könnte also sagen die Namen location und resources sind “belegt”.