1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        feign的一個(gè)注解居然隱藏這么多知識(shí)!

        共 8774字,需瀏覽 18分鐘

         ·

        2021-10-12 21:25

        ????關(guān)注后回復(fù) “進(jìn)群” ,拉你進(jìn)程序員交流群????

        作者丨java金融

        來(lái)源丨java金融

        引言

        最近由于業(yè)務(wù)的需要,需要接入下阿里云的一個(gè)接口,打開(kāi)文檔看了看這個(gè)接口看下來(lái)還是比簡(jiǎn)單的目測(cè)個(gè)把小時(shí)就可以搞定,但是接入的過(guò)程還是比較坎坷的。首先我看了看他給的示例,首先把阿里云文檔推薦的demo下載下來(lái),把它的例子跑起來(lái),替換下幾個(gè)必要的參數(shù)比如秘鑰啥的。這些秘鑰一般公司都會(huì)有專(zhuān)職的人員與阿里云去對(duì)接,你只要負(fù)責(zé)管他要就行了。不過(guò)也不排除也有得公司需要自己去對(duì)接阿里云。說(shuō)到這里就想吐槽下,對(duì)接阿里云的時(shí)候技術(shù)支持群居然是釘釘,所以需要他們的支持就必須要下載個(gè)釘釘, 電腦上莫名的有需要多裝一個(gè)軟件。扯遠(yuǎn)了我們還是回到正題,把它demo下載下來(lái),然后把對(duì)應(yīng)的秘鑰等參數(shù)替換下,然后運(yùn)行下demo看看是否能夠正常返回結(jié)果,做這一步主要是為了保證產(chǎn)品給過(guò)來(lái)的秘鑰等參數(shù)是否正確。如果能夠掉通接口,那就說(shuō)明參數(shù)沒(méi)啥問(wèn)題的接著我們就可以著手來(lái)寫(xiě)業(yè)務(wù)代碼了。接入阿里云二要素認(rèn)證https://market.aliyun.com/products/57000002/cmapi029454.html?spm=5176.10695662.1194487.1.60066c190NsSkZ#sku=yuncode2345400003 把官網(wǎng)的demo下載下來(lái)跑起來(lái)看看,官網(wǎng)給出的例子還是比較簡(jiǎn)單粗暴的,就是封裝了一個(gè)Apachehttplcient工具類(lèi)一大坨的代碼,個(gè)人還是習(xí)慣性的使用feign來(lái)進(jìn)行調(diào)用,因?yàn)?code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">feign的代碼干凈整潔,雖然底層也是通過(guò)HttpClient來(lái)實(shí)現(xiàn),但是實(shí)現(xiàn)對(duì)我來(lái)說(shuō)是無(wú)感的,畢竟業(yè)務(wù)代碼看起來(lái)干凈整潔。它的demo如下:

        public static void main(String[] args) {
             String host = "https://safrvcert.market.alicloudapi.com";
             String path = "/safrv_2meta_id_name/";
             String method = "GET";
             String appcode = "你自己的AppCode";
             Map<String, String> headers = new HashMap<String, String>();
             //最后在header中的格式(中間是英文空格)為Authorization:APPCODE 83359fd73fe94948385f570e3c139105
             headers.put("Authorization""APPCODE " + appcode);
             Map<String, String> querys = new HashMap<String, String>();
             querys.put("__userId""__userId");
             querys.put("customerID""customerID");
             querys.put("identifyNum""identifyNum");
                    querys.put("identifyNumMd5""identifyNumMd5");
             querys.put("userName""userName");
             querys.put("verifyKey""verifyKey");


             try {
              /**
              * 重要提示如下:
              * HttpUtils請(qǐng)從
              * https://github.com/aliyun/api-gateway-demo-sign-java/blob/master/src/main/java/com/aliyun/api/gateway/demo/util/HttpUtils.java
              * 下載
              *
              * 相應(yīng)的依賴(lài)請(qǐng)參照
              * https://github.com/aliyun/api-gateway-demo-sign-java/blob/master/pom.xml
              */

              HttpResponse response = HttpUtils.doGet(host, path, method, headers, querys);
              //錯(cuò)誤信息見(jiàn)X-Ca-Error-Message字段
                        System.out.println(response.toString());
              //獲取response的body
              System.out.println(EntityUtils.toString(response.getEntity()));
             } catch (Exception e) {
              e.printStackTrace();
             }
         }
        HttpResponse response = HttpUtils.doGet(host, path, method, headers, querys);

        根據(jù)它提供的代碼我們可以看出來(lái)他是用一個(gè)httpUtils 類(lèi)來(lái)實(shí)現(xiàn)http請(qǐng)求的,我們可以把這個(gè)httpClient類(lèi) 替換成我們的FeignClient替換后的代碼如下:

        @FeignClient(name = "verifyIdCardAndNameFeignClient", url = "https://safrvcert.market.alicloudapi.com")
        public interface VerifyIdCardAndNameFeignClient {
            @RequestMapping(value = "/safrv_2meta_id_name/", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
            Response verifyIdCardAndNameMap(@RequestParam Map<String,String> app, @RequestHeader("Authorization") String authorization);

        相對(duì)比較下來(lái)下面這個(gè)HttpClientUtils代碼是不是比較簡(jiǎn)潔按照這個(gè)demo功能確實(shí)是實(shí)現(xiàn)了,說(shuō)實(shí)話(huà)個(gè)人還是不是很喜歡用map來(lái)作為參數(shù),map作為入?yún)⒌脑?huà),參數(shù)全靠猜可讀性以及可維護(hù)性有點(diǎn)差,個(gè)人還是習(xí)慣性的封裝一個(gè)javaBean作為實(shí)體。阿里文檔其實(shí)也有提到一嘴,雖然他只說(shuō)到數(shù)據(jù)查詢(xún)這一層。下面我們就修改下請(qǐng)求參數(shù)把它改成一個(gè)javaBean,改變后的代碼

        @RequestMapping(value = "/safrv_2meta_id_name/", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
        Response verifyIdCardAndNameDTO(@RequestBody AliyunVerifyIdCardAndNameReq app, @RequestHeader("Authorization") String authorization);

        請(qǐng)求并沒(méi)有成功,根據(jù)報(bào)錯(cuò)返回的信息看下來(lái)應(yīng)該是沒(méi)有接受到參數(shù)。我們是GET請(qǐng)求的方式然后參數(shù)傳遞的是實(shí)體導(dǎo)致沒(méi)有接收到。feignClient不支持get方式傳遞實(shí)體類(lèi)嗎?后來(lái)經(jīng)過(guò)查詢(xún)資料發(fā)現(xiàn)了一個(gè)注解@SpringQueryMap 我們把上述代碼@RequestBody替換成@SpringQueryMap完美解決這個(gè)問(wèn)題

        @SpringQueryMap

        spring cloud 2.1.x 以上的版本,提供了一個(gè)新的注解@SpringQueryMap,為何這個(gè)注解可以幫我們實(shí)現(xiàn)。源碼之下無(wú)秘密,我們可以翻翻feign的源碼相對(duì)來(lái)說(shuō)應(yīng)該是比較簡(jiǎn)單的,我們可以簡(jiǎn)單的來(lái)看下源碼??丛创a是不是也不知道從哪里看起,從頭看到尾肯定也不現(xiàn)實(shí), 不從頭開(kāi)始看,又不知道源碼在哪里,有個(gè)很簡(jiǎn)單的方法我們直接拿著這個(gè)注解全局搜一下,看看有哪些地方使用到了,在每個(gè)地方都打上一個(gè)斷點(diǎn)試試我們?nèi)炙严掳l(fā)現(xiàn)使用的地方主要在QueryMapParameterProcessor這個(gè)類(lèi)里面。所以我們可以在這個(gè)類(lèi)里面打上一個(gè)斷點(diǎn)試試。


        /**
         * {@link SpringQueryMap} parameter processor.
         *
         * @author Aram Peres
         * @see AnnotatedParameterProcessor
         */

        public class QueryMapParameterProcessor implements AnnotatedParameterProcessor {

         private static final Class<SpringQueryMap> ANNOTATION = SpringQueryMap.class;

         @Override
         public Class<? extends Annotation> getAnnotationType() {
          return ANNOTATION;
         }

         @Override
         public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {
          int paramIndex = context.getParameterIndex();
          MethodMetadata metadata = context.getMethodMetadata();
          if (metadata.queryMapIndex() == null) {
           metadata.queryMapIndex(paramIndex);
           metadata.queryMapEncoded(SpringQueryMap.class.cast(annotation).encoded());
          }
          return true;
         }
        }

        我們發(fā)現(xiàn)打這個(gè)類(lèi)的話(huà)在容器啟動(dòng)的時(shí)候會(huì)進(jìn)行加載,并且會(huì)執(zhí)行processArgument方法,這個(gè)我們先不管這個(gè)方法,接下來(lái)我們來(lái)看看 Feign真正發(fā)起調(diào)用的地方找到SynchronousMethodHandler#invoke方法

        public RequestTemplate create(Object[] argv) {
         ... 省略部分代碼
         // metadata.queryMapIndex() 就是QueryMapParameterProcessor #processArgument方法賦值的
              if (metadata.queryMapIndex() != null) {
                // add query map parameters after initial resolve so that they take
                // precedence over any predefined values
                // 通過(guò)下標(biāo)獲取到需要特殊處理的對(duì)象,這里有個(gè)問(wèn)題只會(huì)處理方法參數(shù)的第一個(gè)@SpringQueryMap注解,
                // 原因就是QueryMapParameterProcessor #processArgument這個(gè)方法只會(huì)把第一個(gè)下標(biāo)賦值進(jìn)去,然后這里也只會(huì)取第一個(gè)下標(biāo),所以只會(huì)處理第一個(gè)@SpringQueryMap注解
                Object value = argv[metadata.queryMapIndex()];
                //將對(duì)象轉(zhuǎn)換為map  這里需要注意下默認(rèn)使用解析參數(shù)的是FieldQueryMapEncoder類(lèi)所以它并不會(huì)去解析父類(lèi)的參數(shù),如果需要解析父類(lèi)的參數(shù)我們需要在feign的Config里面指定QueryMapEncoder為FieldQueryMapEncoder
                Map<String, Object> queryMap = toQueryMap(value);
                //拼接解析完成的對(duì)象為URL參數(shù)
                template = addQueryMapQueryParameters(queryMap, template);
              }
        ... 省略部分代碼
        }

        上述代碼邏輯還是挺好理解的

        • 首先去判斷是否需要處理下querymap
        • 通過(guò)下標(biāo)獲取到需要特殊處理的對(duì)象
        • 將對(duì)象轉(zhuǎn)換為map(這里有個(gè)坑默認(rèn)不會(huì)去解析父類(lèi)的字段)
        • 拼接追加mapurl

        總結(jié)

        • 上面通過(guò)@SpringQueryMap注解實(shí)現(xiàn)了get傳參,但是如果需要傳遞多個(gè)@SpringQueryMap注解我們可以怎么來(lái)實(shí)現(xiàn)呢?
        • 或者我們可以自己動(dòng)手來(lái)實(shí)現(xiàn)一個(gè)我們自己的SpringQueryMap,我們?cè)撊绾螌?shí)現(xiàn)?
        • @SpringQueryMap注解默認(rèn)是不會(huì)去解析父類(lèi)的參數(shù),如果需要解析父類(lèi)的參數(shù)需要修改Feignconfig# QueryMapEncoderFieldQueryMapEncoder。
        • 如果我們自己去實(shí)現(xiàn)了一個(gè)AnnotatedParameterProcessor所有默認(rèn)的PathVariableParameterProcessorRequestParamParameterProcessor、RequestHeaderParameterProcessor、QueryMapParameterProcessor都會(huì)失效,為啥會(huì)失效我們?nèi)タ纯?code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">SpringMvcContract這個(gè)類(lèi)。所以自定義AnnotatedParameterProcessor需要慎重。

        結(jié)束

        • 由于自己才疏學(xué)淺,難免會(huì)有紕漏,假如你發(fā)現(xiàn)了錯(cuò)誤的地方,還望留言給我指出來(lái),我會(huì)對(duì)其加以修正。
        • 如果你覺(jué)得文章還不錯(cuò),你的轉(zhuǎn)發(fā)、分享、贊賞、點(diǎn)贊、留言就是對(duì)我最大的鼓勵(lì)。
        • 感謝您的閱讀,十分歡迎并感謝您的關(guān)注。

        -End-

        最近有一些小伙伴,讓我?guī)兔φ乙恍?nbsp;面試題 資料,于是我翻遍了收藏的 5T 資料后,匯總整理出來(lái),可以說(shuō)是程序員面試必備!所有資料都整理到網(wǎng)盤(pán)了,歡迎下載!

        點(diǎn)擊??卡片,關(guān)注后回復(fù)【面試題】即可獲取

        在看點(diǎn)這里好文分享給更多人↓↓

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

        手機(jī)掃一掃分享

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

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            台湾三级肉与欲 | www.大香蕉在线 | 欧美日韩亚洲综合网 | 欧美男女激情视频 | 米奇网五月 | 乱伦小说专区 | 亚洲欧洲精品一区二区三区 | 成人三级久久久 | 欧美视频成人 | 日韩成人电影中文字幕 |