已經學了兩年的MVC了,但是有些概念還是很模糊,希望能在這裡找到答案。
我曾經看到有人說Controller不負責資料處理,全部交給我們的Service來處理,網頁前端傳回來是什麼資料類型,就直接把資料類型轉送到Service,然後由Service來處理;但又有另一個聲音說有時候會同時呼叫多個Service,如果在Controller就將物件封裝好了,就免於在Service的方法中多次封裝。
另外還有一個問題就是關於Controller與Service互動的問題。
我們為了前端的客戶互動良好,往往會透過Controller向前端傳回一些錯誤提示,例如使用者名稱已存在,使用者名稱和密碼不符等等。可是處理業務邏輯我們是放在Service層,那麼如果把一個login(String username,String password)方法的回傳值設定為boolean就無法回傳多種錯誤,但是如果回傳String類型,就需要設定一些基本的字典。
我自己“奇思妙想”,我在Service中通過拋出我自定義的一些RuntimeException,然後在Controller中通過TryCatch來處理不同的錯誤,但是我自己認為這種拋出異常的方式不妥。最近就陷入了迷茫,馬上就要開始做下一個專案了。希望各位能幫我解答一下迷惑。
感謝。
我們專案裡分了三層:
表現層:Spring MVC,負責接收Http請求、展現(回)結果、簡單校驗
App層:提供應用程式層的功能,例如匯入、匯出、複雜校驗
Domain層:處理業務邏輯,例如一些Service
呼叫順序是單項的:Controller->App->Domain
且感知關係為:Controller->App->Domain,即下層不感知上層
先回答你第一個問題:
你說的第一個問題我是否可以理解為,Http請求過來的參數不是Object,而是一堆基本型,但你的Service接收的參數是Object。正確的做法應該是,在Controller將「生」參數轉換成Object對象,然後呼叫Service。
為何這樣是正確的?因為Service屬於Domain層,裡面是業務邏輯,其接受的參數應該根據自己的需求而進行設計,不應該考慮Web層過來的參數是什麼,這樣才可以做到在不同場景下重複使用。
舉個例子,你的Service應該可以重複使用不同表現層環境下:
在一個Web程式中,使用者傳過來的參數是POST/GET形式給你的
在一個Web service程式中,使用者傳過來的參數是json或SOAP
在一個Swing程式中,使用者傳過來的參數就是字串
如果你將Service和具體某個表現層環境綁定,那麼其方法參數肯定不穩定,結果就導致無法重複使用。
同理,Service的回傳值也不應該和特定場景綁定。
在Spring MVC層面,Controller可以很方便的把參數轉換成Object,相關文件
第二個問題:
這個問題可以分為三個:
1)簡單的校驗例如參數長度限制、非空判斷等在哪裡做?
簡單校驗利用Spring MVC的自身提供的機製做,相關文檔,相關文檔
2)和Service本身的業務邏輯平行的校驗在哪裡做,例如用戶下單時判斷其是否帳號被禁用
我傾向於將這些邏輯校驗放在App層做,Controller調用App,App調用兩個不同的Service,將業務編織起來
3)和Service本身有關的業務邏輯校驗怎麼做
你舉的是登入的例子,用異常告知呼叫方(Controller)處理結果沒有任何問題。你也可以豐富Service的回傳值達到這個目的,不過需要注意的是,Service的回傳值的設計不能和表現層環境綁定,否則就不能復用了,這也就是為什麼@YaTou 提到了apache-shiro採用的是異常機制處理認證失敗,因為只有這樣才夠通用。
我覺得
Controller
不负责处理数据是正确的, 因为在spring-mvc
中Controller
是不能复用的, 但是如果你把业务逻辑抽象成Service
, 那么这个Service
就是可以重複使用的.至於你說的"在Controller就將物件封裝好了,就免於在Service的方法中多次封裝" , 沒太明白什麼意思, 你每個
Service
需要什么参数,Controller
就給什麼參數, 至於需要的參數是否需要封裝成對象就可以自己權衡了.你所說的
login(String username,String password)
, 你想抽象成Service
用异常处理处理多种不同的结果, 这个我觉得完全没有问题, 而且我觉得非常好啊, 很多认证框架都用的这种方式, 至少我看的apache-shiro
就是用異常處理認證失敗的不同情況的.第一個問題感覺沒有標準答案,具體情況具體分析,邏輯分層清晰易於維護就好。
第二個問題的話,你這裡給出的交互是隸屬於權限控制的,一般用filter、aop、代理、反射等等方式實現代碼收束都可以,異常也在這些集中控制的代碼里扔一次就好,直接在Controller裡硬編碼我反而覺得累贅。 Service這一層更多的是呼叫Dao層的方法來實現一些複雜的涉及多表的業務邏輯處理,事務也放在這一層(當然現在框架把這事兒都乾了),所以Service這一層一般不丟異常(參數驗證在Controller以及之前的層次都做掉了因此不出現業務相關異常,而Dao把資料庫相關的底層異常屏蔽了)。
當然這是個人看法,沒有定式,還是那句話,分層清晰易於維護就好。
1、Controller預設是單例的,但可用@Scope(value = "prototype")替換
2、登入可以回傳int啊,自己加個枚舉
3、規定是在Service層處理邏輯,要看業務的吧,程式碼冗餘度低些好,也好優化
大家觀點會不同,做開發更多的還是優化改,降低冗餘度,而不是必須怎麼做。 。 。
1,Controller應盡可能的不設計業務邏輯,只涉及交互
2,Service為可復用的業務邏輯
3,Controller為Service的上級調用方
4,你這個case可以在Service中返回固定的傳回值,在Controller層做判斷,並拋出你想對應的例外。
當然了這只是我們目前的做法,分享一下。 。 。
可以看看我這篇
AOP,MVC——Spring的學習以及對CodeIgniter的反思 /a/11...
建議邏輯放在service,我們最近在做分散式微服務架構,我們之前是放在controller層的,拆分的時候基本上全部重新,另外有些app需要使用的接口如果放在controller就無法共用,蛋疼吧?
封裝物件到底在Controller還是Service,還是要看具體的情況,個人認為如果是簡單的參數在Controller中進行封裝時是完全可以的,如果放到Service反而會顯得很冗餘,而且導致Service通用性變差;對於第二個問題,不容同意透過枚舉或不同的狀態碼來在Controller左做判斷拋出異常,完全可以自己定義一套異常處理機制,直接在Service層拋出,項目有針對此類別業務異常的處理機制,直接兩將Service的錯誤訊息和錯誤碼回復到View層,讓客戶端根據狀態碼和錯誤訊息作處理