大家都知道okhttp是square公司开源的java版http客户端工具。事实上,Square也开源了一个基于okhttp的改造工具,进一步封装,支持通过接口进行HTTP请求。如果你还在项目中直接使用RestTemplate或者okhttp,或者基于他们封装的HttpUtils,那么你可以尝试使用Retrofit。retrofit-spring-boot-starter实现了Retrofit与spring-boot框架的快速集成,并支持部分功能增强,大大简化了spring-boot项目下http接口调用的开发。接下来我们直接上retrofit-spring-boot-starter看看spring-boot项目发送http请求是多么的方便。retrofit官方没有提供快速集成spring-boot的starter。retrofit-spring-boot-starter是作者自己打包的,已经在生产环境中使用,非常稳定。喜欢就给个star吧。https://github.com/LianjiaTech/retrofit-spring-boot-starter引入依赖com.github.lianjiatechretrofit-spring-boot-starter2.0.2配置@RetrofitScan注解可以为带@Configuration的类配置@RetrofitScan,也可以直接配置在spring-boot的启动类上,如下:@SpringBootApplication@RetrofitScan("com.github.lianjiatech.retrofit.spring.boot.test")publicclassRetrofitTestApplication{publicstaticvoidmain(String[]args){SpringApplication.run(RetrofitTestApplication.class,args);}}定义http接口接口必须标上@RetrofitClient注解!推荐:100道面试题总结http相关评论可参考官方文档:https://square.github.io/retrofit/@RetrofitClient(baseUrl="${test.baseUrl}")publicinterfaceHttpApi{@GET("person")ResultgetPerson(@Query("id")Longid);}Injection使用接口注入其他服务。@ServicepublicclassTestService{@AutowiredprivateHttpApihttpApi;publicvoidtest(){//通过httpApi发起http请求}}只要经过上面的步骤,就可以通过接口发送http请求了,真的很简单。如果你在spring-boot项目中使用过mybatis,相信你会对这个用法更加熟悉。接下来我们继续介绍retrofit-spring-boot-starter更高级的功能。注解拦截器很多时候,我们希望某个接口下的一些http请求,执行统一的拦截处理逻辑。这时候可以使用注解拦截器。使用步骤主要分为2步:继承BasePathMatchInterceptor编写拦截处理器;使用@Intercept来标记接口。下面以在指定请求的url后拼接timestamp时间戳为例,介绍如何使用注解拦截器。继承BasePathMatchInterceptor编写拦截处理器@ComponentpublicclassTimeStampInterceptorextendsBasePathMatchInterceptor{@OverridepublicResponsedoIntercept(Chainchain)throwsIOException{Requestrequest=chain.request();HttpUrlurl=request.url();longtimestamp=System.currentTimeMillis();HttpUrlnewUrl=url.newBuilder().addQueryParameter("timestamp",String.valueOf(timestamp)).build();RequestnewRequest=request.newBuilder().url(newUrl).build();returnchain.proceed(newRequest);}}使用@Intercept标记@RetrofitClient(baseUrl="${test.baseUrl}")@Intercept(handler=TimeStampInterceptor.class,include={"/api/**"},exclude="/api/test/savePerson")publicinterfaceHttpApi{@GET("person")ResultgetPerson(@Query("id")Longid);@POST("savePerson")ResultsavePerson(@BodyPersonperson);}上面@Intercept配置的意思是:在HttpApi接口下拦截/为api/**路径下的请求(不包括/api/test/savePerson),拦截处理器使用TimeStampInterceptor。推荐:100道面试题总结ExtendedAnnotationInterceptor有时候,我们需要在拦截注解中动态传入一些参数,然后在执行拦截的时候使用这个参数。这时候我们可以扩展实现自定义拦截注解。自定义拦截注解必须使用@InterceptMark标记,注解中必须包含include()、exclude()、handler()属性信息。使用的步骤主要分为3步:自定义拦截注解继承BasePathMatchInterceptor在拦截处理器接口上编写自定义拦截注解;比如我们需要在请求头中动态添加accessKeyId和accessKeySecret签名信息才能正常发起http请求。这时候,我们可以自定义一个签名拦截注解@Sign来实现。下面以自定义@Sign拦截注解为例进行说明。自定义@Sign注解@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documented@InterceptMarkpublic@interfaceSign{/***Keykey*支持占位符表单配置。**@return*/StringaccessKeyId();/***Key*支持占位符配置。**@return*/StringaccessKeySecret();/***拦截器匹配路径**@return*/String[]include()default{"/**"};/***拦截器排除匹配,排除指定路径拦截**@return*/String[]exclude()default{};/***处理这个注解的拦截器类*先从spring容器中获取对应的Bean,如果获取不到,再使用反射创造一个!**@return*/Classhandler()defaultSignInterceptor.class;}扩展自定义拦截器注解需要注意两点:自定义拦截器注解必须使用@InterceptMark标记。注解必须包括include()、exclude()、handler()属性信息。实现SignInterceptor@ComponentpublicclassSignInterceptorextendsBasePathMatchInterceptor{privateStringaccessKeyId;privateStringaccessKeySecret;publicvoidsetAccessKeyId(StringaccessKeyId){this.accessKeyId=accessKeyId;}publicvoidsetAccessKeySecret(StringaccessKeySecret){this.accessKeySecret=accessKeySecret;}@OverridepublicResponsedoIntercept(Chainchain)throwsIOException{Requestrequest=chain.request();RequestnewReq=request.newBuilder().addHeader("accessKeyId",accessKeyId).addHeader("accessKeySecret",accessKeySecret).build();returnchain.proceed(newReq);}}上面的accessKeyId和accessKeySecret字段值会根据@Sign注解的accessKeyId()和accessKeySecret()值是自动注入的。如果@Sign以占位符的形式指定一个字符串,配置属性值将被用于注入。此外,accessKeyId和accessKeySecret字段必须提供setter方法。Java知音公众号回复“后端面试”,送你Java面试题集锦。使用@Sign@RetrofitClient(baseUrl="${test.baseUrl}")@Sign(accessKeyId="${test.accessKeyId}",accessKeySecret="${test.accessKeySecret}",exclude={"/api/test/person"})publicinterfaceHttpApi{@GET("person")ResultgetPerson(@Query("id")Longid);@POST("savePerson")ResultsavePerson(@BodyPersonperson);}在这个方式,签名信息可以自动添加到指定url的请求中。连接池管理默认情况下,所有通过Retrofit发送的http请求都会使用默认的max-idle-connections=5keep-alive-second=300的连接池。当然我们也可以在配置文件中配置多个自定义连接池,然后通过@RetrofitClient的poolName属性指定使用。比如我们想让某个接口下的所有请求都使用poolName=test1的连接池,代码实现如下:1.配置连接池。retrofit:#连接池配置pool:test1:max-idle-connections:3keep-alive-second:100test2:max-idle-connections:5keep-alive-second:502。指定@RetrofitClient的poolName属性使用的连接池。@RetrofitClient(baseUrl="${test.baseUrl}",poolName="test1")publicinterfaceHttpApi{@GET("person")ResultgetPerson(@Query("id")Longid);}log打印很多情况接下来,我们希望记录http请求日志。通过@RetrofitClient的logLevel和logStrategy属性,可以指定各个接口的日志打印级别和日志打印策略。retrofit-spring-boot-starter支持5种日志打印级别(ERROR、WARN、INFO、DEBUG、TRACE),默认为INFO;支持4种日志打印策略(NONE、BASIC、HEADERS、BODY),默认为BASIC。四种日志打印策略的含义如下:NONE:无日志。BASIC:记录请求和响应行。HEADERS:记录请求和响应行及其各自的标头。BODY:记录请求和响应行及其各自的header和body(如果存在)。retrofit-spring-boot-starter默认使用DefaultLoggingInterceptor来执行真正的日志打印功能,其底层是okhttp原生的HttpLoggingInterceptor。当然你也可以自定义实现自己的日志打印拦截器,只需要继承BaseLoggingInterceptor(具体可以参考DefaultLoggingInterceptor的实现),然后在配置文件中进行配置。retrofit:#logprintinginterceptorlogging-interceptor:com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultLoggingInterceptorHttp异常信息格式化器当发生http请求异常时,原始的异常信息可能不便于阅读,所以retrofit-spring-boot-starter提供Http异常信息格式化器,用于美化http请求参数的输出。默认情况下,DefaultHttpExceptionMessageFormatter用于格式化请求数据。JavaMind公众号回复“后端面试”,给你发了Java面试题集锦。您也可以自定义它。只需要继承BaseHttpExceptionMessageFormatter,然后配置即可。retrofit:#Httpexceptionmessageformatterhttp-exception-message-formatter:com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultHttpExceptionMessageFormatter调用适配器CallAdapterRetrofit可以通过调用适配器CallAdapterFactory将Call对象适配为一个接口方法的返回类型。retrofit-spring-boot-starter扩展了两个CallAdapterFactory的实现:BodyCallAdapterFactory默认是开启的,可以通过配置retrofit.enable-body-call-adapter=false来关闭http请求的同步执行,响应body内容适配返回值接口方法实例的类型。除了Retrofit.Call、Retrofit.Response、java.util.concurrent.CompletableFuture,其他返回类型都可以使用这个适配器。ResponseCallAdapterFactory默认开启,可以通过配置retrofit.enable-response-call-adapter=false关闭,同步执行http请求,适配Retrofit.Response返回的responsebody内容。如果方法的返回值类型为Retrofit.Response,则可以使用此适配器。Retrofit根据方法返回值类型自动选择对应的CallAdapterFactory进行适配处理!除了Retrofit默认的CallAdapterFactory,可以支持多种形式的方法返回值类型:Call:不做适配处理,直接返回Call对象CompletableFuture:适配响应体内容CompletableFuture对象返回Void:无论返回类型如何,都可以使用Void。如果http状态码不是2xx,直接抛错!Response:将响应内容适配成Response对象并返回。任何其他Java类型:将响应体内容适配成对应的Java类型对象并返回。如果http状态码不是2xx,直接抛错!/***Call*不做适配处理,直接返回Call对象*@paramid*@return*/@GET("person")Call>getPersonCall(@Query("id")Longid);/***CompletableFuture*将响应体内容适配成CompletableFuture对象并返回*@paramid*@return*/@GET("person")CompletableFuture>getPersonCompletableFuture(@Query("id")Longid);/***Void*不用注意返回类型就可以使用void。如果http状态码不是2xx,直接抛错!*@paramid*@return*/@GET("person")VoidgetPersonVoid(@Query("id")Longid);/***Response*将响应内容适配成Response对象并返回*@paramid*@return*/@GET("person")Response>getPersonResponse(@Query("id")Longid);/***AnyotherJavatype*将响应体的内容适配成返回一个对应的Java类型对象,如果http状态码不是2xx,直接抛出错误!*@paramid*@return*/@GET("person")ResultgetPerson(@Query("id")Longid);我们也可以通过扩展CallAdapter.Factory来实现我们自己的CallAdapter;然后自定义CallAdapterFactory配置为springbean!自定义配置的CallAdapter.Factory具有更高的优先级!数据转码器ConverterRetrofi使用Converter将标有@Body注解的对象转为请求体,将响应体数据转为Java对象。可以选择以下转换器:Gson:com.squareup.Retrofit:converter-gsonJackson:com.squareup.Retrofit:converter-jacksonMoshi:com.squareup.Retrofit:converter-moshiProtobuf:com.squareup.Retrofit:converter-protobufWire:com.squareup.Retrofit:converter-wire简单XML:com.squareup.Retrofit:converter-simplexmlretrofit-spring-boot-starter默认使用jackson进行序列化转换!如果需要使用其他序列化方式,在项目中引入相应的依赖,然后将相应的ConverterFactory配置为springbean即可。我们也可以通过继承Converter.Factory扩展来实现自己的Converter;然后自定义Converter.Factory配置为springbean!自定义配置的Converter.Factory具有更高的优先级!全局拦截器BaseGlobalInterceptor如果我们需要对整个系统的http请求进行统一的拦截处理,我们可以自定义全局拦截器BaseGlobalInterceptor,在spring中配置为一个bean!比如我们需要在整个系统发起的http请求中携带源信息。@ComponentpublicclassSourceInterceptorextendsBaseGlobalInterceptor{@OverridepublicResponsedoIntercept(Chainchain)throwsIOException{Requestrequest=chain.request();RequestnewReq=request.newBuilder().addHeader("source","test").build();returnchain.proceed;}(newReq)结论至此,spring-boot项目下最优雅的http客户端工具介绍就结束了。更详细的信息可以参考官方文档:retrofit和retrofit-spring-boot-starter。