介绍最近由于业务需要,需要访问阿里云的一个接口。打开文档,看看这个界面。比简单的目测一个小时就可以搞定,但是接入过程还是比较坎坷的。.首先看他给的例子,先下载了阿里云文档推荐的demo,跑了它的例子,替换了几个必要的参数比如秘钥。一般公司会有专职人员对接阿里云这些秘钥,你只需要负责他们的需求即可。但不排除部分企业需要自己对接阿里云。说到这里,我想吐槽一下。连接阿里云时,技术支持群其实就是钉钉,所以如果需要他们的支持,就必须下载钉钉。莫名其妙需要在电脑上安装一个额外的软件。言归正传,下载demo,替换相应的key等参数,然后运行demo,看看是否能正常返回结果。这一步主要是为了保证产品给你的秘钥等参数是否正确。如果接口可以断开,说明参数没有问题,接下来我们就可以开始写业务代码了。接入阿里云双因素认证https://market.aliyun.com/products/57000002/cmapi029454.html?spm=5176.10695662.1194487.1.60066c190NsSkZ#sku=yuncode2345400003官网下载demo运行看看.例子比较简单粗暴,封装了Apache的httplcient工具类的大量代码。我还是习惯性的使用feign来调用,因为feign的代码干净整洁。虽然底层也是通过HttpClient来实现的,但是实现方式我无所谓,毕竟业务代码看起来干净整洁。其demo如下:AppCode";Mapheaders=newHashMap();//header中的最终格式(中间的英文空格)为Authorization:APPCODE83359fd73fe94948385f570e3c139105headers.put("Authorization","APPCODE"+appcode);Mapquerys=newHashMap();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请到*https://github.com/aliyun/api-gateway-demo-sign-java/blob/master/src/main/java/com/aliyu下载n/api/gateway/demo/util/HttpUtils.java*下载**对应的依赖请参考*https://github.com/aliyun/api-gateway-demo-sign-java/blob/master/pom.xml*/HttpResponseresponse=HttpUtils.doGet(host,path,method,headers,querys);//错误信息见X-Ca-Error-Message字段System.out.println(response.toString());//获取响应bodySystem.out.println(EntityUtils.toString(response.getEntity()));}catch(Exceptione){e.printStackTrace();}}HttpResponseresponse=HttpUtils.doGet(host,path,method,headers,查询);根据它提供的代码,我们可以看出他使用了一个httpUtils类来实现http请求。我们可以用我们的FeignClient替换这个httpClient类。替换后的代码如下:@FeignClient(name="verifyIdCardAndNameFeignClient",url="https://safrvcert.market.alicloudapi.com")publicinterfaceVerifyIdCardAndNameFeignClient{@RequestMapping(value="/safrv_2meta_id_name/",method=RequestMethod.GET,produces=MediaType.APPLICATION_JSON_UTF8_VALUE)ResponseverifyIdCardAndNameMap(@RequestParam,Mapapp("Authorization")Stringauthori化);相对来说,下面的HttpClientUtils代码是不是比较简单?按照这个demo功能,确实实现了。说实话,我还是不太喜欢用map做参数。如果使用map作为入参,参数都是猜的。可维护性有点差。我还是习惯性的把一个javaBean封装成一个实体。阿里文档其实也提到了,虽然他只讲了数据查询层。接下来我们修改请求参数,改成javaBean。更改后的代码@RequestMapping(value="/safrv_2meta_id_name/",method=RequestMethod.GET,produces=MediaType.APPLICATION_JSON_UTF8_VALUE)ResponseverifyIdCardAndNameDTO(@RequestBodyAliyunVerifyIdCardAndNameReqapp,@RequestHeader("Authorization")Stringauthorization);请求不成功。根据报错返回的信息,好像是没有接收到参数。我们采用的是GET请求的方式,然后将参数传递给实体,所以没有收到。feignClient不支持get方式传递实体类吗?后来通过查资料,发现了一个注解@SpringQueryMap。我们将上面代码中的@RequestBody替换成@SpringQueryMap来完美解决这个问题。@SpringQueryMap在springcloud2.1.x及以上版本提供了新版本。@SpringQueryMap注解,为什么这个注解可以帮我们实现。源代码下没有秘密。我们可以翻看feign的源码。应该比较简单。我们可以简单的看一下源码。如果你看源代码,你不知道从哪里开始。从头看到尾肯定不现实。如果你不从头开始,不知道源代码在哪里,有一个很简单的方法。看看用到了哪些地方,尽量在每个地方下断点。我们全局搜索了一下,发现用到的地方主要在QueryMapParameterProcessor类。所以我们可以尝试在这个类下个断点。/***{@linkSpringQueryMap}parameterprocessor.**@authorAramPeres*@seeAnnotatedParameterProcessor*/publicclassQueryMapParameterProcessorimplementsAnnotatedParameterProcessor{privatestaticfinalClassANNOTATION=SpringQueryMap.class;@OverridepublicClassgetAnnotationType(){returnANNOTATION;}@OverridepublicbooleancontextatedPargument(notation),Methodmethod){intparamIndex=context.getParameterIndex();MethodMetadatametadata=context.getMethodMetadata();if(metadata.queryMapIndex()==null){metadata.queryMapIndex(paramIndex);metadata.queryMapEncoded(SpringQueryMap.class.cast(注解)).encoded());}returntrue;}}我们发现如果我们键入这个类,它会在容器启动时加载,并执行processArgument方法。我们先忽略这个方法。接下来我们看看Feign究竟是如何发起调用的。找到SynchronousMethodHandler#invoke方法publicRequestTemplatecreate(Object[]argv){...省略部分代码//metadata.queryMapIndex()是QueryMapParameterProcessor#processArgument方法赋值的if(metadata.queryMapIndex()!=null){//addquerymapparametersafterinitialresolvesothattheytake//precedenceoveranypredefinedvalues//通过下标获取需要特殊处理的对象。这里有个问题,只会处理方法参数的第一个@SpringQueryMap注解。//原因是QueryMapParameterProcessor#processArgument方法只会赋值第一个下标,然后这里只会取第一个下标,所以只有第一个@SpringQueryMap注解Objectvalue=argv[metadata.queryMapIndex()];//转换这里需要注意的是FieldQueryMapEncoder类默认使用解析参数,所以不会解析父类的参数。如果需要解析父类的参数,需要在feign的Config中指定QueryMapEncoder为FieldQueryMapEncoderMapqueryMap=toQueryMap(value);//完成的拼接解析对象为URL参数template=addQueryMapQueryParameters(queryMap,template);}...省略部分代码}上面代码的逻辑还是比较容易理解的。获取需要特殊处理的对象,将对象转为map(这里有一个坑,默认不会解析父类的字段)拼接map后附加到url总结get参数是通过@实现的上面是SpringQueryMap注解,但是如果需要通过它,我们如何实现多个@SpringQueryMap注解呢?或者我们可以自己实现自己的SpringQueryMap。我们如何实施它?@SpringQueryMap注解默认不解析父类的参数。如果我们需要解析父类的参数需要修改Feign的config#QueryMapEncoder为FieldQueryMapEncoder如果我们自己实现一个AnnotatedParameterProcessor,所有默认的PathVariableParameterProcessor、RequestParamParameterProcessor、RequestHeaderParameterProcessor、QueryMapParameterProcessor都会失效。下面我们看看SpringMvcContract类为什么会失效。所以自定义AnnotatedParameterProcessor需要谨慎。最后,由于自己的无知和知识的匮乏,难免有错误。如果您发现错误,请留言指出给我,我会改正。如果您觉得文章还不错,您的转发、分享、欣赏、点赞、评论就是对我最大的鼓励。感谢您的阅读,非常欢迎并感谢您的关注。本文转载自微信公众号“java财经”,可通过以下二维码关注。转载本文请联系爪哇财经公众号。