在Spring框架中最常見的幾個(gè)注解
@Controller, @Service, @Component, @Repository
其中@Component是一種通用名稱,泛指任意可以通過Spring來管理的組件吼野,@Controller, @Service, @Repository則是一種特定的組件含末,通常用來表示某種特定場(chǎng)合下的組件嫡霞,比如@Repository用來表示倉庫(數(shù)據(jù)層族铆,DAO)叶堆,并且Spring 框架會(huì)根據(jù)這種應(yīng)用場(chǎng)景做些定制包斑,比如@Repository同時(shí)具備了自動(dòng)化的異常轉(zhuǎn)換。類似的渠欺, @Service則用來表示服務(wù)層相關(guān)的類妹蔽, @Controller則用來表示展示層(presentation)的類。
那Service是什么呢挠将?
Service 表示了在軟件分層設(shè)計(jì)中的Service層胳岂,用來連結(jié)數(shù)據(jù)層(DAO)和展示層(Presentation)。
為什么要在DAO層上加一層Service呢舔稀?
在某些簡(jiǎn)單的應(yīng)用中乳丰,DAO層的功能和Service的功能很接近,甚至初學(xué)者會(huì)覺得Service層做的事情和DAO層都一樣镶蹋,那為啥還要將Service層單獨(dú)拿出來做一遍呢?而且赏半,很多場(chǎng)景下贺归,Service層和DAO層同時(shí)存在,往往會(huì)增加代碼復(fù)雜度断箫,編碼工作量拂酣,寫的不好甚至?xí)斐苫煜?/p>
通常來說,DAO層應(yīng)盡力保持簡(jiǎn)單仲义,其功能僅僅是提供了數(shù)據(jù)庫的連接婶熬,以及最簡(jiǎn)單的增刪改查(Crud),有時(shí)還需要做些抽象埃撵,以此來連接使用不同技術(shù)的數(shù)據(jù)庫赵颅。除此之外,任何業(yè)務(wù)相關(guān)的操作都應(yīng)該放到Service層暂刘,即Service層用來編寫業(yè)務(wù)邏輯饺谬,即操作從DAO層讀取的數(shù)據(jù)谣拣,或者將處理好的數(shù)據(jù)給DAO層,當(dāng)使用Domain Driven Design時(shí)森缠, 這兩個(gè)類通常會(huì)放到同一個(gè)Domain(包)中,即便在簡(jiǎn)單的應(yīng)用中贵涵,他們的代碼可能極其類似列肢,但是仍應(yīng)該分別對(duì)待恰画。而不是跳過service層(service)直接去使用DAO層(repository)來放業(yè)務(wù)邏輯數(shù)據(jù)例书。
這樣帶來的好處帶來更好的模塊化結(jié)構(gòu),有便于后期的擴(kuò)展和維護(hù)决采,比如更換數(shù)據(jù)庫實(shí)現(xiàn)時(shí)自沧,我們僅僅需要處理DAO層的內(nèi)容就好了。并且树瞭,當(dāng)業(yè)務(wù)邏輯比較復(fù)雜的時(shí)候,比如有很多報(bào)告要出的時(shí)候晒喷,Service層就提供了一個(gè)很好的空間來實(shí)現(xiàn)這些代碼。
其次衣盾,在web應(yīng)用開發(fā)中爷抓,使用Service層可以將web類的活動(dòng)限制在controller中,這樣可以獨(dú)立的測(cè)試service層
另外蓝撇,還有一種情況,就是當(dāng)應(yīng)用極其復(fù)雜渤昌,需要同時(shí)使用多種數(shù)據(jù)庫時(shí),將從DAO中獲取數(shù)據(jù)的動(dòng)作放到一起可以減少數(shù)據(jù)庫的操作迈窟,并且可以保證數(shù)據(jù)的一致性忌栅。同時(shí)Service可以嵌套,因此如果需要使用不同的數(shù)據(jù)庫時(shí)狂秘,可以在service中指定。
在Service中也可以放一些通知類的操作破衔,比如發(fā)送郵件等钱烟,這樣也可以保持controller的整潔嫡丙。
還有一個(gè)潛在的好處是安全性读第,當(dāng)使用service層包裹DAO層后曙博,數(shù)據(jù)庫的鏈接是被service層保護(hù)起來的,這樣如果客戶端被某種情況攻陷父泳,其只能使用service層提供的有限數(shù)據(jù)吴汪,而無法直接攻擊數(shù)據(jù)庫
另外,在Spring 框架中漾橙,security也是在Service層實(shí)現(xiàn)的。根據(jù)上面的邏輯脾歇,我們?cè)趯?shí)際開發(fā)中淘捡,應(yīng)該不去實(shí)現(xiàn)自己的DAO層,而是使用Spring Data JPA案淋,因?yàn)镾pring Data JPA已經(jīng)實(shí)現(xiàn)了DAO層险绘。
這種寫法常見的問題有啥?
最常見的寫法(或者是錯(cuò)誤的寫法)有以下幾種
1瓣距、面向領(lǐng)域的模型對(duì)象僅僅用來存儲(chǔ)應(yīng)用中的數(shù)據(jù)代咸,換句話說,是不太符合domain model 設(shè)計(jì)的
2逻杖、處理模型數(shù)據(jù)的業(yè)務(wù)邏輯分散在service層
3思瘟、每個(gè)entity都有對(duì)應(yīng)的service類
這樣寫的原因很大程度來源于上面的分層理論,我們確實(shí)將應(yīng)用分成了展示層(web layer)滨攻,服務(wù)層(service layer)蓝翰,數(shù)據(jù)層(repository/dao)女嘲,但是實(shí)際后果卻是一個(gè)極其龐大的service層,這種寫法可以算是一個(gè)面向過程開發(fā)的代碼(procedural code)爆雹, 而不是面向?qū)ο箝_發(fā)媒至。好處是簡(jiǎn)單,當(dāng)業(yè)務(wù)不復(fù)雜時(shí)拒啰,確實(shí)沒有必要使用一個(gè)龐大的面向?qū)ο箝_發(fā)框架(domain driven design)。
一個(gè)責(zé)任并不明確的service層主要有以下問題
1剩失、業(yè)務(wù)邏輯分散在service層中册着,當(dāng)我們需要確認(rèn)或者檢查某個(gè)業(yè)務(wù)邏輯時(shí),可能要在多個(gè)service類中尋找演熟,也許并不那么容易司顿,另外如果同樣的業(yè)務(wù)邏輯在多個(gè)service類中用到時(shí),那么可能會(huì)存在大量的重復(fù)代碼大溜,這種重復(fù)代碼對(duì)于維護(hù)人員來說就是惡魔。
2座云、在service層中付材,每個(gè)entity都有對(duì)應(yīng)的service類時(shí),service層會(huì)有過多的依賴厌衔,甚至是循環(huán)依賴關(guān)系,而不是由松散耦合的service類構(gòu)成service層裸弦,理想中的service層應(yīng)該是由具有單一責(zé)任的service類構(gòu)成,并且這些service類具有松耦合關(guān)系理疙,如果不是這樣的service層,將難以理解砖顷,維護(hù)和重用赃梧。
主要的解決方法是
1、將與entity相關(guān)的業(yè)務(wù)邏輯統(tǒng)一放到領(lǐng)域模型對(duì)象相關(guān)的類中物咳,即所謂的domain service中蹄皱。這樣做的好處時(shí),傳統(tǒng)概念中的service層僅僅處理應(yīng)用相關(guān)的業(yè)務(wù)邏輯巷折,即作為Application Service。 然后domain service中處理domain 內(nèi)的業(yè)務(wù)邏輯油吭。業(yè)務(wù)邏輯將按照domain和application的方式分開署拟,容易定位和維護(hù)。傳統(tǒng)意義上的applicationservice層將變得整潔芍阎。
2缨恒、在domain service中我們將按照entity來編寫對(duì)應(yīng)的service轮听,這些都是特定的service,很小血巍,僅僅面對(duì)很專一的功能。舉例來說柿隙,如果應(yīng)用中的某個(gè)service提供person類的crud, 同時(shí)還提供用戶帳號(hào)的操作禀崖,那么我們應(yīng)該將person的crud單獨(dú)放到一個(gè)service中,然后將用戶帳號(hào)相關(guān)的操作放到另一個(gè)service中艺晴。
所有這些分層方式都是為了解決應(yīng)用從小項(xiàng)目成長(zhǎng)為大項(xiàng)目時(shí)可能遇到的隱患掸屡,代價(jià)是在項(xiàng)目還小時(shí),增加了項(xiàng)目的復(fù)雜度狈究,往往一句代碼就能搞定的事情盏求,卻要拆到三個(gè)類中去。但是太多的實(shí)際例子表明风喇,如果沒有好的架構(gòu),當(dāng)小項(xiàng)目膨脹到一定程度時(shí)还蹲,往往是無法維護(hù)的耙考,只能全部推倒重寫。
在Domain Driven Design中如何區(qū)分各種Service斗遏?
在DDD中鞋邑,service有三種類型
Domain Service
Domain Service: 用于放置領(lǐng)域?qū)ο笙嚓P(guān)的業(yè)務(wù)邏輯,這些業(yè)務(wù)邏輯通常并不適合放到entity中枚碗,也不是常見到的CRUD(這些應(yīng)該放到Repository), 將Domain Service 和Domain Objects放到一起是合理的遵堵,它們都是關(guān)注于domain相關(guān)的業(yè)務(wù)邏輯。在Domain Service中可以使用注入repository的方式來使用entity對(duì)應(yīng)的repository锡足。
舉一個(gè)例子:
一個(gè)圖書館有三個(gè)entity:Book壳坪, Client,Inventory弥虐, 當(dāng)把一本書借給一個(gè)客戶時(shí),就對(duì)應(yīng)了一個(gè)Domain Service珠插。在一個(gè)例子颖对,在Eric Evans的《Domain Driven Design》書中,轉(zhuǎn)賬服務(wù)(FundsTransferService)也是一種domain service顾患,它涉及到帳號(hào)BankAccount个唧,但是并不適合放到BankAccount中。
Application Service
Application Service: 用于為應(yīng)用外的client或consumer提供應(yīng)用級(jí)別的服務(wù)徙歼,比如一個(gè)外部客戶端(程序)需要使用某個(gè)entity的CRUD時(shí),這些服務(wù)程序放到Application Service桨螺。
Application Service通常會(huì)使用Domain Service和repository來處理外部的請(qǐng)求酿秸。常見的場(chǎng)景是,從repository中拿到一些domain objects辣苏, 然后執(zhí)行某些操作考润,在將其放回repository(或者不放)读处, Application Service對(duì)應(yīng)著大部分用戶使用場(chǎng)景,在寫一個(gè)應(yīng)用時(shí)罚舱,可以先從Application service寫起绎谦,這樣可以很好界定應(yīng)用的功能和范圍粥脚。repository雖然可以在某些場(chǎng)景下注入到domain service中,但是更常見的是注入到applicatinoservice中冤留。
Infrastructure service
還有一種Infrastructure service:用于抽象一些技術(shù)問題树灶,比如消息隊(duì)列,郵件服務(wù)