Was ist neu in CUBA 7?
Vor drei Jahren haben wir die zweite öffentlich verfügbare Hauptversion des Frameworks angekündigt. CUBA 6 war die bahnbrechende Version - die Lizenzierung wurde von proprietär auf Apache 2.0 umgestellt. In jenen Tagen konnten wir nicht einmal erraten, wohin es den Rahmen langfristig bringen würde. Die CUBA-Community begann exponentiell zu wachsen, daher haben wir viele mögliche (und manchmal unmögliche) Möglichkeiten kennengelernt, wie Entwickler das Framework verwenden. Jetzt freuen wir uns, CUBA 7 vorstellen zu können , was hoffentlich die Entwicklung für alle Community-Mitglieder kohärenter und erfreulicher macht, von denen, die gerade ihre Reise in CUBA und Java begonnen haben, bis hin zu erfahrenen Unternehmensentwicklern und Java-Experten.

Einen großen Teil des CUBA-Erfolgs verdanken wir natürlich CUBA Studio . Es hat die überarbeitete Java-Unternehmensroutine bemerkenswert vereinfacht und sie vielerorts auf einfache Konfigurationen in den visuellen Designern zurückgeführt: Sie müssen weder die Persistence API noch Gradle oder sogar Spring kennen, um eine vollständige und funktionsreiche CRUD-Anwendung zu entwickeln - Studio wird dies tun für dich.
Das Studio war eine separate Webanwendung, und diese Tatsache verursachte einige erhebliche Einschränkungen:
- Erstens war Studio keine voll funktionsfähige IDE, daher mussten Entwickler zwischen Studio und IntelliJ IDEA oder Eclipse wechseln, um Geschäftslogik zu entwickeln und von bequemer Navigation, Code-Vervollständigung und anderen wichtigen Dingen zu profitieren, was ärgerlich war.
- Zweitens wurde diese magische Einfachheit durch massives Parsen und Generieren von Quellcode aufgebaut. Die Verbesserung der Funktionen zur Codegenerierung würde bedeuten, dass eine voll funktionsfähige IDE entwickelt wird - ein zu ehrgeiziges Unterfangen.
Wir beschlossen, uns auf die Schulter eines anderen Riesen zu stützen, um diese Einschränkungen zu überwinden. Studio wurde von JetBrains in IntelliJ IDEA integriert. Jetzt können Sie es als Plugin für Ihre IntelliJ IDEA installieren oder als separates Standalone-Bundle herunterladen.

Dies eröffnet neue Horizonte:
- Unterstützung für andere JVM-Sprachen (und Kotlin an erster Stelle)
- Verbesserte Hot-Bereitstellung
- Intuitive Navigation durch das gesamte Projekt
- Intelligentere Hinweise und Codegeneratoren
Derzeit befindet sich das neue Studio in der aktiven Entwicklung: Wir portieren Funktionen aus der alten Version. Kurzfristig ist auch geplant, webbasierte Designer mithilfe der nativen IntelliJ-Benutzeroberfläche erneut zu implementieren und die Projektnavigation zu verbessern.
Stapel-Upgrade
Traditionell wurde der zugrunde liegende Stack auch stark aktualisiert, z. B. Java 8/11, Vaadin 8, Spring 5.

Standardmäßig verwenden neue Projekte Java 8, aber Sie können die Java-Version angeben, indem Sie der Datei build.gradle die folgende Klausel hinzufügen:
subprojects { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 }
Das Upgrade auf Vaadin 8 war eine große Herausforderung, da die Vaadin-Datenbindungs-API massiv geändert wurde. Glücklicherweise abstrahiert CUBA Entwickler von Vaadin-Interna, indem es sie in eine eigene API-Schicht einwickelt. Das CUBA-Team hat großartige Arbeit geleistet, indem es Interna neu implementiert hat und seine eigene API unberührt gelassen hat. Dies bedeutet, dass die Kompatibilität vollständig gespeichert ist und Sie direkt nach der Migration eines Projekts auf CUBA 7 ohne Refactoring von Vaadin 8 profitieren können.
Die vollständige Liste der aktualisierten Abhängigkeiten finden Sie in den offiziellen Versionshinweisen .
Neue Bildschirm-API
Dieser Abschnitt könnte auch als "API für die ersten Bildschirme" bezeichnet werden, da CUBA noch nie eine offiziell deklarierte API in der Webclient-Ebene hatte. Es stammt aus der Geschichte des Frameworks und bestimmten Annahmen, die in der ersten Phase getroffen wurden:
- Deklarativ-zentrierter Ansatz - Alles, was deklarativ beschrieben werden kann, sollte in einem Bildschirmdeskriptor deklariert und nicht in seinem Controller codiert werden
- Standardbildschirme (Browser und Editor) bieten konkrete allgemeine Funktionen, die nicht geändert werden müssen
Seit die ersten tausend Mitglieder unserer Community beigetreten sind, haben wir festgestellt, wie vielfältig die Anforderungen an "Standard" -CRUD-Bildschirme sind - weit über die ursprünglich entworfenen Funktionen hinaus. Trotzdem konnten wir lange Zeit Anfragen nach benutzerdefiniertem Verhalten auch ohne API-Schicht bearbeiten - dank einer anderen Annahme der ersten Stufe - Open Inheritance. Effektiv Open Inheritance bedeutet, dass Sie jede öffentliche oder geschützte Methode einer zugrunde liegenden Klasse überschreiben können, um ihr Verhalten an Ihre Bedürfnisse anzupassen. Dies mag nach einer Heilung für alle Krankheiten klingen, gibt Ihnen jedoch nicht einmal einen kurzfristigen Vertrag: Was ist, wenn die überschriebene Methode in zukünftigen Versionen des Frameworks umbenannt, gelöscht oder einfach nie verwendet wird?

Als Reaktion auf die wachsende Nachfrage der Community haben wir uns entschlossen, eine neue Bildschirm-API einzuführen. Die API bietet klare und langfristige Erweiterungspunkte ohne versteckte deklarative Magie, ist flexibel und sehr einfach zu bedienen.
Bildschirmdeklaration
In CUBA 7 ist die Bildschirmdeklaration äußerst einfach:
@UiController("new-screen") // screen id public class NewScreen extends Screen { }
Aus dem obigen Beispiel können wir ersehen, dass die Bildschirmkennung direkt über der Controller-Klasse explizit definiert ist. Mit anderen Worten, Bildschirm-ID und Controller-Klasse entsprechen jetzt eindeutig einander. Gute Nachrichten, jetzt können Bildschirme auf sichere Weise direkt von ihrer Controller-Klasse angesprochen werden:
@Inject private ScreenBuilders screenBuilders; @Subscribe private void onBeforeClose(BeforeCloseEvent event) { screenBuilders.screen(this) .withScreenClass(SomeConfirmationScreen.class) .build() .show(); }
Der Bildschirmdeskriptor wird zu einem ergänzenden Teil anstatt zu einem obligatorischen. Das Layout kann programmgesteuert erstellt oder als XML-Bildschirmdeskriptor deklariert werden, der durch die Annotation @UiDescriptor über die Controller-Klasse definiert wird. Dies erleichtert das Lesen und Verstehen von Controllern und Layouts erheblich - dieser Ansatz ist dem in der Android-Entwicklung verwendeten sehr ähnlich.
Zuvor war es auch erforderlich, einen Bildschirmdeskriptor in der Datei web-screen.xml zu registrieren und ihm eine Kennung zuzuweisen. In CUBA 7 wird diese Datei aus Kompatibilitätsgründen beibehalten. Für die Erstellung von Bildschirmen auf neue Weise ist jedoch keine solche Registrierung erforderlich.
Bildschirmlebenszyklus
Die neue API führt klare und selbsterklärende Ereignisse im Bildschirmlebenszyklus ein:
- Init
- Nachher
- Vorab
- Aftershow
- Vorher schließen
- Nach dem Schließen
Alle bildschirmbezogenen Ereignisse in CUBA 7 können wie folgt abonniert werden:
@UiController("new-screen") public class NewScreen extends Screen { @Subscribe private void onInit(InitEvent event) { } @Subscribe private void onBeforeShow(BeforeShowEvent event) { } }
Wenn Sie die neue API mit dem alten Ansatz vergleichen, sehen Sie, dass wir keine Hook-Methoden überschreiben, die in der Hierarchie der übergeordneten Klassen dunkel aufgerufen werden, sondern die Logik in klar vordefinierten Punkten des Bildschirmlebenszyklus definieren.
Ereignisbehandlung und funktionale Delegierte
Im vorherigen Abschnitt haben wir gelernt, wie Sie die Lebenszyklusereignisse abonnieren. Was ist also mit anderen Komponenten? Sollten wir weiterhin alle erforderlichen Listener auf die Bildschirminitialisierung verteilen, wie dies in 6.x-Versionen der Fall war? Die neue API ist sehr einheitlich, sodass das Abonnieren anderer Ereignisse den Ereignissen im Lebenszyklus absolut ähnlich ist.
Nehmen wir ein einfaches Beispiel mit zwei UI-Elementen: einer Schaltfläche und einem Währungsfeld, sodass der XML-Deskriptor folgendermaßen aussieht:
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <window xmlns="http://schemas.haulmont.com/cuba/screen/window.xsd" caption="msg://caption" messagesPack="com.company.demo.web"> <layout> <hbox spacing="true"> <currencyField id="currencyField" currency="$" currencyLabelPosition="LEFT"/> <button id="calcPriceBtn" caption="Calculate Price"/> </hbox> </layout> </window>
Durch Klicken auf die Schaltfläche rufen wir den Middleware-Dienst auf und geben eine Nummer zurück, die in das Währungsfeld wechselt. Das Währungsfeld sollte seinen Stil abhängig vom Preiswert ändern.
@UiController("demo_MyFirstScreen") @UiDescriptor("my-first-screen.xml") public class MyFirstScreen extends Screen { @Inject private PricingService pricingService; @Inject private CurrencyField<BigDecimal> currencyField; @Subscribe("calcPriceBtn") private void onCalcPriceBtnClick(Button.ClickEvent event) { currencyField.setValue(pricingService.calculatePrice()); } @Subscribe("currencyField") private void onPriceChange(HasValue.ValueChangeEvent<BigDecimal> event) { BigDecimal price = pricingService.calculatePrice(); currencyField.setStyleName(getStyleNameByPrice(price)); } private String getStyleNameByPrice(BigDecimal price) { ... } }
Im obigen Beispiel sehen wir zwei Ereignishandler: Einer wird aufgerufen, wenn auf die Schaltfläche geklickt wird, und ein anderer wird ausgeführt, wenn das Währungsfeld seinen Wert ändert - so einfach ist das.
Stellen wir uns nun vor, wir müssen unseren Preis validieren und überprüfen, ob sein Wert positiv ist. Der einfachste Weg wäre, während der Bildschirminitialisierung einen Validator hinzuzufügen:
@UiController("demo_MyFirstScreen") @UiDescriptor("my-first-screen.xml") public class MyFirstScreen extends Screen { @Inject private CurrencyField<BigDecimal> currencyField; @Subscribe private void onInit(InitEvent event) { currencyField.addValidator(value -> { if (value.compareTo(BigDecimal.ZERO) <= 0) throw new ValidationException("Price should be greater than zero"); }); } }
In realen Anwendungen wird ein Bildschirmeinstiegspunkt normalerweise mit dieser Art von Bildschirmelementinitialisierern übersät. Um dieses Problem zu beheben, bietet CUBA die nützliche Anmerkung @Install
. Mal sehen, wie es in unserem Fall helfen kann:
@UiController("demo_MyFirstScreen") @UiDescriptor("my-first-screen.xml") public class MyFirstScreen extends Screen { @Inject private CurrencyField<BigDecimal> currencyField; @Install(to = "currencyField", subject = "validator") private void currencyFieldValidator(BigDecimal value) { if (value.compareTo(BigDecimal.ZERO) <= 0) throw new ValidationException("Price should be greater than zero"); } }
Tatsächlich delegieren wir die Validierungslogik aus unserem Währungsfeld an die CurrencyFieldValidator- Methode in unserem Bildschirm. Dies mag etwas kompliziert aussehen, Entwickler übernehmen diese Funktion jedoch überraschend schnell.
Screen Builder / Benachrichtigungen / Dialoge

CUBA 7 stellt außerdem eine Reihe nützlicher Komponenten mit fließenden APIs vor:
ScreenBuilders kombiniert fließende Fabriken, um Standard-Lookups, Editoren und benutzerdefinierte Bildschirme zu generieren. Das folgende Beispiel zeigt, wie Sie einen Bildschirm von einem anderen aus öffnen können. Beachten Sie, dass die build () -Methode die Bildschirminstanz des richtigen Typs zurückgibt, ohne dass eine unsichere Umwandlung erforderlich ist.
CurrencyConversionsrencyConversions = screenBuilders.screen (this)
.withScreenClass (CurrencyConversions.class)
.withLaunchMode (OpenMode.DIALOG)
.build ();
rencyConversions.setBaseCurrency (Currency.EUR);
rencyConversions.show ();
Die Bildschirmkomponente bietet eine Abstraktion auf niedrigerer Ebene zum Erstellen und Anzeigen von Bildschirmen anstelle von ScreenBuilders . Es bietet auch Zugriff auf die Informationen zu allen geöffneten Bildschirmen in Ihrer CUBA-Anwendung ( Screens # getOpenedScreens ), falls Sie diese durchlaufen müssen.
Die Komponenten Benachrichtigungen und Dialoge bieten praktische, selbsterklärende Schnittstellen. Hier ist ein Beispiel zum Erstellen und Anzeigen eines Dialogfelds und einer Benachrichtigung:
dialogs.createOptionDialog ()
.withCaption ("Mein erster Dialog")
.withMessage ("Möchten Sie dem CUBA-Team danken?")
.withActions (
neue DialogAction (DialogAction.Type.YES) .withHandler (e ->
notifications.create ()
.withCaption ("Danke!")
.withDescription ("Wir schätzen alle Community-Mitglieder")
.withPosition (Notifications.Position.MIDDLE_CENTER)
.withHideDelayMs (3000)
.show ()),
neue DialogAction (DialogAction.Type.CANCEL)
)
.show ();
Datenbindung
CUBA ermöglicht eine extrem schnelle Entwicklung von Backoffice-Benutzeroberflächen, indem nicht nur fortschrittliche visuelle Tools mit umfangreichen Funktionen zur Codegenerierung bereitgestellt werden, sondern auch eine Vielzahl von datensensitiven Komponenten, die sofort verfügbar sind. Solche Komponenten müssen nur wissen, mit welchen Daten sie arbeiten, und der Rest wird automatisch verwaltet, z. B. Suchlisten, Auswahlfelder, verschiedene Raster mit CRUD-Operationen usw.
Vor Version 7 wurde die Datenbindung über sogenannte Datenquellen implementiert - Objekte, die eine einzelne Entität oder eine Sammlung von Entitäten umschließen, um sie reaktiv mit datensensitiven Komponenten zu verknüpfen. Dieser Ansatz funktionierte sehr gut, in Bezug auf die Implementierung war es jedoch ein Monolith. Die monolithische Architektur verursacht normalerweise Probleme bei der Anpassung. In CUBA 7 wurde dieser feste Felsblock in drei Datenkomponenten aufgeteilt:
- Data Loader ist ein Datenprovider für Datencontainer. Datenlader speichern keine Daten, sondern übergeben lediglich alle erforderlichen Abfrageparameter an einen Datenspeicher und füttern Datencontainer mit dem resultierenden Datensatz.
- Der Datencontainer speichert die geladenen Daten (eine einzelne Entität oder eine Anzahl von Entitäten) und stellt sie den datensensitiven Komponenten auf reaktive Weise zur Verfügung: Alle Änderungen der umschlossenen Entitäten werden den entsprechenden UI-Komponenten ausgesetzt und umgekehrt, alle Änderungen in Die UI-Komponenten führen zu den entsprechenden Änderungen in ihrem Datencontainer.
- Der Datenkontext ist ein leistungsstarker Datenänderungsmanager, der Änderungen verfolgt und alle geänderten Entitäten festschreibt. Eine Entität kann in einem Datenkontext zusammengeführt werden, sodass eine Kopie der ursprünglichen Entität mit dem einzigen, aber sehr wichtigen Unterschied bereitgestellt wird: Alle Änderungen der resultierenden Entität und aller Entitäten, auf die sie verweist (einschließlich Sammlungen), werden verfolgt, gespeichert und entsprechend verpflichtet.
Datenkomponenten können in Bildschirmdeskriptoren deklariert oder mithilfe einer speziellen Factory - DataComponents - programmgesteuert instanziiert werden.
Verschiedenes
Ufff, die wichtigsten Teile der neuen Bildschirm-API werden beschrieben. Lassen Sie mich daher kurz andere wichtige Funktionen in der Web-Client-Ebene auflisten:
- URL-Verlauf und Navigation . Diese Funktion löst ein sehr häufiges SPA-Problem mit der Schaltfläche "Zurück" in einem Webbrowser, bietet eine einfache Möglichkeit, Routen zu Anwendungsbildschirmen zuzuweisen, und ermöglicht einer API, den aktuellen Status eines Bildschirms in ihrer URL wiederzugeben.
- Formular anstelle von FieldGroup . FieldGroup ist eine datensensitive Komponente zum Anzeigen und Ändern von Feldern einer einzelnen Entität. Daraus folgt die tatsächliche Benutzeroberfläche, die zur Laufzeit für ein Feld angezeigt wird. Mit anderen Worten, wenn Sie ein Datumsfeld in Ihrer Entität haben, wird es als Datumsfeld angezeigt . Wenn Sie dieses Feld jedoch programmgesteuert bearbeiten möchten , müssen Sie dieses Feld in die Bildschirmsteuerung einfügen und manuell in den richtigen Typ umwandeln ( in unserem Beispiel DateField ). Später ändern wir unseren Feldtyp in einen anderen und unsere Anwendung stürzt zur Laufzeit ab ... Das Formular behebt dieses Problem durch explizite Feldtypdeklaration. Weitere Informationen zu dieser neuen Komponente finden Sie hier .
- Die Integration von JavaScript-Komponenten von Drittanbietern wird erheblich vereinfacht. Befolgen Sie die Dokumentation , um benutzerdefinierte JavaScript-Komponenten in eine CUBA-Anwendung einzubetten.
- HTML / CSS- Attribute können jetzt einfach direkt aus dem XML-Bildschirmdeskriptor definiert oder programmgesteuert festgelegt werden. Weitere Informationen finden Sie hier .
Middleware-Funktionen
Der vorherige Block über die neue Bildschirm-API war größer als ich erwartet hatte, also werde ich in diesem Abschnitt versuchen, ordentlich zu sein!
Entity Changed Event
Entity Changed Event ist ein Spring-Anwendungsereignis, das ausgelöst wird, wenn Ihre Entität den Weg zu einem Datenspeicher gefunden hat, physisch eingefügt wurde und sich nur einen Zentimeter von der Festschreibung entfernt befindet. Hier können Sie einige zusätzliche Überprüfungen durchführen (z. B. die Produktverfügbarkeit auf Lager prüfen, bevor Sie eine Bestellung bestätigen) und diese ändern (z. B. die Gesamtsummen neu berechnen), bevor sie für andere Transaktionen sichtbar werden (natürlich mit gelesener Isolationsstufe). Sie können dieses Ereignis auch als letzte Möglichkeit verwenden, um das Festschreiben der Transaktion zu unterbrechen, indem Sie eine Ausnahme auslösen. Dies kann in einigen Eckfällen hilfreich sein.
Es gibt auch eine Möglichkeit, das Ereignis "Entity Changed" direkt nach dem Festschreiben abzufangen.
Folgen Sie diesem Kapitel der Dokumentation, um ein Beispiel zu sehen.
Transaktionsdaten-Manager
Bei der Entwicklung einer Anwendung arbeiten wir normalerweise mit getrennten Entitäten - solchen, die von keiner Transaktion verwaltet werden. Die Arbeit mit getrennten Entitäten ist jedoch nicht immer möglich, insbesondere wenn versucht wird, die ACID-Anforderungen zu erfüllen. Dies ist der Fall, wenn Sie den Transaktionsdatenmanager verwenden können. Es sieht dem normalen Datenmanager sehr ähnlich, unterscheidet sich jedoch in folgenden Aspekten:
- Es kann einer vorhandenen Transaktion beitreten (falls sie im Transaktionskontext aufgerufen wird) oder eine eigene Transaktion erstellen.
- Es gibt keine Festschreibungsmethode , aber es gibt die Speichermethode, die nicht zum sofortigen Festschreiben führt, sondern wartet, bis die angehängte Transaktion festgeschrieben wird.
Ein Beispiel für die Verwendung finden Sie hier .
JPA Lifecycle Callbacks
Schließlich unterstützt CUBA 7 JPA-Lifecycle-Rückrufe. Um keine gut geschriebenen Informationen darüber zu replizieren, wofür diese Rückrufe verwendet werden können, möchte ich nur diesen Link teilen, der das Thema vollständig abdeckt.
Was ist mit Kompatibilität?

Eine faire Frage für jede größere Veröffentlichung, besonders wenn es so viele scheinbar bahnbrechende Änderungen gibt! Wir haben all diese neuen Funktionen und APIs unter Berücksichtigung der Abwärtskompatibilität entwickelt:
- Die alte Bildschirm-API wird in CUBA 7 unterstützt und über die neue unter der Haube implementiert :)
- Wir haben auch Adapter für die alte Datenbindung bereitgestellt, die weiterhin für die altmodischen Bildschirme funktionieren.
Gute Nachrichten, der Migrationspfad von Version 6 auf Version 7 sollte also recht einfach sein.
Fazit
Abschließend möchte ich erwähnen, dass es weitere wichtige Neuerungen gibt, insbesondere bei der Lizenzierung:
- Das Limit von 10 Entitäten für Studio ist jetzt weg
- Reporting, BPM, Diagramme und Karten sowie Addons für die Volltextsuche sind jetzt kostenlos und Open Source.
- Die kommerzielle Version von Studio bietet visuellen Designern zusätzlichen Entwicklungskomfort für Entitäten, Bildschirme, Menüs und andere Plattformelemente, während sich die kostenlose Version auf die Arbeit mit Code konzentriert
- Bitte beachten Sie, dass für 6.x und ältere Versionen der Plattform und Studio die Lizenzbedingungen gleich bleiben!
Abschließend möchte ich mich noch einmal bei den Community-Mitgliedern für die Unterstützung und das Feedback bedanken. Ich hoffe du wirst Version 7 lieben! Die vollständige Liste der Änderungen finden Sie traditionell in den Versionshinweisen .