陳剛
關(guān)鍵詞:SpringBoot;Java反射解耦;Python模型;Mars3D框架;Ajax異步通信
0 引言
現(xiàn)狀:目前,全國各地水庫系統(tǒng)的信息化程度參差不齊,盡管所有系統(tǒng)的安全級別均較高,且必須在獲得授權(quán)的情況下離線完成系統(tǒng)部署,甚至不允許隨意調(diào)試。一些水庫的數(shù)據(jù)存儲在基于XML格式的古老WebService中,而另一些則建立了數(shù)據(jù)中心平臺,需要通過調(diào)用平臺API來獲取數(shù)據(jù)。還有的水庫引入了Kafka技術(shù),須建立Kafka消費端以獲取數(shù)據(jù)。因此,不同的生產(chǎn)環(huán)境對開發(fā)的需求各異,這部分開發(fā)要求能獨立于整個項目之外,同時又能與原有項目互相整合調(diào)用,這就要求設(shè)計出一個低耦合的系統(tǒng)調(diào)用架構(gòu),以實現(xiàn)在不同生產(chǎn)環(huán)境下的開發(fā)。
已有項目:現(xiàn)存的已有項目采用了SpringBoot+MySQL+JPA+Thymeleaf+Bootstrap的技術(shù)選型,這是一個前后端不分離的項目,要求在此基礎(chǔ)上擴展功能以適配不同的水庫生產(chǎn)環(huán)境。這個前提使得項目不能重新開始,需要在原有項目中進行拆解和分割,既要保證開發(fā)進度,又能將功能模塊獨立出來,這是一個關(guān)鍵點。需求:新的需求要求能夠在任意生產(chǎn)環(huán)境下獲取水庫數(shù)據(jù);新增GIS可視化系統(tǒng);新增新水庫的AI模型(Python程序)。
難點及關(guān)鍵技術(shù):
1) Java調(diào)用獨立jar包,以實現(xiàn)數(shù)據(jù)的抽取并持久化到數(shù)據(jù)庫。
2) Java調(diào)用Python編寫的模型算法,以獲得結(jié)果并繪制可視化圖形。
3) 將基于Vue 的Mars3D 開源GIS 框架整合到SpringBoot中。
1 開發(fā)框架簡介
SpringBoot:SpringBoot 是一種后端框架,它采用特定的配置方式,并通過內(nèi)嵌的Tomcat服務(wù)器,直接將項目打包成jar 包,從而簡化項目的部署工作[1]。SpringBoot框架采用MVC三層結(jié)構(gòu),其中MVC包括3 個模塊:模型層(Model) 、視圖層(View) 、控制層(Con?troller) [2]。此外,通過配置Maven工具來管理大量的項目資源,可以有效解決項目資源管理的問題。
Thymeleaf:Thymeleaf 是一個面向Web 服務(wù)器端的現(xiàn)代化Java模板引擎。與傳統(tǒng)的Java模板引擎不同,Thymeleaf可以直接被瀏覽器加載,以展示靜態(tài)頁面效果。當通過Web應(yīng)用程序訪問時,Thymeleaf會根據(jù)對應(yīng)的標簽屬性進行動態(tài)渲染。SpringBoot推薦使用Thymeleaf作為模板引擎[3]。
Python:Python是一種面向?qū)ο蟮慕忉屝驼Z言,具有動態(tài)數(shù)據(jù)類型特性。它是一種高級通用編程語言,相比其他語言,Python更便于實現(xiàn)原型開發(fā)。
Vue3:Vue是一款由國內(nèi)開發(fā)者尤雨溪研發(fā)的用于搭建用戶界面的框架。Vue采用自底向上增量開發(fā)的設(shè)計方式,提供了豐富的組件庫,支持獨立開發(fā)。結(jié)合使用Vue生態(tài)系統(tǒng)支持的庫和單文件組件,Vue 可以為復(fù)雜的單頁應(yīng)用程序提供支持[4]。此外,通過Vue,前端更便于調(diào)用第三方工具(如Web API) 。
此外,開發(fā)過程中還使用到了MySQL、SpringData JPA、Bootstrap、WebService、PostgreSQL等技術(shù)。
2 技術(shù)選型
本次項目開發(fā)采用前后端一體化開發(fā),并配合使用Ajax 技術(shù),其技術(shù)選型包括:SpringBoot、MySQL、Spring Data JPA、Thymeleaf、Bootstrap、Vue和Mars3D。
2.1 為什么不選擇前后端分離
2.1.1 基于舊項目上新增功能
選擇前后端分離的優(yōu)勢在于前后端可以相互獨立開發(fā),只需要在接口規(guī)范上做到統(tǒng)一就可以加速開發(fā)過程。然而,這種方式適合新項目的全新設(shè)計,不適合舊項目的改造。在現(xiàn)有的前后端不分離的項目中增加新功能時,既要保證開發(fā)進度,又要做到盡可能低耦合的設(shè)計。在代碼重構(gòu)的環(huán)節(jié),應(yīng)該采用敏捷開發(fā)的思路,逐漸向解耦的方向演化。
2.1.2 服務(wù)器資源限制
服務(wù)器的資源分配是按照單個項目進行的,如果改為前后端分離,就需要在原有MySQL服務(wù)和Spring?Boot服務(wù)的基礎(chǔ)上開啟NginX長期服務(wù)。
2.1.3 項目進度約束
項目進度是一個重要的限制因素。從前后端不分離的模式轉(zhuǎn)變?yōu)榉蛛x模式,將涉及整個設(shè)計的變動和大量的代碼修改,這可能導(dǎo)致項目完成的預(yù)期時間難以預(yù)測。
2.2 為什么不使用Python+Flask 選型
與上述2.1.2點類似,盡管Python+Flask能獨立啟動Web服務(wù)并長期占用Web資源,但實際上調(diào)用模型算法的頻率較低,多數(shù)情況下是按需調(diào)用。因此,為了優(yōu)化資源利用,選擇按需調(diào)用而非常駐Web服務(wù)的方案。
2.3 為什么要調(diào)用獨立Jar 實現(xiàn)數(shù)據(jù)抽取
鑒于不同水庫的信息化程度各不相同,數(shù)據(jù)存儲方式也有所差異,因此面對不同的水庫需要進行單獨開發(fā)。這種開發(fā)方式不會影響原有項目,相對較為獨立。因此,可以采用獨立的jar程序來獲取數(shù)據(jù),并將其存儲在原有項目的數(shù)據(jù)庫中。在實際開發(fā)中,會采用反射機制,并在原有項目中設(shè)計好接口,以實現(xiàn)主動調(diào)用獨立jar(或class) 進行數(shù)據(jù)傳遞。
3 架構(gòu)設(shè)計分析
3.1 反射解耦原理與實踐
3.1.1 原理
在面對不同的數(shù)據(jù)獲取環(huán)境時,編寫?yīng)毩⒌膉ar 程序進行數(shù)據(jù)抽取,這樣可以實現(xiàn)獨立開發(fā),而不會影響到原有項目的代碼。然而,在調(diào)用這些jar程序時,需要一個通用的解決方案來實現(xiàn)對不同jar程序的調(diào)用。這就要求每個數(shù)據(jù)抽取程序設(shè)計出相同的調(diào)用方法和參數(shù)。根據(jù)設(shè)計模式原則中的“依賴倒置原則”(面向接口而不是具體實現(xiàn)),雖然沒有直接使用接口類型,但在反射機制的實踐中,動態(tài)調(diào)用類方法可以被理解為面向接口。例如,定義如下類結(jié)構(gòu):package com.p1; class App{public void run(){}}。每個數(shù)據(jù)抽取程序被劃分到不同的包中,它們具有相同的類名和方法,但內(nèi)部實現(xiàn)邏輯完全不同。
為了能夠在運行時加載每個獨立的數(shù)據(jù)抽取程序(既可以是jar文件,也可以是class文件),這里以class為例,通過Class.forName“( 包名.類名”)的方式加載class。如果加載成功就執(zhí)行,加載失敗則通過異常捕獲順序加載下一個。由于加載class時輸入的包名和類名屬于字符串,更容易提煉為參數(shù),甚至可以把包名和類名作為數(shù)據(jù)庫中的配置項通過DAO層獲取,從而實現(xiàn)徹底解耦。這樣,新的數(shù)據(jù)抽取程序加入時,完全不需要修改任何主程序代碼,而只需要在數(shù)據(jù)庫中配置全限定名即可。
3.1.2 實踐
已知的數(shù)據(jù)獲取方式包括:從WebService 獲取XML 格式數(shù)據(jù)、從數(shù)據(jù)中心平臺API 獲取數(shù)據(jù)、從PostgreSQL數(shù)據(jù)庫獲取數(shù)據(jù)、從Kafka消費端獲取數(shù)據(jù)以及從Excel文件中獲取數(shù)據(jù)等。
對于從WebService獲取XML格式數(shù)據(jù)的情況,借助JDK自帶的工具wsimport生成WebService客戶端,將功能調(diào)用代碼放入com.core.extract.webservice.Cli?ent類中。部分代碼參考如下:
3.2 系統(tǒng)結(jié)構(gòu)功能設(shè)計
在設(shè)計整個系統(tǒng)時,重點放在功能的獨立性和耦合性上,旨在減少各功能模塊之間的依賴程度,確保系統(tǒng)的易維護性和可擴展性。因此,系統(tǒng)被劃分為以下幾個關(guān)鍵模塊:
1) 數(shù)據(jù)抽取模塊:負責(zé)獲得實時數(shù)據(jù)。
2) 數(shù)據(jù)導(dǎo)出模塊:處理數(shù)據(jù)計算結(jié)果,生成數(shù)據(jù)報表,并負責(zé)數(shù)據(jù)的導(dǎo)出等功能。
3) 數(shù)據(jù)處理模塊:作為系統(tǒng)的核心模塊,它是主要的交互區(qū)域。
通過這種模塊化的設(shè)計,系統(tǒng)的結(jié)構(gòu)變得清晰,每個模塊的職責(zé)明確,便于未來根據(jù)需求進行相應(yīng)的功能擴展或調(diào)整。
首先,數(shù)據(jù)抽取模塊構(gòu)成了系統(tǒng)運行的基礎(chǔ)部分,其主要職責(zé)是從生產(chǎn)環(huán)境中獲取實時數(shù)據(jù)。該模塊與各種數(shù)據(jù)庫、平臺接口、Kafka服務(wù)等數(shù)據(jù)源建立連接,定時或?qū)崟r抽取所需數(shù)據(jù),并將其傳輸?shù)较到y(tǒng)內(nèi)部的MySQL數(shù)據(jù)庫表中,為后續(xù)的數(shù)據(jù)處理和導(dǎo)出提供數(shù)據(jù)基礎(chǔ)。數(shù)據(jù)抽取模塊包括:WebService、平臺API、Kafka、PostgreSQL、Excel及其他數(shù)據(jù)源。
其次,數(shù)據(jù)導(dǎo)出模塊負責(zé)將數(shù)據(jù)處理結(jié)果以多種形式導(dǎo)出,包括數(shù)據(jù)報表、數(shù)據(jù)文件、報警平臺消息推送等。該模塊提供了豐富的導(dǎo)出選項,以滿足不同用戶的需求,并保證了數(shù)據(jù)的靈活性和可擴展性。目前,數(shù)據(jù)導(dǎo)出模塊包括:推送平臺API、報警平臺、Ex?cel和報表。
最后,數(shù)據(jù)處理模塊是整個系統(tǒng)的核心模塊,也是用戶使用頻率最高的功能區(qū)域。該模塊提供了實時預(yù)報、歷史預(yù)報、洪水歸檔、模型調(diào)用、可視化呈現(xiàn)、Echarts、GIS等功能。
3.3 項目模塊組織代碼
原項目為單個Module實現(xiàn),內(nèi)部包含MVC三層結(jié)構(gòu)。然而,新增的功能中包含相對獨立的GIS可視化系統(tǒng),主要用于地理信息呈現(xiàn)。因此,可以將GIS前端及其交互的控制器層剝離成為獨立的Module。代碼組織的邏輯是橫向切分子系統(tǒng),縱向切分代碼調(diào)用層次。因此,原項目被重構(gòu)為包含若干Module的大項目,分為核心功能模塊群(由多個縱向切割的模塊,例如控制器層、域?qū)ο髮印?shù)據(jù)訪問層等)和獨立模塊(橫向切割,模塊內(nèi)部包含MVC三層結(jié)構(gòu),形成若干相對獨立的子系統(tǒng)模塊)。
3.4 系統(tǒng)與Python 模型對接設(shè)計
使用Python語言,通過構(gòu)建LSTM模型,形成適應(yīng)某水庫流域特點的智能洪水預(yù)報方案。該方案采用歷史洪水數(shù)據(jù)進行模擬預(yù)報,以檢驗預(yù)報方案的效果。同時,利用氣象水文耦合技術(shù),對未來水庫入庫洪水過程進行實時滾動預(yù)報。為實現(xiàn)Java與Python 的混合編程,特別是將Java 作為主控語言,需要在Java程序中以某種方式訪問Python腳本代碼[5]。Java 程序通過其命令行函數(shù)執(zhí)行Python命令,以高效獲取數(shù)據(jù),而Python執(zhí)行的結(jié)果則通過Java對象直接獲取并返回給控制器。
// 為了消除硬編碼,下面采用了變量,cmd[]數(shù)組中的元素分別是命令行的命令和參數(shù),元素之間是空格分隔符
此時,變量text保存了模型運算的結(jié)果,并在內(nèi)存中直接由控制器返回給前端,用于繪制可視化圖形。這種調(diào)用方式使得數(shù)據(jù)無須落地,可直接調(diào)用,并將運算結(jié)果直接返回。
3.5 系統(tǒng)與GIS 純前端項目對接設(shè)計
前端采用Mars3D 開源框架實現(xiàn)快速開發(fā)。Mars3D基于Vue3+Vite構(gòu)建,屬于純前端項目。在與SpringBoot項目對接時,需要使用異步通信(Mars3D框架內(nèi)置Axios) ,這要求后端提供JSON 格式的、遵循REST風(fēng)格的控制器接口。然而,原項目使用Thyme?leaf引擎渲染HTML文件,控制器采用@Controller 注解,方法返回視圖名稱。因此,在對接過程中,需要創(chuàng)建專門針對GIS的控制器,并使用SpringBoot的@Rest?Controller注解。
關(guān)鍵操作包括在后端部署以確保能正常運行。前端使用命令“npm run build”進行打包,隨后將打包得到的dist目錄下的文件復(fù)制到后端的static目錄中。需要注意的是,static目錄默認位于SpringBoot項目內(nèi)部,在將SpringBoot項目打包成jar文件時,static目錄會被一同打包。如果static 目錄中的文件內(nèi)容有變動,須重新打包。為便于部署,可以通過添加額外配置使static目錄從SpringBoot項目中獨立出來,這樣就無須因靜態(tài)資源的變化而重新打包。要使SpringBoot 引用外部資源,可在SpringBoot 的默認application.properties文件中添加關(guān)鍵代碼:
# 實際路徑根據(jù)實際情況修改
spring. web. resources. static-locations=file: D:/re?sources/static
4 結(jié)束語
隨著信息技術(shù)的快速發(fā)展,集成多語言和多框架的開發(fā)已成為跨專業(yè)和跨領(lǐng)域合作的主要趨勢。本項目建立在現(xiàn)有系統(tǒng)的基礎(chǔ)上,分析了新的需求,明確了項目開發(fā)的難點和關(guān)鍵技術(shù),提出了符合項目實際需求的架構(gòu)設(shè)計,并在新架構(gòu)中實現(xiàn)了各個模塊的解耦和集成。特別是,使用反射機制極大地減少了項目功能間的依賴關(guān)系,實現(xiàn)了功能動態(tài)加載的靈活性設(shè)計。
項目開發(fā)的首要任務(wù)是確保按時交付。在系統(tǒng)運行資源充足、開發(fā)時間充裕及技術(shù)儲備充分的情況下,可以實現(xiàn)更優(yōu)的設(shè)計方案。前后端分離+反射機制的應(yīng)用將更好地適應(yīng)當前的需求。