CUBA 7 新特性(上篇)
三年前,我們宣佈了 CUBA 框架的第二個公開的主版本。 CUBA 6 是改變遊戲規則的版本 - 框架的許可從私有化變成了公開的 Apache2.0。那些日子裡,我們甚至猜不到這個變化會最終將框架帶向何方。隨之而來的是,CUBA社群開始呈指數級增長,從中我們學習到許多開發人員可能使用框架的方法(有時甚至是不可能的方法)。現在我們很高興的宣佈 CUBA 7 的釋出,通過這個版本,我們希望那些剛剛開始 CUBA和Java之旅的社群成員能更加順利和快樂的成長為熟練的企業級開發人員或者Java專家。
開發工具
顯然, CUBA 的成功很大一部分要依賴於 CUBA Studio 。它極大的簡化了繁瑣的 Java 企業級開發任務,很多地方被簡化成只需要在視覺化編輯器進行簡單的配置即可,不需要了解Persistence API 或者 Gradle,甚至不需要了解 Spring 就能開發出來完整的、功能豐富的CRUD 應用程式。這一切,Studio就能幫你完成。
以前, Studio 是一個單獨的 web 應用程式,這樣會有一些明顯的侷限:
l 首先, Studio 並不是功能完備的 IDE,所以開發者需要經常在 Studio 和 IntelliJ IDEA 或者Eclipse 之間切換,以便在 IDE 中開發業務邏輯,也能更好的利用 IDE 方便的導航、程式碼完成功能和其他必要的功能。來回地切換有時候很煩人。
l 其次, Studio 的簡單性是建立在大量的原始碼解析和生成的基礎上。所以,要提高程式碼生成的能力也就意味著要朝著開發功能完備的IDE方向努力 - 這個想法太過雄心勃勃了。
最後我們決定依靠另一位巨人的肩膀來解決這些侷限。現在 Studio 跟 JetBrains 開發的IntelliJ IDEA 合併了。現在可以將 Studio 作為 IntelliJ IDEA 的外掛安裝或者下載單獨打包的版本。
這個方法為我們開闢了新的視野:
l 能支援其它 JVM的開發語言(首先就是Kotlin)
l 提升了熱部署的能力
l 整個專案中能更直觀的導航
l 更聰明的程式碼生成和提醒
現在新的 Studio正在積極的開發中:我們正在移植舊版本的功能。短期計劃還包括使用原生IntelliJ UI重新實現基於 Web 的設計器,並改善專案導航體驗。
技術棧升級
跟以前主版本升級一樣,這次底層的技術棧也做了升級,比如 Java 8/11,Vaadin 8,Spring 5。
預設情況下新專案會使用 Java 8,但是也可以通過在build.gradle中新增下面的內容來指定需要的Java版本:
升級到 Vaadin 8是個不小的挑戰,因為Vaadin的資料繫結API發生了很大的破壞性變化。但使用CUBA的開發者很幸運,因為CUBA為開發者提供了統一封裝的自有API層,遮蔽了底層Vaadin的內部結構。CUBA開發團隊做了大量的工作,重新實現了很多內部邏輯以保持CUBA自有的API不變化。也就是說,這很好的保持了CUBA框架的相容性,不需要做任何重構就可以直接移植到CUBA 7並享受Vaadin 8帶來的好處。
依賴庫的完整升級列表可以在官方的 release notes 中找到。
新的介面 API
這一小節也可以稱為 “第一版介面API”,因為CUBA之前沒有任何官方的宣告在web客戶端層有API存在。介面API基於框架的歷史,也基於我們最初的一些假設:
以宣告為中心的方法 - 所有可以以宣告式描述的,都應該在介面描述檔案中宣告,而不是在其控制器中編碼。
標準介面(瀏覽和編輯介面)提供具體的通用功能,一般不需要修改。
從最初的一千個成員加入了社群開始,我們就認識到對於 “標準” CRUD 介面的需求是有多麼廣泛,已經超出了最開始我們設計的一組功能了。然而,很長一段時間,即使沒有 API 層,我們也能夠處理自定義行為的需求,這是因為有另一個第一階段假設 - 開放繼承。有效地進行開放繼承意味著可以覆蓋基礎類的任何公共或保護方法,再根據需要定製其行為。這聽起來似乎是所有頑疾的解藥,但事實上可能短期都不一定能見效:如果被覆蓋的方法被重新命名、刪除了或者將來版本的框架根本不同這個方法了,該怎麼辦?
所以,為了響應社群日益增長的需求,我們決定引入新的介面 API。API提供了清晰的長期的擴充套件點,而沒有隱藏的宣告式暗喻,靈活並且易於使用。
介面宣告
在 CUBA 7 裡,介面宣告異常簡單:
從上面的例子我們可以看到,介面的識別符號在控制器類上顯式的進行定義。也就是說,現在介面 id和控制器類能相互唯一的對應。由此帶來的好訊息就是,現在介面可以直接通過其控制類來安全訪問了(注意下面例子用控制器類來建立確認視窗):
至此,介面描述檔案不再是必須的,而成為了一個補充的部分。介面佈局可以通過程式設計的方式建立或者通過 XML 介面描述宣告式建立,介面描述通過控制器類的 @UiDescriptor 註解定義。這樣能使得控制器和佈局更加容易讀懂。這個方式跟Android開發中使用的模式非常類似。
之前,需要在 web-screens.xml中註冊一個介面描述併為其設定一個識別符號。在 CUBA 7 中,這個檔案只是因為相容性的考慮被保留下來,用新方法建立介面不需要這種註冊了。
介面生命週期
新的 API帶來了清晰的自描述的介面生命週期事件:
l Init
l AfterInit
l BeforeShow
l AfterShow
l BeforeClose
l AfterClose
CUBA 7 中所有的介面相關的事件都可以用下面的方式訂閱:
將新 API與舊方法進行比較,可以看到我們沒有重寫鉤子方法,之前這些鉤子方法在父類的層次結構中被模糊地呼叫。現在我們在介面生命週期的明確預定義的點中定義業務邏輯。
事件處理和功能代理
前一小節我們介紹瞭如何訂閱生命週期事件,那麼,其他元件呢?我們是否應該像在 6.x版本中那樣在介面初始化時分散所有必需的監聽器?新API非常統一,因此訂閱其他事件與生命週期事件完全相似。
我們舉一個帶有兩個 UI元素的簡單例子,一個按鈕和一個貨幣欄位控制元件,因此它的XML描述符如下所示:
通過單擊按鈕我們呼叫中介軟體服務返回一個數字,該數字將被寫到貨幣控制元件中。貨幣控制元件需要根據價格的值更改其樣式。
在上面的例子中,我們看到有兩個事件處理器:一個是按鈕按下時呼叫的,另一個是當貨幣控制元件的值發生變化時執行的。就是這麼簡單。
現在,我們設想一下,如果需要驗證價格的值並確保其為一個正數。最直接的方法就是在介面初始化的時候為其新增一個驗證器:
在真實的應用程式中,介面的入口點經常會被這種介面元素的初始化方法填滿。為了避免這個問題, CUBA提供了一個非常有用的註解 @Install。看看使用這個註解怎麼避免這個情況:
事實上,這裡是將貨幣控制元件驗證的邏輯代理給了介面的 currencyFieldValidator 方法來執行。雖然看上去稍微複雜一點,但是開發人員使用起這個功能來驚人的快速。
介面 Builders/通知訊息/對話方塊
CUBA 7 還引入了一些新的非常有用的帶有流式 API 的元件:
l ScreenBuilders 結合了流式工廠來生成標準的查詢、編輯和自定義介面。下面的例子展示瞭如何從一個介面開啟另一個介面。注意,build() 方法能返回正確型別的介面例項,不需要不安全的型別轉換。
l Screens 元件相對於 ScreenBuilders 來說提供了更底層的抽象,用來顯示和建立介面。並且提供了訪問 CUBA 應用程式中所有已開啟介面資訊的方法(Screens#getOpenedScreens),如果需要遍歷這些介面,這個方法很有用。
l Notifications和Dialogs 元件均提供了自描述的方便介面。這裡有個例子建立對話方塊和訊息通知:
資料繫結
CUBA 之所以可以做到後臺UI的快速開發,不僅僅是因為提供了可以生成大部分程式碼的視覺化工具,還因為提供了大量開箱即用的具有資料感知能力的元件。 這些元件只需要知道要使用哪些資料,其餘事情會自動管理。例如, 查詢列表、選擇器欄位、具有 CRUD 操作的各種網格等。
在版本 7 之前,資料繫結是通過稱為資料來源的物件實現的,資料來源包裝單個實體或實體集合、與資料感知元件繫結,然後響應資料感知元件的資料變化。 這種方法非常有效,但是是以一個整塊的方式實現的。 整塊石頭似的架構通常在可定製性方面會有問題。因此在 CUBA 7 中,這塊堅固的巨石被分成 3 個數據元件:
l Data Loader (資料載入器) 是 資料容器的資料提供者。 資料載入器不儲存資料,它們只是將所有必需的查詢引數傳遞給資料儲存,並將結果資料集提供給資料容器。
l Data container (資料容器) 保留載入的資料(單個實體或多個實體)並以響應式的方式將資料提供給資料感知元件:被包裝實體的所有更改都會暴露給相應的 UI元件,反之亦然,UI元件內的所有更改都會引起資料容器作出相應更改。
l Data context (資料上下文) 是一個強大的資料更改管理器,可跟蹤更改並提交所有已修改的 實體。 一個實體可以合併到一個數據上下文中,合併後會得到一個原始實體的副本,這個副本與原始實體有一個唯一但非常重要的區別:對副本實體及其引用的所有實體(包括集合)的所有修改都將被跟蹤、儲存和提交。
資料元件可以在介面描述符中宣告,也可以使用專門的工廠類 - DataComponents 以程式設計的方式建立。
其它
上面介紹了新的介面 API中最重要的部分,所以剩下的部分我簡要列出 Web客戶端層中的其他重要功能:
URL 歷史記錄和導航 。此功能解決了在 WEB 瀏覽器中具帶有“後退”按鈕的 SPA 應用程式存在的一個普遍問題,提供了一種簡單地為應用程式介面分配路徑的方法,同時使 API 能夠在URL中反映介面的當前狀態。
使用 Form 代替 FieldGroup 。 FieldGroup 是一個數據感知元件,用於顯示和修改單個實體的欄位。它在執行時推斷出用於顯示欄位的實際UI元件。也就是說,如果你的實體中有一個日期型別的欄位,它將使用 DateField 元件來顯示 。但是,如果你希望以程式設計方式使用此元件,則需要將此元件注入到介面控制器並手動將其轉換為正確的型別(在我們的示例中為DateField)。過了一段時間,可能會欄位型別更改為其他型別,這時應用程式就是崩潰。表單通過顯式宣告元件型別解決此問題。關於 Form 的更多資訊請參閱 這裡 。
顯著地簡化了第三方 JavaScript 元件的整合 ,可參考這個 文件 將自定義 JavaScript 元件嵌入到CUBA 應用程式中。
現在可以在 XML 介面描述中輕鬆定義 HTML/CSS屬性 ,也可以通過程式設計方式設定。詳細資訊請參閱 這裡 。
好了,以上只是關於 Studio 和偏前端的新功能介紹,下篇會介紹偏後端的新功能。