工作中,少不了要定義各種介面,系統整合要定義介面,前後台掉呼叫也要定義介面。介面定義一定程度上能反應程式設計師的編程功底。列舉一下工作中我發現大家容易出現的問題:
1. 返回格式不統一
同一個介面,有時候返回陣列,有時候返回單個;成功的時候返回物件,失敗的時候返回錯誤資訊字串。工作中有個系統整合就是這樣定義的介面,真是辣眼睛。這個對應程式碼上,返回的型別是map,json,object,都是不應該的。實際工作中,我們會定義一個統一的格式,就是ResultBean,分頁的有另外一個PageResultBean。
錯誤範例:
//返回map可讀性不好,盡量不要
@PostMapping("/delete")
public Map<String, Object> delete(long id, String lang) {
}
// 成功返回boolean,失敗返回string,大忌
@PostMapping("/delete")
publicObjectdelete(long id, String lang) {
try {
boolean result = configService.delete(id, local);
return result;
} catch (Exception e) {
log.error(e);
return e.toString();
}
}
2. 沒有考慮失敗情況
一開始只考慮成功場景,等後面測試發現有錯誤情況,怎麽辦,改介面唄,前後台都改,勞民傷財無用功。
錯誤範例:
//不返回任何數據,沒有考慮失敗場景,容易返工
@PostMapping("/update")
publicvoidupdate(long id, xxx){
}
3. 出現和業務無關的輸入參數
如lang語言,當前使用者資訊 都不應該出現參數裏面,應該從當前會話裏面獲取。後面講ThreadLocal會說到怎麽樣去掉。除了程式碼可讀性不好問題外,尤其是參數出現當前使用者資訊的,這是個嚴重問題。
錯誤範例:
// (當前使用者刪除數據)參數出現lang和userid,尤其是userid,大忌
@PostMapping("/delete")
public Map<String, Object> delete(long id, String lang, String userId) {
}
4. 出現復雜的輸入參數
一般情況下,不允許出現例如json字串這樣的參數,這種參數可讀性極差。應該定義對應的bean。
錯誤範例:
// 參數出現json格式,可讀性不好,程式碼也難看
@PostMapping("/update")
public Map<String, Object> update(long id, String jsonStr) {
}
5. 沒有返回應該返回的數據
例如,新增介面一般情況下應該返回新物件的id標識,這需要編程經驗。新手定義的時候因為前台沒有用就不返回數據或者只返回true,這都是不恰當的。別人要不要是別人的事情,你該返回的還是應該返回。
錯誤範例:
// 約定俗成,新建應該返回新物件的資訊,只返回boolean容易導致返工
@PostMapping("/add")
public boolean add(xxx) {
//xxx
return configService.add();
}
很多人都覺得技術也很簡單,沒有什麽特別的地方,但是,實作這個程式碼框架之前,就是要你的介面的統一的格式ResultBean,aop才好做。有些人誤解了,上周末那篇文章說的都不是技術,重點說的是編碼習慣工作方式,如果你重點還是放在什麽技術上,那我也幫不了你了。同樣,如果我後面的關於習慣和規範的貼文,你重點還是放在技術上的話,那是丟了西瓜撿芝麻,有很多貼還是沒有任何技術點呢。
附上ResultBean,沒有任何技術含量:
@Data
public classResultBean<T> implementsSerializable{
privatestaticfinallong serialVersionUID = 1L;
publicstaticfinalint SUCCESS = 0;
publicstaticfinalint FAIL = 1;
publicstaticfinalint NO_PERMISSION = 2;
private String msg = "success";
privateint code = SUCCESS;
private T data;
publicResultBean(){
super();
}
publicResultBean(T data){
super();
this.data = data;
}
publicResultBean(Throwable e){
super();
this.msg = e.toString();
this.code = FAIL ;
}
}
統一的介面規範,能幫忙規避很多無用的返工修改和可能出現的問題。能使程式碼可讀性更加好,利於進行aop和自動化測試這些額外工作。大家一定要重視。
上面2段程式碼,第一個是原生態的,第2段是我指定了介面定義規範,使用AOP技術之後最終交付的程式碼,從15行到1行,自己感受一下。接下來說說大家關註的AOP如何實作。
先說說Controller規範,主要的內容是就是介面定義裏面的內容,你只要遵循裏面的規範,controller就問題不大,除了這些,還有另外的幾點:
1.所有函式返回統一的ResultBean/PageResultBean格式
原因見我的介面定義這個貼。沒有統一格式,AOP無法玩。
2.ResultBean/PageResultBean是controller專用的,不允許往後傳!
3.Controller做參數格式的轉換,不允許把json,map這類物件傳到services去,也不允許services返回json、map。
一般情況下!寫過程式碼都知道,map,json這種格式靈活,但是可讀性差,如果放業務數據,每次閱讀起來都比較困難。定義一個bean看著工作量多了,但程式碼清晰多了。
4.參數中一般情況不允許出現Request,Response這些物件
主要是可讀性問題。一般情況下。
5.不需要打印日誌
日誌在AOP裏面會打印,而且我的建議是大部份日誌在Services這層打印。
規範裏面大部份是 不要做的項多,要做的比較少,落地比較容易。
ResultBean定義帶泛型,使用了lombok。公眾 號:Java精選,回復面試,獲取面試資料。
@Data
public classResultBean<T> implementsSerializable{
privatestaticfinallong serialVersionUID = 1L;
publicstaticfinalint NO_LOGIN = -1;
publicstaticfinalint SUCCESS = 0;
publicstaticfinalint FAIL = 1;
publicstaticfinalint NO_PERMISSION = 2;
private String msg = "success";
privateint code = SUCCESS;
private T data;
publicResultBean(){
super();
}
publicResultBean(T data){
super();
this.data = data;
}
publicResultBean(Throwable e){
super();
this.msg = e.toString();
this.code = FAIL;
}
}
AOP程式碼,主要就是打印日誌和捕獲異常,異常要區分已知異常和未知異常,其中未知的異常是我們重點關註的,可以做一些信件通知啥的,已知異常可以再細分一下,可以不同的異常返回不同的返回碼:
/**
* 處理和包裝異常
*/
public classControllerAOP{
privatestaticfinal Logger logger = LoggerFactory.getLogger(ControllerAOP. class);
public Object handlerControllerMethod(ProceedingJoinPoint pjp) {
long startTime = System.currentTimeMillis();
ResultBean<?> result;
try {
result = (ResultBean<?>) pjp.proceed();
logger.info(pjp.getSignature() + "use time:" + (System.currentTimeMillis() - startTime));
} catch (Throwable e) {
result = handlerException(pjp, e);
}
return result;
}
private ResultBean<?> handlerException(ProceedingJoinPoint pjp, Throwable e) {
ResultBean<?> result = new ResultBean();
// 已知異常
if (e instanceof CheckException) {
result.setMsg(e.getLocalizedMessage());
result.setCode(ResultBean.FAIL);
} elseif (e instanceof UnloginException) {
result.setMsg("Unlogin");
result.setCode(ResultBean.NO_LOGIN);
} else {
logger.error(pjp.getSignature() + " error ", e);
//TODO 未知的異常,應該格外註意,可以發送信件通知等
result.setMsg(e.toString());
result.setCode(ResultBean.FAIL);
}
return result;
}
}
AOP配置:(關於用java程式碼還是xml配置,這裏我傾向於xml配置,因為這個會非週期性改動)
<aop:aspectj-autoproxy />
<beans:beanid="controllerAop" class="xxx.common.aop.ControllerAOP" />
<aop:config>
<aop:aspectid="myAop"ref="controllerAop">
<aop:pointcutid="target"
expression="execution(public xxx.common.beans.ResultBean *(..))" />
<aop:aroundmethod="handlerControllerMethod"pointcut-ref="target" />
</aop:aspect>
</aop:config>
現在知道為什麽要返回統一的一個ResultBean了:1.為了統一格式 ;2.為了套用AOP ;3.為了包裝異常資訊。
分頁的PageResultBean大同小異,大家自己依葫蘆畫瓢自己完成就好了。
貼一個簡單的controller(左邊的箭頭表示AOP攔截了)。請對比 吐槽我見過的最爛的java程式碼裏面原來的程式碼檢視,沒有對比就沒有傷害。
最後說一句,先有統一的介面定義規範,然後有AOP實作。先有思想再有技術。技術不是關鍵,AOP技術也很簡單,這個貼文的關鍵點不是技術,而是習慣和思想,不要撿了芝麻丟了西瓜。網路上講技術的貼多,講習慣、風格的少,這些都是我工作多年的行之有效的經驗之談。
來源:https://zhuanlan.zhihu.com/p/28708259
>>
END
精品資料,超贊福利,免費領
微信掃碼/長按辨識 添加【技術交流群】
群內每天分享精品學習資料
最近開發整理了一個用於速刷面試題的小程式;其中收錄了上千道常見面試題及答案(包含基礎、並行、JVM、MySQL、Redis、Spring、SpringMVC、SpringBoot、SpringCloud、訊息佇列等多個型別),歡迎您的使用。
👇👇
👇點選"閱讀原文",獲取更多資料(持續更新中)