日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長提供免費收錄網(wǎng)站服務(wù),提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

之前在Web開發(fā)框架推導(dǎo)一文中我們一步步的搭建了一個開發(fā)框架。

如何搭建合適的Web框架?

 

在當(dāng)時的情況下,還算滿足需求。但是隨著項目的逐漸完善,需求變更的頻度逐漸變得比新增需求的頻度高,原來框架的弊端越來越明顯,所以需要對框架進(jìn)行升級改進(jìn)。

我們先來看原來框架的問題,然后基于這些問題,來對框架進(jìn)行改進(jìn)。

原框架的問題

  • 代碼生成問題
  • 參數(shù)傳遞問題
  • Service層問題
  • 測試依賴問題
  • MApper.xml的問題

代碼生成問題

在原框架中,我們基于各種約束,編寫了一個代碼生成組件,通過這個組件,我們可以針對選中的表來生成Controller,Service,Model,Mapper等一系列的類,也就是說,只要建完表,就可以直接生成一套CRUD,直接就可以啟動并測試。這在項目初期看起來很美,但是在需求變動時,還是有很多的局限性。

首先,生成的代碼邏輯是固化的。如果稍微有些調(diào)整,就需要調(diào)整生成代碼的組件,然后重新打包,上傳到j(luò)ar倉庫,項目修改組件版本,再進(jìn)行代碼生成,整個流程過于繁瑣。

其次,為了方便代碼的生成,其實是做了不少妥協(xié)的:

  • 為了方便在修改表字段以后,能夠重新生成,很多類都抽象了一個基類用于操作Model字段。這些基類不能夠手動修改,因為每次生成都會覆蓋。這實際導(dǎo)致了類的數(shù)量的增多。
  • 生成的CRUD固化了,不能手動調(diào)整。如果生成的CRUD不滿足需求,不能直接在代碼上修改。只能拷貝一份進(jìn)行修改,因為再次生成時會覆蓋。這導(dǎo)致了代碼的冗余。
  • Param和Result委托了Model,這在Model發(fā)生改變時,能在編譯期就能知道對應(yīng)字段的調(diào)整。但是也引入了不少問題,我們在「參數(shù)傳遞問題」一節(jié)單獨討論。

參數(shù)傳遞問題

當(dāng)初為了便于代碼的生成,決定Param和Result都繼承Model,這導(dǎo)致了如下的一些問題:

  • 使得Param和Result都依賴了Model。但是Param和Result是視圖層模型,而Model是持久層模型,兩者的進(jìn)化度并不是一致的。但是現(xiàn)在的繼承關(guān)系導(dǎo)致了在默認(rèn)情況下視圖層模型的進(jìn)化需要和持久層同步,當(dāng)然你也可以手動調(diào)整Param和Result,但是這又導(dǎo)致了代碼生成的優(yōu)勢沒有了。
  • Param和Result通過委托的方式來設(shè)置字段,也就是說,它們實際是沒有字段的,通過getter和setter將值設(shè)置到了Model中。這就沒法使用lombok來簡化getter和setter,使得Param和Result代碼行數(shù)較多
  • 同時,對于swagger來說,有些注解需要基于字段,導(dǎo)致某些功能無法實現(xiàn)(例如:ModelAttribute),只能基于額外手段來處理(例如:需要通過ApiImplicitParams來實現(xiàn)字段文檔)。
  • CRUD都是基于同一個Param和Result,導(dǎo)致前端的接口會顯示很多無用的字段,加大前端理解接口的難度

Service層問題

Service層有如下問題:

  • Service層的職責(zé)過重,包括了事務(wù)處理、參數(shù)設(shè)置、業(yè)務(wù)邏輯
  • 導(dǎo)致Service中的代碼是面條代碼,不利于業(yè)務(wù)邏輯的理解
  • 同時事務(wù)注解是直接加在類上的,Spring的默認(rèn)事務(wù)機制會導(dǎo)致類似如下代碼的邏輯調(diào)用不會拋出期望的異常
// PostService
public String savePost(Post post) {
 postRepository.save(post);
 for(PostDiscuss discuss : post.getDiscuss()) {
 // 這里是抓不到RuntimeException異常的,會是一個TransactionRollBack的異常
 discussService.save(discuss);
 }
}
// discussService
public String savePost(PostDiscuss discuss) { 
 throw new RuntimeException("保存失敗");
}

測試依賴問題

核心的業(yè)務(wù)邏輯在Service中,測試還是需要依賴于Spring,當(dāng)項目越來越大時,啟動項目的時間越來越長,可能要1分鐘甚至更長。這就導(dǎo)致單元測試效率越來越低。

Mapper.xml的問題

在面試的時候,我經(jīng)常會問下面的一些問題:

  • JAVA里面接口的作用是什么?
  • Service、DAO為什么要編寫接口,再去實現(xiàn)這個接口?
  • 接口和實現(xiàn)在相同的模塊下,反正都要重新打包的。多寫個接口不是多寫了好幾行代碼嗎?
  • 和上面類似的問題,Mybatis里面,聲稱將sql獨立到了Mapper.xml文件中,使得可以不需要編譯直接修改sql。但Mapper.xml都是和Class放在一起的,改了還是需要重新打包,而且Mybatis是不能動態(tài)加載Mapper.xml的,那把sql獨立到XML里,到底有什么優(yōu)勢?

對于最后一個問題,我的答案是,對于大部分項目來說,沒什么優(yōu)勢項目易不易于部署、擴展,不在于你使用的框架,而在于你的設(shè)計

就以Mapper.xml來說,Mybatis將sql與代碼分離了,但是你在項目里還是將Mapper.xml和代碼放在同一個模塊下,那這個優(yōu)勢就沒有了。既然沒有這個優(yōu)勢,我們還有必要單獨寫Mapper.xml文件嗎?我的選擇是,那就不寫了,直接使用Mybatis提供的注解。

同時為了解決Service層對DAO層(這里也就是對Mybatis)的強依賴,對框架進(jìn)行了一些改進(jìn),解耦Service和DAO層。具體見下面的改進(jìn)方案。

框架改進(jìn)方案

為了解決上面這些問題,對框架進(jìn)行了如下調(diào)整:

  • 分離Param、Result和Model
  • 替換代碼生成
  • 獨立業(yè)務(wù)邏輯
  • Model層優(yōu)化

分離Param、Result和Model

上面已經(jīng)提到了Param、Result和Model強耦合會有很多問題,所以這里就將Param、Result和Model分離開。每個都是獨立的Bean,這就解決了上面幾個問題。但是引入了兩個新問題:

  • 首先,很明顯的,增加了手動編碼的量。當(dāng)一個表修改了字段,需要修改三個類甚至更多的類
  • 其次,增加了數(shù)據(jù)傳遞之間的代碼。即Param傳遞到Model,需要對字段賦值。如果一個字段一個字段的設(shè)值,會增加很多無聊的代碼。而使用反射的話會對性能有一些影響

那如何解決這兩個問題呢?首先,純手?jǐn)]肯定是不可能的。需要提供一些自動化手段。

對于賦值來說,Spring提供了BeanUtils來簡化處理,雖然是基于反射來設(shè)值的,但是對于現(xiàn)階段來說,這點性能損耗還是沒什么影響的。但是,BeanUtils對于不同類型的屬性不能進(jìn)行拷貝,假設(shè)我有一個Domain對象Book,里面有個字段Author,現(xiàn)在我要賦值給BookResult,其中有個字段AuthorResult,此時BeanUtils是無法賦值的。所以我編寫了一個基于Gson的工具類來處理,性能測試10000次的屬性拷貝BeanUtils需要500多毫秒,基于Gson的工具類只需要300毫秒左右。

對于表字段的生成,如果使用的是IDEA的話,IDE默認(rèn)提供了一個腳本,可以從表來生成POJO!我們可以使用這個腳本來生成Model,然后將字段拷貝到Param和Result中,來簡化字段的編寫。我對這個腳本進(jìn)行了修改,以符合項目需求。主要增加了lombok的支持,新增了類注釋和字段注釋。

替換代碼生成

對于上面代碼生成組件的問題,我調(diào)整了代碼生成的方式。不再基于組件來生成,而是基于IDEA本身的FileTemplate、LiveTemplate以及Scripted Extensions來進(jìn)行生成。雖然這樣的方式,不能夠一次性生成多個文件,但是由于生成邏輯基本是一次性的,所以影響不是很大。在初次生成代碼時,代碼生成組件的效率是高于FileTemplate、LiveTemplate以及Scripted Extensions的組合,但是后期調(diào)整的靈活性,明顯是FileTemplate、LiveTemplate以及Scripted Extensions的組合要高于代碼生成組件的:

  • 首先,當(dāng)文件結(jié)構(gòu)調(diào)整時,只需要修改FileTemplate,并將配置文件導(dǎo)出給項目組成員即可。
  • 同樣的,當(dāng)LiveTemplate調(diào)整時,也只需要修改對應(yīng)的LiveTemplate,并將配置文件導(dǎo)出給項目組成員即可。
  • 其次,想生成哪個文件,只要針對這個文件生成即可
  • 第三,通過FileTemplate生成完整的文件后,可以通過LiveTemplate快速的進(jìn)行模塊化的編碼
  • 最后,F(xiàn)ileTemplate可以設(shè)置為項目級別,即每個項目可以有獨立的FileTemplate

具體的操作流程,在下面演示。

獨立業(yè)務(wù)邏輯

針對Service和測試的問題,將原來的Controller、Service和Model三層,拆分為四層:

  • Controller負(fù)責(zé)前端數(shù)據(jù)的接收和返回,以及統(tǒng)一異常處理
  • Service負(fù)責(zé)事務(wù)以及Domain層邏輯的組裝。這里就不會出現(xiàn)事務(wù)嵌套問題,也就不會導(dǎo)致抓不到期望的異常的問題
  • Domain負(fù)責(zé)業(yè)務(wù)邏輯
  • Model負(fù)責(zé)數(shù)據(jù)持久

這樣Service的職責(zé)減輕了,同時不再有事務(wù)嵌套的問題。

Model層優(yōu)化

上面提到,框架中最終放棄了Mapper.xml,轉(zhuǎn)而使用Mybatis的注解來實現(xiàn)持久化操作。改用注解,規(guī)避了XML代碼的編寫,但是并沒有解決框架對Mybatis的強依賴。所以這里在Domain中新增了Repository接口層,此層用于定義Domain的持久化操作,而Model層中對Repository進(jìn)行實現(xiàn),這里的實現(xiàn)就是Mybatis實現(xiàn)。這樣做有兩個好處:

  • 依賴倒置:原來是Domain依賴Model層,而現(xiàn)在是Model層依賴Domain層,這樣當(dāng)我要把Mybatis替換掉時,Domain完全無感知。
  • 獨立測試:因為現(xiàn)在Domain不依賴于其它任何層,所以可以脫離數(shù)據(jù)庫和容器來進(jìn)行測試。使得測試的效率不會隨著項目的開發(fā)而越來越低
如何搭建合適的Web框架?

 

框架改進(jìn)細(xì)節(jié)

現(xiàn)在已經(jīng)知道了,如何對框架進(jìn)行改進(jìn),我們現(xiàn)在就開始著手進(jìn)行改造。其實主要的改造是對代碼生成方式的改造,也就是編寫FileTemplate、LiveTemplate和ScriptedExtensions。下面對這三個功能進(jìn)行簡單的說明,先說ScriptedExtensions。

Scripted Extensions

先來解釋一下,什么是Scripted Extensions。我們都知道,現(xiàn)在的IDE都是插件式的,也就是說,我們可以通過開發(fā)商提供的插件開發(fā)包來開發(fā)插件,擴展現(xiàn)有的IDE功能。但是編寫插件需要特定的開發(fā)環(huán)境,如果是一個很簡單的功能,還要費勁去搭開發(fā)環(huán)境,挺麻煩的。所以IDEA提供了Scripted Extensions,可以理解為一個簡化版的插件,就是可以通過腳本來擴展IDE功能。

IDEA提供了Database功能,可以連接數(shù)據(jù)庫進(jìn)行相關(guān)操作。當(dāng)你連接了數(shù)據(jù)庫,在表上右擊時,可以看到Scripted Extensions這個選項,里面有一個功能是可以基于表來生成POJO的groovy腳本。

但是功能比較low:

  • 包名是寫死的:com.sample
  • 沒有生成table注釋
  • 沒有基于lombok來簡化getter和setter

不過好在,我們能基于這個腳本來自行修改,在剛才的Scripted Extensions菜單里,有個Go to Scripts Directory選項,點擊后,可以進(jìn)入腳本目錄。

如何搭建合適的Web框架?

 

直接對這個groovy文件Ctrl+c,Ctrl-v,復(fù)制一份,重命名一下,基于這個腳本進(jìn)行修改即可。具體怎么修改,按照自己的需求來,里面主要就是根據(jù)表信息對String的拼接而已。

FileTemplate

FileTemplate是IDEA提供的生成文件的模板,你在點擊菜單的File->New...以后,出現(xiàn)的各種文件,都是基于FileTemplate來實現(xiàn)的。我們自定義的Controller、Service、Domain等類,都可以通過FileTemplate來簡化創(chuàng)建。

具體使用方式為,按下Ctrl-Alt-S呼出設(shè)置菜單,點擊Editor->File And Code Template,在里面新增Template即可。

如何搭建合適的Web框架?

 

幾點說明:

  • 下面的描述中列出了默認(rèn)的一些參數(shù)以及作用
  • 你也可以自定義變量,自定義的變量如果沒有賦值,在創(chuàng)建時會有輸入框提示輸入內(nèi)容
  • 模板是基于Velocity的,所以如果你熟悉 Velocity,那就可以直接上手
  • Enable Live Template選項是在FileTemplate激活LiveTemplate變量,不過需要使用#[[]]#包裹。但是對于創(chuàng)建Java,這個功能有bug,并不能定位到需要的位置,所以暫時沒使用

創(chuàng)建完成后,就可以在New菜單中看到這個模板了。

LiveTemplate

LiveTemplate實際就是CodeSnippet。創(chuàng)建方式和FileTemplate類似。按下Ctrl-Alt-S呼出設(shè)置菜單,點擊Editor->Live Template,在里面新增Template即可。

如何搭建合適的Web框架?

 

幾點說明:

  • 這里的變量是使用$$包裹
  • 每個變量就是一個占位符,在使用tab展開后,可以手動輸入值
  • 右下角的Edit variables,用于對變量賦值,IDEA提供了一些方法、也可以設(shè)置默認(rèn)值
  • 下面的change鏈接,可以選擇LiveTemplate生效的位置,比如只在Java類聲明處生效

編碼流程

創(chuàng)建了上面的幾個模板后,編碼流程如下:

  • 在表上右擊,通過Scripted Extensions來生成Model
  • 通過FileTemplate來快速生成Controller、Service、Domain等類
  • 通過LiveTemplate來快速編寫代碼

總結(jié)

本文通過對原框架問題的梳理及解決,來對框架進(jìn)行升級改造,以適應(yīng)項目的發(fā)展和推進(jìn)。

分享到:
標(biāo)簽:框架 Web
用戶無頭像

網(wǎng)友整理

注冊時間:

網(wǎng)站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨大挑戰(zhàn)2018-06-03

數(shù)獨一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運動步數(shù)有氧達(dá)人2018-06-03

記錄運動步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績評定2018-06-03

通用課目體育訓(xùn)練成績評定