点击上方蓝色“方志朋”,选择“设为星标”回复“666”获取独家整理的学习资料!

SpringBoot项目直接使用okhttphttpClient或者RestTemplate发起HTTP请求,既繁琐又不方便统一管理。因此,在这里推荐一个适用于SpringBoot项目的轻量级HTTP客户端框架retrofit-spring-boot-starter,使用非常简单方便,同时又提供诸多功能增强。目前项目已经更新至2.2.2版本,并且会持续进行迭代优化。

前言

Retrofit是适用于AndroidJava且类型安全的HTTP客户端,其最大的特性的是支持通过接口的方式发起HTTP请求。而spring-boot是使用最广泛的Java开发框架,但是Retrofit官方没有支持与spring-boot框架快速整合,因此我们开发了retrofit-spring-boot-starter

retrofit-spring-boot-starter实现了Retrofitspring-boot框架快速整合,并且支持了诸多功能增强,极大简化开发

????项目持续优化迭代,欢迎大家提ISSUE和PR!麻烦大家能给一颗star✨,您的star是我们持续更新的动力!

功能特性

  • [x] 自定义注入OkHttpClient

  • [x] 注解式拦截器

  • [x] 连接池管理

  • [x] 日志打印

  • [x] 请求重试

  • [x] 错误解码器

  • [x] 全局拦截器

  • [x] 熔断降级

  • [x] 微服务之间的HTTP调用

  • [x] 调用适配器

  • [x] 数据转换器

快速使用

引入依赖

<dependency><groupId>com.github.lianjiatech</groupId><artifactId>retrofit-spring-boot-starter</artifactId><version>2.2.2</version>
</dependency>
复制代码

定义http接口

接口必须使用@RetrofitClient注解标记!http相关注解可参考官方文档:retrofit官方文档。

@RetrofitClient(baseUrl = "${test.baseUrl}")
public interface HttpApi {@GET("person")Result<Person> getPerson(@Query("id") Long id);
}
复制代码

注入使用

将接口注入到其它Service中即可使用!

@Service
public class TestService {@Autowiredprivate HttpApi httpApi;public void test() {// 通过httpApi发起http请求}
}
复制代码

HTTP请求相关注解

HTTP请求相关注解,全部使用了retrofit原生注解。详细信息可参考官方文档:retrofit官方文档,以下是一个简单说明。

注解分类 支持的注解
请求方式 @GET @HEAD @POST @PUT @DELETE @OPTIONS
请求头 @Header @HeaderMap @Headers
Query参数 @Query @QueryMap @QueryName
path参数 @Path
form-encoded参数 @Field @FieldMap @FormUrlEncoded
文件上传 @Multipart @Part @PartMap
url参数 @Url

配置项说明

retrofit-spring-boot-starter支持了多个可配置的属性,用来应对不同的业务场景。您可以视情况进行修改,具体说明如下:

配置 默认值 说明
enable-log true 启用日志打印
logging-interceptor DefaultLoggingInterceptor 日志打印拦截器
pool 连接池配置
disable-void-return-type false 禁用java.lang.Void返回类型
retry-interceptor DefaultRetryInterceptor 请求重试拦截器
global-converter-factories JacksonConverterFactory 全局转换器工厂
global-call-adapter-factories BodyCallAdapterFactory,ResponseCallAdapterFactory 全局调用适配器工厂
enable-degrade false 是否启用熔断降级
degrade-type sentinel 熔断降级实现方式(目前仅支持Sentinel)
resource-name-parser DefaultResourceNameParser 熔断资源名称解析器,用于解析资源名称

yml配置方式:

retrofit:enable-response-call-adapter: true# 启用日志打印enable-log: true# 连接池配置pool:test1:max-idle-connections: 3keep-alive-second: 100test2:max-idle-connections: 5keep-alive-second: 50# 禁用void返回值类型disable-void-return-type: false# 日志打印拦截器logging-interceptor: com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultLoggingInterceptor# 请求重试拦截器retry-interceptor: com.github.lianjiatech.retrofit.spring.boot.retry.DefaultRetryInterceptor# 全局转换器工厂global-converter-factories:- retrofit2.converter.jackson.JacksonConverterFactory# 全局调用适配器工厂global-call-adapter-factories:- com.github.lianjiatech.retrofit.spring.boot.core.BodyCallAdapterFactory- com.github.lianjiatech.retrofit.spring.boot.core.ResponseCallAdapterFactory# 是否启用熔断降级enable-degrade: true# 熔断降级实现方式degrade-type: sentinel# 熔断资源名称解析器resource-name-parser: com.github.lianjiatech.retrofit.spring.boot.degrade.DefaultResourceNameParser
复制代码

高级功能

自定义注入OkHttpClient

通常情况下,通过@RetrofitClient注解属性动态创建OkHttpClient对象能够满足大部分使用场景。但是在某些情况下,用户可能需要自定义OkHttpClient,这个时候,可以在接口上定义返回类型是OkHttpClient.Builder的静态方法来实现。代码示例如下:

@RetrofitClient(baseUrl = "http://ke.com")
public interface HttpApi3 {@OkHttpClientBuilderstatic OkHttpClient.Builder okhttpClientBuilder() {return new OkHttpClient.Builder().connectTimeout(1, TimeUnit.SECONDS).readTimeout(1, TimeUnit.SECONDS).writeTimeout(1, TimeUnit.SECONDS);}@GETResult<Person> getPerson(@Url String url, @Query("id") Long id);
}
复制代码

方法必须使用@OkHttpClientBuilder注解标记!

注解式拦截器

很多时候,我们希望某个接口下的某些http请求执行统一的拦截处理逻辑。为了支持这个功能,retrofit-spring-boot-starter提供了注解式拦截器,做到了基于url路径的匹配拦截。使用的步骤主要分为2步:

  1. 继承BasePathMatchInterceptor编写拦截处理器;

  2. 接口上使用@Intercept进行标注。如需配置多个拦截器,在接口上标注多个@Intercept注解即可!

下面以_给指定请求的url后面拼接timestamp时间戳_为例,介绍下如何使用注解式拦截器。

继承BasePathMatchInterceptor编写拦截处理器

@Component
public class TimeStampInterceptor extends BasePathMatchInterceptor {@Overridepublic Response doIntercept(Chain chain) throws IOException {Request request = chain.request();HttpUrl url = request.url();long timestamp = System.currentTimeMillis();HttpUrl newUrl = url.newBuilder().addQueryParameter("timestamp", String.valueOf(timestamp)).build();Request newRequest = request.newBuilder().url(newUrl).build();return chain.proceed(newRequest);}
}复制代码

接口上使用@Intercept进行标注

@RetrofitClient(baseUrl = "${test.baseUrl}")
@Intercept(handler = TimeStampInterceptor.class, include = {"/api/**"}, exclude = "/api/test/savePerson")
public interface HttpApi {@GET("person")Result<Person> getPerson(@Query("id") Long id);@POST("savePerson")Result<Person> savePerson(@Body Person person);
}
复制代码

上面的@Intercept配置表示:拦截HttpApi接口下/api/**路径下(排除/api/test/savePerson)的请求,拦截处理器使用TimeStampInterceptor

扩展注解式拦截器

有的时候,我们需要在拦截注解动态传入一些参数,然后再执行拦截的时候需要使用这个参数。这种时候,我们可以扩展实现自定义拦截注解自定义拦截注解必须使用@InterceptMark标记,并且注解中必须包括include()、exclude()、handler()属性信息。使用的步骤主要分为3步:

  1. 自定义拦截注解

  2. 继承BasePathMatchInterceptor编写拦截处理器

  3. 接口上使用自定义拦截注解;

例如我们需要在请求头里面动态加入accessKeyIdaccessKeySecret签名信息才能正常发起http请求,这个时候可以自定义一个加签拦截器注解@Sign来实现。下面以自定义@Sign拦截注解为例进行说明。

自定义@Sign注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@InterceptMark
public @interface Sign {/*** 密钥key* 支持占位符形式配置。** @return*/String accessKeyId();/*** 密钥* 支持占位符形式配置。** @return*/String accessKeySecret();/*** 拦截器匹配路径** @return*/String[] include() default {"/**"};/*** 拦截器排除匹配,排除指定路径拦截** @return*/String[] exclude() default {};/*** 处理该注解的拦截器类* 优先从spring容器获取对应的Bean,如果获取不到,则使用反射创建一个!** @return*/Class<? extends BasePathMatchInterceptor> handler() default SignInterceptor.class;
}
复制代码

扩展自定义拦截注解有以下2点需要注意:

  1. 自定义拦截注解必须使用@InterceptMark标记。

  2. 注解中必须包括include()、exclude()、handler()属性信息。

实现SignInterceptor

@Component
public class SignInterceptor extends BasePathMatchInterceptor {private String accessKeyId;private String accessKeySecret;public void setAccessKeyId(String accessKeyId) {this.accessKeyId = accessKeyId;}public void setAccessKeySecret(String accessKeySecret) {this.accessKeySecret = accessKeySecret;}@Overridepublic Response doIntercept(Chain chain) throws IOException {Request request = chain.request();Request newReq = request.newBuilder().addHeader("accessKeyId", accessKeyId).addHeader("accessKeySecret", accessKeySecret).build();return chain.proceed(newReq);}
}
复制代码

上述accessKeyIdaccessKeySecret字段值会依据@Sign注解的accessKeyId()accessKeySecret()值自动注入,如果@Sign指定的是占位符形式的字符串,则会取配置属性值进行注入。另外,accessKeyIdaccessKeySecret字段必须提供setter方法

接口上使用@Sign

@RetrofitClient(baseUrl = "${test.baseUrl}")
@Sign(accessKeyId = "${test.accessKeyId}", accessKeySecret = "${test.accessKeySecret}", exclude = {"/api/test/person"})
public interface HttpApi {@GET("person")Result<Person> getPerson(@Query("id") Long id);@POST("savePerson")Result<Person> savePerson(@Body Person person);
}
复制代码

这样就能在指定url的请求上,自动加上签名信息了。

连接池管理

默认情况下,所有通过Retrofit发送的http请求都会使用max-idle-connections=5 keep-alive-second=300的默认连接池。当然,我们也可以在配置文件中配置多个自定义的连接池,然后通过@RetrofitClientpoolName属性来指定使用。比如我们要让某个接口下的请求全部使用poolName=test1的连接池,代码实现如下:

  1. 配置连接池。

    retrofit:
    # 连接池配置
    pool:test1:max-idle-connections: 3keep-alive-second: 100test2:max-idle-connections: 5keep-alive-second: 50
    复制代码
    
  2. 通过@RetrofitClientpoolName属性来指定使用的连接池。

    @RetrofitClient(baseUrl = "${test.baseUrl}", poolName="test1")
    public interface HttpApi {@GET("person")
    Result<Person> getPerson(@Query("id") Long id);
    }
    复制代码
    

日志打印

很多情况下,我们希望将http请求日志记录下来。通过retrofit.enableLog配置可以全局控制日志是否开启。针对每个接口,可以通过@RetrofitClientenableLog控制是否开启,通过logLevellogStrategy,可以指定每个接口的日志打印级别以及日志打印策略。retrofit-spring-boot-starter支持了5种日志打印级别(ERROR, WARN, INFO, DEBUG, TRACE),默认INFO;支持了4种日志打印策略(NONE, BASIC, HEADERS, BODY),默认BASIC。4种日志打印策略含义如下:

  1. NONE:No logs.

  2. BASIC:Logs request and response lines.

  3. HEADERS:Logs request and response lines and their respective headers.

  4. BODY:Logs request and response lines and their respective headers and bodies (if present).

retrofit-spring-boot-starter默认使用了DefaultLoggingInterceptor执行真正的日志打印功能,其底层就是okhttp原生的HttpLoggingInterceptor。当然,你也可以自定义实现自己的日志打印拦截器,只需要继承BaseLoggingInterceptor(具体可以参考DefaultLoggingInterceptor的实现),然后在配置文件中进行相关配置即可。

retrofit:# 日志打印拦截器logging-interceptor: com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultLoggingInterceptor
复制代码

请求重试

retrofit-spring-boot-starter支持请求重试功能,只需要在接口或者方法上加上@Retry注解即可。@Retry支持重试次数maxRetries、重试时间间隔intervalMs以及重试规则retryRules配置。重试规则支持三种配置:

  1. RESPONSE_STATUS_NOT_2XX:响应状态码不是2xx时执行重试;

  2. OCCUR_IO_EXCEPTION:发生IO异常时执行重试;

  3. OCCUR_EXCEPTION:发生任意异常时执行重试;

默认响应状态码不是2xx或者发生IO异常时自动进行重试。需要的话,你也可以继承BaseRetryInterceptor实现自己的请求重试拦截器,然后将其配置上去。

retrofit:# 请求重试拦截器retry-interceptor: com.github.lianjiatech.retrofit.spring.boot.retry.DefaultRetryInterceptor
复制代码

错误解码器

HTTP发生请求错误(包括发生异常或者响应数据不符合预期)的时候,错误解码器可将HTTP相关信息解码到自定义异常中。你可以在@RetrofitClient注解的errorDecoder()指定当前接口的错误解码器,自定义错误解码器需要实现ErrorDecoder接口:

/*** 错误解码器。ErrorDecoder.* 当请求发生异常或者收到无效响应结果的时候,将HTTP相关信息解码到异常中,无效响应由业务自己判断** When an exception occurs in the request or an invalid response result is received, the HTTP related information is decoded into the exception,* and the invalid response is determined by the business itself.** @author 陈添明*/
public interface ErrorDecoder {/*** 当无效响应的时候,将HTTP信息解码到异常中,无效响应由业务自行判断。* When the response is invalid, decode the HTTP information into the exception, invalid response is determined by business.** @param request  request* @param response response* @return If it returns null, the processing is ignored and the processing continues with the original response.*/default RuntimeException invalidRespDecode(Request request, Response response) {if (!response.isSuccessful()) {throw RetrofitException.errorStatus(request, response);}return null;}/*** 当请求发生IO异常时,将HTTP信息解码到异常中。* When an IO exception occurs in the request, the HTTP information is decoded into the exception.** @param request request* @param cause   IOException* @return RuntimeException*/default RuntimeException ioExceptionDecode(Request request, IOException cause) {return RetrofitException.errorExecuting(request, cause);}/*** 当请求发生除IO异常之外的其它异常时,将HTTP信息解码到异常中。* When the request has an exception other than the IO exception, the HTTP information is decoded into the exception.** @param request request* @param cause   Exception* @return RuntimeException*/default RuntimeException exceptionDecode(Request request, Exception cause) {return RetrofitException.errorUnknown(request, cause);}}复制代码

全局拦截器

全局应用拦截器

如果我们需要对整个系统的的http请求执行统一的拦截处理,可以自定义实现全局拦截器BaseGlobalInterceptor, 并配置成spring容器中的bean!例如我们需要在整个系统发起的http请求,都带上来源信息。

@Component
public class SourceInterceptor extends BaseGlobalInterceptor {@Overridepublic Response doIntercept(Chain chain) throws IOException {Request request = chain.request();Request newReq = request.newBuilder().addHeader("source", "test").build();return chain.proceed(newReq);}
}
复制代码

全局网络拦截器

只需要实现NetworkInterceptor接口 并配置成spring容器中的bean就支持自动织入全局网络拦截器。

熔断降级

在分布式服务架构中,对不稳定的外部服务进行熔断降级是保证服务高可用的重要措施之一。由于外部服务的稳定性是不能保证的,当外部服务不稳定时,响应时间会变长。相应地,调用方的响应时间也会变长,线程会产生堆积,最终可能耗尽调用方的线程池,导致整个服务不可用。因此我们需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定导致整体服务雪崩。

retrofit-spring-boot-starter支持熔断降级功能,底层基于Sentinel实现。具体来说,支持了熔断资源自发现注解式降级规则配置。如需使用熔断降级,只需要进行以下操作即可:

1. 开启熔断降级功能

默认情况下,熔断降级功能是关闭的,需要设置相应的配置项来开启熔断降级功能

retrofit:# 是否启用熔断降级enable-degrade: true# 熔断降级实现方式(目前仅支持Sentinel)degrade-type: sentinel# 资源名称解析器resource-name-parser: com.github.lianjiatech.retrofit.spring.boot.degrade.DefaultResourceNameParser
复制代码

资源名称解析器用于实现用户自定义资源名称,默认配置是DefaultResourceNameParser,对应的资源名称格式为HTTP_OUT:GET:http://localhost:8080/api/degrade/test。用户可以继承BaseResourceNameParser类实现自己的资源名称解析器。

另外,由于熔断降级功能是可选的,因此启用熔断降级需要用户自行引入Sentinel依赖

<dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-core</artifactId><version>1.6.3</version>
</dependency>
复制代码

2. 配置降级规则(可选)

retrofit-spring-boot-starter支持注解式配置降级规则,通过@Degrade注解来配置降级规则@Degrade注解可以配置在接口或者方法上,配置在方法上的优先级更高。

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@Documented
public @interface Degrade {/*** RT threshold or exception ratio threshold count.*/double count();/*** Degrade recover timeout (in seconds) when degradation occurs.*/int timeWindow() default 5;/*** Degrade strategy (0: average RT, 1: exception ratio).*/DegradeStrategy degradeStrategy() default DegradeStrategy.AVERAGE_RT;
}
复制代码

如果应用项目已支持通过配置中心配置降级规则,可忽略注解式配置方式

3. @RetrofitClient设置fallback或者fallbackFactory (可选)

如果@RetrofitClient不设置fallback或者fallbackFactory,当触发熔断时,会直接抛出RetrofitBlockException异常。用户可以通过设置fallback或者fallbackFactory来定制熔断时的方法返回值fallback类必须是当前接口的实现类,fallbackFactory必须是FallbackFactory<T>实现类,泛型参数类型为当前接口类型。另外,fallbackfallbackFactory实例必须配置成Spring容器的Bean

fallbackFactory相对于fallback,主要差别在于能够感知每次熔断的异常原因(cause)。参考示例如下:

@Slf4j
@Service
public class HttpDegradeFallback implements HttpDegradeApi {@Overridepublic Result<Integer> test() {Result<Integer> fallback = new Result<>();fallback.setCode(100).setMsg("fallback").setBody(1000000);return fallback;}
}
复制代码
@Slf4j
@Service
public class HttpDegradeFallbackFactory implements FallbackFactory<HttpDegradeApi> {/*** Returns an instance of the fallback appropriate for the given cause** @param cause fallback cause* @return 实现了retrofit接口的实例。an instance that implements the retrofit interface.*/@Overridepublic HttpDegradeApi create(Throwable cause) {log.error("触发熔断了! ", cause.getMessage(), cause);return new HttpDegradeApi() {@Overridepublic Result<Integer> test() {Result<Integer> fallback = new Result<>();fallback.setCode(100).setMsg("fallback").setBody(1000000);return fallback;}}
}
复制代码

微服务之间的HTTP调用

为了能够使用微服务调用,需要进行如下配置:

配置ServiceInstanceChooserSpring容器Bean

用户可以自行实现ServiceInstanceChooser接口,完成服务实例的选取逻辑,并将其配置成Spring容器的Bean。对于Spring Cloud应用,retrofit-spring-boot-starter提供了SpringCloudServiceInstanceChooser实现,用户只需将其配置成SpringBean即可。

@Bean
@Autowired
public ServiceInstanceChooser serviceInstanceChooser(LoadBalancerClient loadBalancerClient) {return new SpringCloudServiceInstanceChooser(loadBalancerClient);
}
复制代码

使用@RetrofitserviceIdpath属性,可以实现微服务之间的HTTP调用

@RetrofitClient(serviceId = "${jy-helicarrier-api.serviceId}", path = "/m/count", errorDecoder = HelicarrierErrorDecoder.class)
@Retry
public interface ApiCountService {}
复制代码

调用适配器和数据转码器

调用适配器

Retrofit可以通过调用适配器CallAdapterFactoryCall<T>对象适配成接口方法的返回值类型。retrofit-spring-boot-starter扩展2种CallAdapterFactory实现:

  1. BodyCallAdapterFactory

  • 默认启用,可通过配置retrofit.enable-body-call-adapter=false关闭

  • 同步执行http请求,将响应体内容适配成接口方法的返回值类型实例。

  • 除了Retrofit.Call<T>Retrofit.Response<T>java.util.concurrent.CompletableFuture<T>之外,其它返回类型都可以使用该适配器。

  • ResponseCallAdapterFactory

    • 默认启用,可通过配置retrofit.enable-response-call-adapter=false关闭

    • 同步执行http请求,将响应体内容适配成Retrofit.Response<T>返回。

    • 如果方法的返回值类型为Retrofit.Response<T>,则可以使用该适配器。

    Retrofit自动根据方法返回值类型选用对应的CallAdapterFactory执行适配处理!加上Retrofit默认的CallAdapterFactory,可支持多种形式的方法返回值类型:

    • Call<T>: 不执行适配处理,直接返回Call<T>对象

    • CompletableFuture<T>: 将响应体内容适配成CompletableFuture<T>对象返回

    • Void: 不关注返回类型可以使用Void。如果http状态码不是2xx,直接抛错!

    • Response<T>: 将响应内容适配成Response<T>对象返回

    • 其他任意Java类型:将响应体内容适配成一个对应的Java类型对象返回,如果http状态码不是2xx,直接抛错!

        /*** Call<T>* 不执行适配处理,直接返回Call<T>对象* @param id* @return*/@GET("person")Call<Result<Person>> getPersonCall(@Query("id") Long id);/***  CompletableFuture<T>*  将响应体内容适配成CompletableFuture<T>对象返回* @param id* @return*/@GET("person")CompletableFuture<Result<Person>> getPersonCompletableFuture(@Query("id") Long id);/*** Void* 不关注返回类型可以使用Void。如果http状态码不是2xx,直接抛错!* @param id* @return*/@GET("person")Void getPersonVoid(@Query("id") Long id);/***  Response<T>*  将响应内容适配成Response<T>对象返回* @param id* @return*/@GET("person")Response<Result<Person>> getPersonResponse(@Query("id") Long id);/*** 其他任意Java类型* 将响应体内容适配成一个对应的Java类型对象返回,如果http状态码不是2xx,直接抛错!* @param id* @return*/@GET("person")Result<Person> getPerson(@Query("id") Long id);复制代码
    

    我们也可以通过继承CallAdapter.Factory扩展实现自己的CallAdapter

    retrofit-spring-boot-starter支持通过retrofit.global-call-adapter-factories配置全局调用适配器工厂,工厂实例优先从Spring容器获取,如果没有获取到,则反射创建。默认的全局调用适配器工厂是[BodyCallAdapterFactory, ResponseCallAdapterFactory]

    retrofit:# 全局调用适配器工厂global-call-adapter-factories:- com.github.lianjiatech.retrofit.spring.boot.core.BodyCallAdapterFactory- com.github.lianjiatech.retrofit.spring.boot.core.ResponseCallAdapterFactory
    复制代码
    

    针对每个Java接口,还可以通过@RetrofitClient注解的callAdapterFactories()指定当前接口采用的CallAdapter.Factory,指定的工厂实例依然优先从Spring容器获取。

    注意:如果CallAdapter.Factory没有public的无参构造器,请手动将其配置成Spring容器的Bean对象

    数据转码器

    Retrofit使用Converter@Body注解标注的对象转换成请求体,将响应体数据转换成一个Java对象,可以选用以下几种Converter

    • Gson: com.squareup.Retrofit:converter-gson

    • Jackson: com.squareup.Retrofit:converter-jackson

    • Moshi: com.squareup.Retrofit:converter-moshi

    • Protobuf: com.squareup.Retrofit:converter-protobuf

    • Wire: com.squareup.Retrofit:converter-wire

    • Simple XML: com.squareup.Retrofit:converter-simplexml

    • JAXB: com.squareup.retrofit2:converter-jaxb

    retrofit-spring-boot-starter支持通过retrofit.global-converter-factories配置全局数据转换器工厂,转换器工厂实例优先从Spring容器获取,如果没有获取到,则反射创建。默认的全局数据转换器工厂是retrofit2.converter.jackson.JacksonConverterFactory,你可以直接通过spring.jackson.*配置jackson序列化规则,配置可参考Customize the Jackson ObjectMapper!

    retrofit:# 全局转换器工厂global-converter-factories:- retrofit2.converter.jackson.JacksonConverterFactory
    复制代码
    

    针对每个Java接口,还可以通过@RetrofitClient注解的converterFactories()指定当前接口采用的Converter.Factory,指定的转换器工厂实例依然优先从Spring容器获取。

    注意:如果Converter.Factory没有public的无参构造器,请手动将其配置成Spring容器的Bean对象

    总结

    retrofit-spring-boot-starter一个适用于SpringBoot项目的轻量级HTTP客户端框架,已在线上稳定运行一年多,并且已经有多个外部公司也接入使用。有兴趣的朋友可以尝试一下,如有任何问题,欢迎提issue或者加QQ群(806714302)反馈,极速响应支持。

    来源:https://juejin.cn/post/6898485806587969544

    在这里,我为大家准备了一份2021年最新最全的《史上最简单的java面试题》,这套电子书涵盖了诸多java技术栈的面试知识题,是作者面试BAT大厂的前的总结,作者顺利拿下AT的offer,相信可以帮助大家在最短的时间内复习Java后端的大多数面试题,从而拿到自己心仪的offer。

    截了张图,大家可以仔细查看左边的菜单栏,覆盖的知识面真的很广,而且质量都很不错。

    资料获取方法扫描下方二维码
    后台回复关键词:BAT
    明天见(。・ω・。)
    

我终于决定要放弃 okhttp、httpClient,选择了这个牛逼的神仙工具!贼爽!相关推荐

  1. 我终于决定要放弃 okhttp、httpClient,选择了这个牛逼的神仙工具!贼爽

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 在SpringBoot项目直接使用okhttp.httpC ...

  2. 放弃 PHP,选择 Node.JS 的 8 个理由!

    [CSDN 编者按]谈到 PHP 逐渐被看衰这个现象,我想起前两天看到一篇浏览器领域龙头老大的争夺战的有趣报道,报道里大致讲了几个浏览器奋发图强夺得宝座之后又不思进取,然后逐渐被超越的演化史.更往远看 ...

  3. 放弃Dubbo,选择最流行的Spring Cloud微服务架构实践与经验总结

    Spring Cloud 在国内中小型公司能用起来吗?从 2016 年初一直到现在,我们在这条路上已经走了一年多. 在使用 Spring Cloud 之前,我们对微服务实践是没有太多的体会和经验的.从 ...

  4. 放弃 Notepad++,事实证明,还有 5 款更牛逼……

    点击关注公众号,Java干货及时送达 今天跟大家聊一聊,作为文本编辑工具,还有比 Notepad++ 更好的替代工具吗?别说,还真有另外5款(为啥有些粉丝说,要卸载Notepad++ 呢,其实这个No ...

  5. HighNewTech:低代码(0代码/无代码,无需代码)开发的简介以及如何选择最合适的低代码开发工具

    High&NewTech:低代码(0代码/无代码,无需代码)开发的简介以及如何选择最合适的低代码开发工具 导读:在互联网时代,博主经常反思一个问题,如何跟进这个快速发展的时代才能不会被淘汰?博 ...

  6. 如何选择一个合适的大数据可视化工具

    高质量的可视化工具对数据分析是必不可少的.数据可视化工具是一种应用软件,它帮助用户以可视化和图形化的格式显示数据,并提供数据的完整轮廓.饼图.曲线.热图.直方图.雷达/蜘蛛图只是可视化的一小部分,这些 ...

  7. 还在付费使用 XShell?我选择这款超牛逼的 SSH 客户端,完全免费!

    点击关注公众号,回复"1024"获取2TB学习资源! 分享过 FinallShell 这款 SSH 客户端,也是我目前常用的 SSH 客户端工具,FinalShell 使用起来方便 ...

  8. 阿里内部的那个牛逼带闪电的Java诊断工具arthas终于开源了

    在阿里巴巴内部,有很多自研工具供开发者使用,其中有一款工具,是几乎每个Java开发都使用过的工具,那就是Arthas,这是一款Java诊断工具,是一款牛逼带闪电的工具.该工具已于2018年9月份开源. ...

  9. 不用 Notepad++,还有更牛逼的选择!

    来源:oschina.net/news/110987/no-notepad-plus-plus 这两天 Notepad++ 牛逼了,然后引发了大家的关注,具体事件内容请大家自行百度,其实作为文本编辑工 ...

最新文章

  1. php邮箱地址验证,js和php邮箱地址验证的实现方法
  2. 我的世界java和网易能联机吗_网易我的世界单机怎么转联机
  3. 失败如何助你升入最高管理层
  4. an导入html5,H5-FLASH:AN HTML5-BASED FLASH RUNTIME
  5. 计算机驱动空间不够,Win8.1系统如何释放驱动器空间解决可用空间不足问题
  6. python中import sys_python import sys出错怎么办
  7. 信息学奥赛一本通(1065:奇数求和)
  8. 分布式事务模型--最大努力通知型分布式事务
  9. ubuntu pycharm mysql_ubuntu安装mysql pycharm sublime
  10. 测试sql server服务是否配置正确
  11. 前端学HTTP之网络基础
  12. LoadRunner11的安装与破解
  13. Esp8266对接阿里云物联网远程实时温度监控{探讨帖}
  14. 中国象棋棋盘java_JAVA中用程序绘制国际象棋与中国象棋棋盘
  15. win10 插上有线耳机 无声音:装上声卡驱动【检查】
  16. Android中把图片、视频保存到相册中
  17. wmware网络模式
  18. 对于python中range函数的使用
  19. java 计算π_Java实现计算圆周率π的两种方法 - 博客频道 - CSDN.NET
  20. QT设置非主窗口的背景图片和主窗口的背景图片(详细版)

热门文章

  1. 线程和进程的自己觉得好的文章以及自我理解
  2. OpenJDK 编译-Linux环境
  3. 1282. Game Tree
  4. asp.net 操作excel的实现代码
  5. HDOJ2270(How Many Friends Will Be Together With You)
  6. 桌面图标不透明的小问题处理
  7. 20个案例详解 Pandas 当中的数据统计分析与排序
  8. Go modules基础精进,六大核心概念全解析(上)
  9. Python 搭建车道智能检测系统
  10. 创新工场提出中文分词和词性标注模型,性能分别刷新五大数据集| ACL 2020​