Komponentenbasierte JavaFX-Architektur mit CDI (FX Comp)
Eine modulare und wartbare Architektur für komplexe Desktop-Anwendungen mit JavaFX, Jakarta CDI (Weld SE) und dem Java Platform Module System (JPMS).
Inspiration und Vorarbeiten
Viele Konzepte der FX Comp Architektur wurden initial von Adam Bien und seinem Pionierprojekt afterburner.fx inspiriert. FX Comp führt diesen Ansatz konsequent weiter und nutzt moderne Industriestandards für CDI-basiertes Inversion of Control (IoC), typisierte Service-Schnittstellen und ereignisgesteuerte lose Kopplung.
🎯 Motivation & Grundlagen: Wie JavaFX arbeitet
JavaFX ist ein modernes UI-Toolkit für die JVM, das die visuelle Gestaltung von Benutzeroberflächen durch das WYSIWYG-Designwerkzeug SceneBuilder von Gluon und deklarative Layout-Dateien (.fxml) unterstützt.
Trennung von Layout und Programmlogik
In JavaFX kann die UI-Gestaltung strikt von der Geschäftslogik getrennt werden:
- FXML-Deklaration: Die Oberfläche wird in XML-basierten
.fxml-Dateien beschrieben. - Controller-Bindung: Ein
FXMLLoaderinstanziiert den Controller und injiziert Referenzen auf UI-Elemente (Button,TableView,AnchorPaneetc.) automatisch in mit@FXMLannotierte Felder.
<!-- FXML-Ausschnitt mit Button-Definitionen -->
<Button fx:id="btnOk" text="Übernehmen" />
<Button fx:id="btnCancel" text="Abbrechen" />
// Java-Controller mit @FXML-Injektion
public class MyDialogController {
@FXML private Button btnOk;
@FXML private Button btnCancel;
@FXML protected void initialize() {
btnOk.setOnAction(e -> handleOk());
btnCancel.setOnAction(e -> handleCancel());
}
}
Abb. 1: Informationsfluss und Bindung zwischen SceneBuilder/FXML, FXMLLoader und Controller
⚠️ Herausforderungen bei großen JavaFX-Anwendungen
Während dieser Mechanismus für kleinere Dialoge hervorragend funktioniert, stößt Standard-JavaFX bei wachsender Komplexität an architektonische Grenzen:
-
Monolithische Controller (Fat Controllers):
Standardmäßig existiert eine 1:1-Beziehung zwischen einer FXML-Datei und ihrer Controller-Klasse. Bei umfangreichen Oberflächen wachsen Controller schnell auf hunderte Zeilen an und vermischen UI-Steuerung, Validierung, Datenzugriff und Navigation. -
Fehlende native Komponenten-Komposition:
Es gibt in Standard-JavaFX kein standardisiertes Muster, um eine Gesamtoberfläche aus wiederverwendbaren, in sich geschlossenen Sub-Komponenten zusammenzusetzen, die eigene Services und Lebenszyklen kapseln. -
Hohe Kopplung zwischen UI-Teilen:
Werden Teilbereiche manuell instanziiert, entsteht oft ein engmaschiges Geflecht direkter Objektreferenzen, das Refactorings erschwert und automatisiertes Testen behindert.
🏛️ Das FX Comp Architekturmodell
Hier setzt FX Comp an. Anstelle großer monolithischer Masken wird die gesamte Benutzeroberfläche aus autonomen, wiederverwendbaren Komponenten aufgebaut.
Abb. 2: Konzeptionelle Übersicht der FX-Comp-Kommunikation (Synchron über Services vs. Asynchron über CDI-Events)
Was zeichnet eine FX-Comp-Komponente aus?
Eine Komponente in FX Comp ist ein mehrteiliger, wohldefinierter Software-Baustein:
- View (
FXCView): Repräsentiert die Komponente im JavaFX-Szenengraph (javafx.scene.Parent) und dient als visueller Einstiegspunkt für die Komposition. - Controller (
FXCViewController): Steuert das UI-Verhalten und reagiert auf Benutzerinteraktionen. - Service-Vertrag (
FXCService): Definiert eine typsichere Java-Schnittstelle für direkte Methodenaufrufe (z. B.Customer getSelectedCustomer()). - FXML-Layout: Deklarative Oberfläche, bearbeitbar mit SceneBuilder.
- Ereignis-Messaging (CDI Events): Ermöglicht lose gekoppelte Benachrichtigungen zwischen Komponenten (z. B. TaskCreatedEvent, SelectionChangedEvent).
- Autonome Ausführbarkeit: Jede Komponente kann isoliert als eigenständige JavaFX-Applikation gestartet werden.
🧩 Technische Kernbausteine & Top-Level-Typen
FX Comp strukturiert Komponenten in standardisierte Basisklassen, die nach dem Prinzip Convention over Configuration arbeiten:
Abb. 3: Kollaboration der Kern-Typen in der FX-Comp-Architektur
1. FXCView & DefaultFXCView
FXCView ist der zentrale Ankerpunkt. Die abstrakte Basisklasse DefaultFXCView lädt automatisch die zugehörige FXML-Datei gleichen Namens und bindet den passenden Controller an:
package de.ruu.app.pragma.fx.task.editor;
import de.ruu.lib.fx.comp.DefaultFXCView;
import jakarta.enterprise.context.Dependent;
@Dependent
public class TaskEditor extends DefaultFXCView<TaskEditor, TaskEditorService, TaskEditorController> {
// Standard-Implementierung lädt TaskEditor.fxml und verknüpft TaskEditorController
}
2. FXCViewController & DefaultFXCViewController
Der Controller steuert die Interaktionen. Er greift über @FXML auf die Widgets zu und kann mit anderen Komponenten über deren View-Schnittstellen oder Events interagieren:
package de.ruu.app.pragma.fx.task.editor;
import de.ruu.lib.fx.comp.FXCViewController.DefaultFXCController;
import javafx.fxml.FXML;
import javafx.scene.control.TextField;
public class TaskEditorController extends DefaultFXCViewController {
@FXML private TextField titleField;
@FXML private TextField descriptionField;
@Override
@FXML protected void initialize() {
// Initialisierungslogik und Bindings
}
}
⚡ Das duale Inversion-of-Control-Prinzip (JavaFX + CDI)
Die besondere Stärke von FX Comp liegt im nahtlosen Zusammenspiel zweier Inversion-of-Control-Mechanismen:
┌─────────────────────────────────────────────────────────────┐
│ FX Comp Controller │
├──────────────────────────────┬──────────────────────────────┤
│ JavaFX FXMLLoader │ Jakarta CDI (Weld SE) │
│ @FXML │ @Inject │
│ - Button btnSave │ - SubView subComponent │
│ - TableView tableView │ - TaskService taskService │
│ - AnchorPane container │ - Event<TaskSaved> events │
└──────────────────────────────┴──────────────────────────────┘
- JavaFX
FXMLLoaderinjiziert visuelle Steuerelemente (@FXML) direkt in den Controller. - Jakarta CDI (Weld SE) injiziert Geschäfts-Services, Daten-Repositories und Sub-Komponenten (
@Inject).
🔄 Hierarchische Komponenten-Komposition in der Praxis
Komplexe Oberflächen entstehen durch einfaches Schachteln kleinerer Komponenten:
Abb. 4: Hauptlayout mit Umschalt-Buttons und Container (`AnchorPane main`)
Controller-Code für dynamische Sub-Komponenten
public class HierarchyDemoMainController extends DefaultFXCViewController {
@FXML private AnchorPane main;
@FXML private Button btnShow1;
@FXML private Button btnShow2;
// Sub-Komponenten werden einfach via CDI injiziert!
@Inject private HierarchyDemoSub1 sub1;
@Inject private HierarchyDemoSub2 sub2;
@Override
@FXML protected void initialize() {
// Initialen Inhalt setzen
main.getChildren().add(sub1.getLocalRoot());
// Umschalten per Button-Klick
btnShow1.setOnAction(e -> showComponent(sub1));
btnShow2.setOnAction(e -> showComponent(sub2));
}
private void showComponent(FXCView<?, ?, ?> view) {
main.getChildren().clear();
main.getChildren().add(view.getLocalRoot());
}
}
🧪 Isolierte Testbarkeit & Rapid Prototyping
Ein gravierender Nachteil herkömmlicher UI-Entwicklung ist, dass Komponenten oft nur im Gesamtkontext der gestarteten Anwendung getestet werden können.
FX Comp stellt dafür FXCApp und FXCAppRunner bereit:
Abb. 5: Bootstrapping und Standalone-Ausführung via FXCApp und FXCAppRunner
Standalone-Starter für jede Komponente
Mit wenigen Zeilen Code lässt sich jede einzelne Komponente unabhängig von der Gesamtanwendung starten:
public class TaskEditorApp extends FXCApp {
public static void main(String[] args) {
FXCAppRunner.run(TaskEditorApp.class, args);
}
}
- Vorteile:
- Schnelles Feedback: Sofortige visuelle Begutachtung im Entwicklungsprozess.
- Entkoppelte UI-Tests: Schnelle automatisierte Tests ohne Hochfahren des gesamten Anwendungs-Stacks.
🔒 Kombination mit dem Java Module System (JPMS)
FX Comp ist von Grund auf so entworfen, dass es nahtlos mit JPMS (Java Platform Module System) harmoniert:
- Jede Komponente oder Komponenten-Gruppe kann in einem eigenen Java-Modul (
module-info.java) gekapselt werden. - Nur die öffentlichen
FXCView- und Service-Schnittstellen werden perexportsfreigegeben; Controller und interne FXML-Ressourcen bleiben gekapselt (opens ... to javafx.fxml, weld.core.impl).
Vertiefende Informationen zu Modularisierung und JPMS finden Sie in den Fachartikeln:
📊 Zusammenfassung
| Eigenschaft | Standard-JavaFX | FX Comp mit CDI |
|---|---|---|
| Controller-Größe | Wächst schnell monolithisch an | Schlank, fokussiert auf eine Komponente |
| Komposition | Manuell / ad-hoc | Standardisiert über FXCView & @Inject |
| Kommunikation | Enge Objektreferenzen | Typisierte Services + CDI-Events |
| Testbarkeit | Meist nur im Gesamtsystem | Autonom via FXCAppRunner startbar |
| Kapselung | Oft schwach | Durch JPMS und CDI strikt durchsetzbar |
📬 Kontakt zum Autor
Haben Sie Fragen, Anregungen oder Feedback zur FX-Comp-Architektur oder zum Einsatz von CDI mit JavaFX?
✉️ Kontaktformular öffnen