<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          這是我見(jiàn)過(guò)寫(xiě)得最爛的Controller層代碼,沒(méi)有之一!

          共 7350字,需瀏覽 15分鐘

           ·

          2022-07-23 16:31

          往期熱門(mén)文章:

          1、聊聊接口優(yōu)化的幾種方法

          2、3 步完成 Spring Boot 的日志脫敏

          3、線(xiàn)上MySQL的自增id用盡怎么辦?被面試官干趴下了!

          4、求求你別再用 System.currentTimeMillis() 統(tǒng)計(jì)代碼耗時(shí)了,真的太 Low 了!

          5、如何將 @Transactional 事務(wù)注解運(yùn)用到爐火純青?

          來(lái)源:https://zhuanlan.zhihu.com/p/28708259

          一. 接口定義
          工作中,少不了要定義各種接口,系統(tǒng)集成要定義接口,前后臺(tái)掉調(diào)用也要定義接口。接口定義一定程度上能反應(yīng)程序員的編程功底。列舉一下工作中我發(fā)現(xiàn)大家容易出現(xiàn)的問(wèn)題:
          1. 返回格式不統(tǒng)一
          同一個(gè)接口,有時(shí)候返回?cái)?shù)組,有時(shí)候返回單個(gè);成功的時(shí)候返回對(duì)象,失敗的時(shí)候返回錯(cuò)誤信息字符串。工作中有個(gè)系統(tǒng)集成就是這樣定義的接口,真是辣眼睛。這個(gè)對(duì)應(yīng)代碼上,返回的類(lèi)型是map,json,object,都是不應(yīng)該的。實(shí)際工作中,我們會(huì)定義一個(gè)統(tǒng)一的格式,就是ResultBean,分頁(yè)的有另外一個(gè)PageResultBean。
          錯(cuò)誤范例:
          //返回map可讀性不好,盡量不要
           @PostMapping("/delete")
           public Map<StringObjectdelete(long id, String lang) {

           }

           // 成功返回boolean,失敗返回string,大忌
           @PostMapping("/delete")
           public Object delete(long id, String lang) {
             try {
               boolean result = configService.delete(id, local);
               return result;
             } catch (Exception e) {
               log.error(e);
               return e.toString();
             }
           }

          2. 沒(méi)有考慮失敗情況
          一開(kāi)始只考慮成功場(chǎng)景,等后面測(cè)試發(fā)現(xiàn)有錯(cuò)誤情況,怎么辦,改接口唄,前后臺(tái)都改,勞民傷財(cái)無(wú)用功。
          錯(cuò)誤范例:
          //不返回任何數(shù)據(jù),沒(méi)有考慮失敗場(chǎng)景,容易返工
           @PostMapping("/update")
           public void update(long id, xxx) {

           }

          3. 出現(xiàn)和業(yè)務(wù)無(wú)關(guān)的輸入?yún)?shù)
          如lang語(yǔ)言,當(dāng)前用戶(hù)信息 都不應(yīng)該出現(xiàn)參數(shù)里面,應(yīng)該從當(dāng)前會(huì)話(huà)里面獲取。后面講ThreadLocal會(huì)說(shuō)到怎么樣去掉。除了代碼可讀性不好問(wèn)題外,尤其是參數(shù)出現(xiàn)當(dāng)前用戶(hù)信息的,這是個(gè)嚴(yán)重問(wèn)題。
          錯(cuò)誤范例:
          // (當(dāng)前用戶(hù)刪除數(shù)據(jù))參數(shù)出現(xiàn)lang和userid,尤其是userid,大忌
           @PostMapping("/delete")
           public Map<StringObjectdelete(long id, String lang, String userId) {

           }

          4. 出現(xiàn)復(fù)雜的輸入?yún)?shù)
          一般情況下,不允許出現(xiàn)例如json字符串這樣的參數(shù),這種參數(shù)可讀性極差。應(yīng)該定義對(duì)應(yīng)的bean。
          錯(cuò)誤范例:
          // 參數(shù)出現(xiàn)json格式,可讀性不好,代碼也難看
           @PostMapping("/update")
           public Map<StringObject> update(long id, String jsonStr) {

           }

          5. 沒(méi)有返回應(yīng)該返回的數(shù)據(jù)

          例如,新增接口一般情況下應(yīng)該返回新對(duì)象的id標(biāo)識(shí),這需要編程經(jīng)驗(yàn)。新手定義的時(shí)候因?yàn)榍芭_(tái)沒(méi)有用就不返回?cái)?shù)據(jù)或者只返回true,這都是不恰當(dāng)?shù)摹e人要不要是別人的事情,你該返回的還是應(yīng)該返回。

          錯(cuò)誤范例:

          // 約定俗成,新建應(yīng)該返回新對(duì)象的信息,只返回boolean容易導(dǎo)致返工
           @PostMapping("/add")
           public boolean add(xxx{
             //xxx
             return configService.add();
           }

          很多人都覺(jué)得技術(shù)也很簡(jiǎn)單,沒(méi)有什么特別的地方,但是,實(shí)現(xiàn)這個(gè)代碼框架之前,就是要你的接口的統(tǒng)一的格式ResultBean,aop才好做。有些人誤解了,上周末那篇文章說(shuō)的都不是技術(shù),重點(diǎn)說(shuō)的是編碼習(xí)慣工作方式,如果你重點(diǎn)還是放在什么技術(shù)上,那我也幫不了你了。同樣,如果我后面的關(guān)于習(xí)慣和規(guī)范的帖子,你重點(diǎn)還是放在技術(shù)上的話(huà),那是丟了西瓜撿芝麻,有很多貼還是沒(méi)有任何技術(shù)點(diǎn)呢。

          附上ResultBean,沒(méi)有任何技術(shù)含量:

          @Data
          public class ResultBean<Timplements Serializable {

           private static final long serialVersionUID = 1L;

           public static final int SUCCESS = 0;

           public static final int FAIL = 1;

           public static final int NO_PERMISSION = 2;

           private String msg = "success";

           private int code = SUCCESS;

           private T data;

           public ResultBean() {
             super();
           }

           public ResultBean(T data) {
             super();
             this.data = data;
           }

           public ResultBean(Throwable e) {
             super();
             this.msg = e.toString();
             this.code = FAIL ;
           }
          }

          統(tǒng)一的接口規(guī)范,能幫忙規(guī)避很多無(wú)用的返工修改和可能出現(xiàn)的問(wèn)題。能使代碼可讀性更加好,利于進(jìn)行aop和自動(dòng)化測(cè)試這些額外工作。大家一定要重視。

          二. Controller規(guī)范

          上面2段代碼,第一個(gè)是原生態(tài)的,第2段是我指定了接口定義規(guī)范,使用AOP技術(shù)之后最終交付的代碼,從15行到1行,自己感受一下。接下來(lái)說(shuō)說(shuō)大家關(guān)注的AOP如何實(shí)現(xiàn)。

          先說(shuō)說(shuō)Controller規(guī)范,主要的內(nèi)容是就是接口定義里面的內(nèi)容,你只要遵循里面的規(guī)范,controller就問(wèn)題不大,除了這些,還有另外的幾點(diǎn):


          1.所有函數(shù)返回統(tǒng)一的ResultBean/PageResultBean格式
          原因見(jiàn)我的接口定義這個(gè)貼。沒(méi)有統(tǒng)一格式,AOP無(wú)法玩。
          2.ResultBean/PageResultBean是controller專(zhuān)用的,不允許往后傳!
          3.Controller做參數(shù)格式的轉(zhuǎn)換,不允許把json,map這類(lèi)對(duì)象傳到services去,也不允許services返回json、map。
          一般情況下!寫(xiě)過(guò)代碼都知道,map,json這種格式靈活,但是可讀性差,如果放業(yè)務(wù)數(shù)據(jù),每次閱讀起來(lái)都比較困難。定義一個(gè)bean看著工作量多了,但代碼清晰多了。
          4.參數(shù)中一般情況不允許出現(xiàn)Request,Response這些對(duì)象
          主要是可讀性問(wèn)題。一般情況下。
          5.不需要打印日志
          日志在AOP里面會(huì)打印,而且我的建議是大部分日志在Services這層打印。
          規(guī)范里面大部分是 不要做的項(xiàng)多,要做的比較少,落地比較容易。
          ResultBean定義帶泛型,使用了lombok。
          @Data
          public class ResultBean<Timplements Serializable {

           private static final long serialVersionUID = 1L;

           public static final int NO_LOGIN = -1;

           public static final int SUCCESS = 0;

           public static final int FAIL = 1;

           public static final int NO_PERMISSION = 2;

           private String msg = "success";

           private int code = SUCCESS;

           private T data;

           public ResultBean() {
             super();
           }

           public ResultBean(T data) {
             super();
             this.data = data;
           }

           public ResultBean(Throwable e) {
             super();
             this.msg = e.toString();
             this.code = FAIL;
           }
          }
          AOP代碼,主要就是打印日志和捕獲異常,異常要區(qū)分已知異常和未知異常,其中未知的異常是我們重點(diǎn)關(guān)注的,可以做一些郵件通知啥的,已知異常可以再細(xì)分一下,可以不同的異常返回不同的返回碼:
          /**
          * 處理和包裝異常
          */

          public class ControllerAOP {
           private static final 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);
             } else if (e instanceof UnloginException) {
               result.setMsg("Unlogin");
               result.setCode(ResultBean.NO_LOGIN);
             } else {
               logger.error(pjp.getSignature() + " error ", e);
               //TODO 未知的異常,應(yīng)該格外注意,可以發(fā)送郵件通知等
               result.setMsg(e.toString());
               result.setCode(ResultBean.FAIL);
             }

             return result;
           }
          }
          AOP配置:(關(guān)于用java代碼還是xml配置,這里我傾向于xml配置,因?yàn)檫@個(gè)會(huì)不定期改動(dòng))

           <aop:aspectj-autoproxy />
           <beans:bean id="controllerAop" class="xxx.common.aop.ControllerAOP" />
           <aop:config>
             <aop:aspect id="myAop" ref="controllerAop">
               <aop:pointcut id="target"
                 expression="execution(public xxx.common.beans.ResultBean *(..))" />

               <aop:around method="handlerControllerMethod" pointcut-ref="target" />
             </aop:aspect>
           </aop:config>

          現(xiàn)在知道為什么要返回統(tǒng)一的一個(gè)ResultBean了:1.為了統(tǒng)一格式 ;2.為了應(yīng)用AOP ;3.為了包裝異常信息。

          分頁(yè)的PageResultBean大同小異,大家自己依葫蘆畫(huà)瓢自己完成就好了。

          貼一個(gè)簡(jiǎn)單的controller(左邊的箭頭表示AOP攔截了)。請(qǐng)對(duì)比 吐槽我見(jiàn)過(guò)的最爛的java代碼里面原來(lái)的代碼查看,沒(méi)有對(duì)比就沒(méi)有傷害。


          最后說(shuō)一句,先有統(tǒng)一的接口定義規(guī)范,然后有AOP實(shí)現(xiàn)。先有思想再有技術(shù)。技術(shù)不是關(guān)鍵,AOP技術(shù)也很簡(jiǎn)單,這個(gè)帖子的關(guān)鍵點(diǎn)不是技術(shù),而是習(xí)慣和思想,不要撿了芝麻丟了西瓜。網(wǎng)絡(luò)上講技術(shù)的貼多,講習(xí)慣、風(fēng)格的少,這些都是我工作多年的行之有效的經(jīng)驗(yàn)之談。

          往期熱門(mén)文章:

          1、線(xiàn)上MySQL的自增id用盡怎么辦?被面試官干趴下了!
          2、計(jì)算機(jī)專(zhuān)業(yè)會(huì)不會(huì)成為下一個(gè)土木?
          3、xxl-job驚艷的設(shè)計(jì),怎能叫人不愛(ài)
          4、ArrayList#subList這四個(gè)坑,一不小心就中招
          5、面試官:大量請(qǐng)求 Redis 不存在的數(shù)據(jù),從而影響數(shù)據(jù)庫(kù),該如何解決?
          6、MySQL 暴跌!
          7、超越 Xshell!號(hào)稱(chēng)下一代 Terminal 終端神器,用完愛(ài)不釋手!
          8、IDEA 官宣全新默認(rèn) UI,太震撼了!!
          9、讓你直呼「臥槽」的 GitHub 項(xiàng)目!
          10、Kafka又笨又重,為啥不選Redis?

          瀏覽 18
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久精品禁一区二区三区四区五区 | 蜜桃黄片av | 夜天干天干啦天干天天爽 | 网址成人视频国产偷拍 | 国产一级黄片视频 |