HttpClient is Outdated, OkHttp is Losing Ground! This Tool is the New King!

HttpClient is Outdated, OkHttp is Losing Ground! This Tool is the New King!We are dedicated to exploring, sharing, and recommending the latest practical technology stacks, open-source projects, frameworks, and useful tools. Fresh open-source information awaits your discovery every day!

Using <span>SpringBoot</span> directly with <span>OkHttp</span>, <span>HttpClient</span>, or <span>RestTemplate</span> to initiate <span>HTTP</span> requests is cumbersome and inconvenient for unified management. Therefore, we recommend a lightweight HTTP client framework suitable for <span>SpringBoot</span> projects: <span>retrofit-spring-boot-starter</span>, which is very simple and convenient to use while providing many functional enhancements. The project has been updated to version <span>2.2.2</span> and will continue to be iterated and optimized.

Introduction

<span>Retrofit</span> is a type-safe HTTP client for <span>Android</span> and <span>Java</span>, with its biggest feature being support for initiating HTTP requests via<strong><span> interfaces</span></strong>. Meanwhile, <span>spring-boot</span> is the most widely used Java development framework, but the official <span>Retrofit</span> does not support quick integration with the <span>spring-boot</span> framework, which is why we developed <span>retrofit-spring-boot-starter</span>.

<strong><span>retrofit-spring-boot-starter</span></strong> achieves quick integration of <strong><span>Retrofit</span></strong> with the <strong><span>spring-boot</span></strong> framework and supports many functional enhancements, greatly simplifying development.

Features

  • Custom injection of OkHttpClient
  • Annotation-based interceptors
  • Connection pool management
  • Logging
  • Request retries
  • Error decoders
  • Global interceptors
  • Circuit breaker and degradation
  • HTTP calls between microservices
  • Call adapters
  • Data converters

Quick Start

Include Dependency

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

Define HTTP Interface

The interface must be marked with the<strong><span>@RetrofitClient</span></strong> annotation! HTTP-related annotations can refer to the official documentation: Retrofit official documentation.

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

Inject and Use

Simply inject the interface into other services to use!

@Service
public class TestService {
    @Autowired
    private HttpApi httpApi;
    public void test() {
        // Initiate HTTP request through httpApi
    }
}

HTTP Request Related Annotations

<span>HTTP</span> request-related annotations all use the <span>retrofit</span> native annotations. For detailed information, refer to the official documentation: Retrofit official documentation, below is a simple explanation.

Annotation Category Supported Annotations
Request Methods @GET @HEAD @POST @PUT @DELETE @OPTIONS
Request Headers @Header @HeaderMap @Headers
Query Parameters @Query @QueryMap @QueryName
Path Parameters @Path
Form-encoded Parameters @Field @FieldMap @FormUrlEncoded
File Upload @Multipart @Part @PartMap
URL Parameters @Url

Configuration Item Description

<span>retrofit-spring-boot-starter</span> supports multiple configurable properties to handle different business scenarios. You can modify them as needed, specific descriptions are as follows:

If you are preparing for an interview or job change, it is recommended to practice online at ddkk.com, covering over 10,000 Java interview questions, almost all mainstream technical interview questions, and the most comprehensive technical resources available on the market, along with high-quality tutorial series, provided for free.

Configuration Default Value Description
enable-log true Enable logging
logging-interceptor DefaultLoggingInterceptor Logging interceptor
pool Connection pool configuration
disable-void-return-type false Disable java.lang.Void return type
retry-interceptor DefaultRetryInterceptor Request retry interceptor
global-converter-factories JacksonConverterFactory Global converter factory
global-call-adapter-factories BodyCallAdapterFactory, ResponseCallAdapterFactory Global call adapter factory
enable-degrade false Whether to enable circuit breaker degradation
degrade-type sentinel Circuit breaker degradation implementation method (currently only supports Sentinel)
resource-name-parser DefaultResourceNameParser Circuit breaker resource name parser, used to parse resource names

<span>yml</span> configuration method:

retrofit:
  enable-response-call-adapter: true
  # Enable logging
  enable-log: true
  # Connection pool configuration
  pool:
    test1:
      max-idle-connections: 3
      keep-alive-second: 100
    test2:
      max-idle-connections: 5
      keep-alive-second: 50
  # Disable void return type
  disable-void-return-type: false
  # Logging interceptor
  logging-interceptor: com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultLoggingInterceptor
  # Request retry interceptor
  retry-interceptor: com.github.lianjiatech.retrofit.spring.boot.retry.DefaultRetryInterceptor
  # Global converter factory
  global-converter-factories:
    - retrofit2.converter.jackson.JacksonConverterFactory
  # Global call adapter factory
  global-call-adapter-factories:
    - com.github.lianjiatech.retrofit.spring.boot.core.BodyCallAdapterFactory
    - com.github.lianjiatech.retrofit.spring.boot.core.ResponseCallAdapterFactory
  # Whether to enable circuit breaker degradation
  enable-degrade: true
  # Circuit breaker degradation implementation method
  degrade-type: sentinel
  # Circuit breaker resource name parser
  resource-name-parser: com.github.lianjiatech.retrofit.spring.boot.degrade.DefaultResourceNameParser

Advanced Features

Custom Injection of OkHttpClient

In most cases, dynamically creating the <span>OkHttpClient</span> object through the properties of the <span>@RetrofitClient</span> annotation can meet most usage scenarios. However, in some cases, users may need to customize the <span>OkHttpClient</span>, at which point, you can define a static method returning type <span>OkHttpClient.Builder</span> on the interface to achieve this. The code example is as follows:

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

The method must be marked with the <span>@OkHttpClientBuilder</span> annotation!

Annotation-based Interceptors

Many times, we want certain HTTP requests under a specific interface to execute unified interception logic. To support this functionality, <span>retrofit-spring-boot-starter</span> provides annotation-based interceptors, achieving URL path-based matching interception. The steps to use it are mainly divided into 2 steps:

1. Inherit <span>BasePathMatchInterceptor</span> to write the interceptor;2. Use <span>@Intercept</span> to annotate the interface. If you need to configure multiple interceptors, simply annotate multiple <span>@Intercept</span> annotations on the interface!

Below is an example of how to use annotation-based interceptors to append a timestamp to a specified request URL.

Inherit<strong><span>BasePathMatchInterceptor</span></strong> to Write the Interceptor

@Component
public class TimeStampInterceptor extends BasePathMatchInterceptor {
    @Override
    public 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);
    }
}

Use<strong><span>@Intercept</span></strong> to Annotate the Interface

@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);
}

The above <span>@Intercept</span> configuration indicates: intercept requests under the <span>HttpApi</span> interface at the <span>/api/**</span> path (excluding <span>/api/test/savePerson</span>), using the <span>TimeStampInterceptor</span> interceptor.

Extend Annotation-based Interceptors

Sometimes, we need to dynamically pass some parameters into the intercept annotation, and then use these parameters during interception. In such cases, we can extend to implement custom intercept annotations.<span>Custom intercept annotations</span> must be marked with <span>@InterceptMark</span>, and the annotation must include include(), exclude(), handler() attribute information. The steps to use are mainly divided into 3 steps:

1. Custom Intercept Annotation 2. Inherit <span>BasePathMatchInterceptor</span> to write the interceptor3. Use the custom intercept annotation on the interface;

For example, if we need to dynamically add accessKeyId, accessKeySecret signature information in the request header to initiate HTTP requests normally, we can define a signing interceptor annotation @Sign to achieve this. Below is an example of the custom <span>@Sign</span> intercept annotation.

Custom<strong><span>@Sign</span></strong> Annotation

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@InterceptMark
public @interface Sign {
    /**
     * Secret key
     * Supports placeholder configuration.
     *
     * @return
     */
    String accessKeyId();
    /**
     * Secret
     * Supports placeholder configuration.
     *
     * @return
     */
    String accessKeySecret();
    /**
     * Interceptor matching path
     *
     * @return
     */
    String[] include() default {"/**"};
    /**
     * Interceptor exclusion matching, exclude specified path interception
     *
     * @return
     */
    String[] exclude() default {};
    /**
     * Interceptor class that handles this annotation
     * Priority is given to obtaining the corresponding Bean from the spring container, if not found, it will be created using reflection!
     *
     * @return
     */
    Class<? extends BasePathMatchInterceptor> handler() default SignInterceptor.class;
}

There are two points to note about extending <span>custom intercept annotations</span>:

1. Custom intercept annotations must be marked with <span>@InterceptMark</span>.

2. The annotation must include <span>include(), exclude(), handler()</span><strong><span> attribute information.</span></strong>

Implement<strong><span>SignInterceptor</span></strong>

@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;
    }
    @Override
    public 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);
    }
}

The above <strong><span>accessKeyId</span></strong> and <strong><span>accessKeySecret</span></strong> field values will be automatically injected based on the <strong><span>@Sign</span></strong> annotation’s <strong><span>accessKeyId()</span></strong> and <strong><span>accessKeySecret()</span></strong> values. If the <strong><span>@Sign</span></strong> specifies a placeholder string, the configuration property value will be used for injection. Additionally, the <strong><span>accessKeyId</span></strong> and <strong><span>accessKeySecret</span></strong> fields must provide <strong><span>setter</span></strong><code><strong><span> methods.</span></strong>

Use on the Interface<strong><span>@Sign</span></strong>

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

This way, signature information will be automatically added to requests at the specified URL.

Connection Pool Management

By default, all HTTP requests sent through <span>Retrofit</span> will use the default connection pool with <span>max-idle-connections=5 keep-alive-second=300</span>. Of course, we can also configure multiple custom connection pools in the configuration file and specify which one to use through the <span>@RetrofitClient</span>’s <span>poolName</span> property. For example, if we want all requests under a certain interface to use the <span>poolName=test1</span> connection pool, the code implementation is as follows:

1. Configure the Connection Pool.

retrofit:
        # Connection pool configuration
        pool:
            test1:
            max-idle-connections: 3
            keep-alive-second: 100
            test2:
            max-idle-connections: 5
            keep-alive-second: 50

2. Specify the Connection Pool to Use via <span>@RetrofitClient</span>’s <span>poolName</span> property.

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

Logging

In many cases, we want to log HTTP request details. The logging can be globally controlled by the <span>retrofit.enableLog</span> configuration. For each interface, you can control whether logging is enabled through the <span>@RetrofitClient</span>’s <span>enableLog</span> property, and specify the logging level and strategy for each interface through <span>logLevel</span> and <span>logStrategy</span>.

<span>retrofit-spring-boot-starter</span> supports 5 logging levels (<span>ERROR</span>, <span>WARN</span>, <span>INFO</span>, <span>DEBUG</span>, <span>TRACE</span>), with the default being <span>INFO</span>; it supports 4 logging strategies (<span>NONE</span>, <span>BASIC</span>, <span>HEADERS</span>, <span>BODY</span>), with the default being <span>BASIC</span>. The meanings of the 4 logging strategies are as follows:

1. <span>NONE</span>: No logs.2. <span>BASIC</span>: Logs request and response lines.3. <span>HEADERS</span>: Logs request and response lines and their respective headers.4. <span>BODY</span>: Logs request and response lines and their respective headers and bodies (if present).

<span>retrofit-spring-boot-starter</span> uses <span>DefaultLoggingInterceptor</span> to perform the actual logging function, which is based on <span>okhttp</span>’s native <span>HttpLoggingInterceptor</span>. Of course, you can also implement your own logging interceptor by inheriting from <span>BaseLoggingInterceptor</span> (for specific implementation, refer to <span>DefaultLoggingInterceptor</span>’s implementation), and then configure it in the configuration file.

retrofit:
  # Logging interceptor
  logging-interceptor: com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultLoggingInterceptor

Request Retries

<span>retrofit-spring-boot-starter</span> supports request retry functionality, simply add the <span>@Retry</span> annotation to the interface or method. <strong><span>@Retry</span></strong> supports retry count<strong><span>maxRetries</span></strong>, retry interval<strong><span>intervalMs</span></strong>, and retry rules<strong><span>retryRules</span></strong> configuration. The retry rules support three configurations:

1. <span>RESPONSE_STATUS_NOT_2XX</span>: Retry when the response status code is not <span>2xx</span>.2. <span>OCCUR_IO_EXCEPTION</span>: Retry when an IO exception occurs.3. <span>OCCUR_EXCEPTION</span>: Retry when any exception occurs.

By default, retries are automatically performed when the response status code is not <span>2xx</span> or when an IO exception occurs. If needed, you can also inherit from <span>BaseRetryInterceptor</span> to implement your own request retry interceptor and configure it.

retrofit:
  # Request retry interceptor
  retry-interceptor: com.github.lianjiatech.retrofit.spring.boot.retry.DefaultRetryInterceptor

Error Decoders

When an HTTP request error occurs (including exceptions or response data not meeting expectations), the error decoder can decode the HTTP-related information into a custom exception. You can specify the current interface’s error decoder in the <span>@RetrofitClient</span> annotation’s <span>errorDecoder()</span><span> method; custom error decoders need to implement the </span><code><span>ErrorDecoder</span> interface:

/**
 * Error decoder. ErrorDecoder.
 * 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 {
    /**
     * 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;
    }
    /**
     * 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);
    }
    /**
     * 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);
    }
}

Global Interceptors

Global Application Interceptors

If we need to perform unified interception processing on all HTTP requests in the system, we can customize and implement a global interceptor <span>BaseGlobalInterceptor</span>, and configure it as a <span>bean</span> in the <span>spring</span> container! For example, if we want all HTTP requests initiated in the system to carry source information.

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

Global Network Interceptors

Simply implement the <span>NetworkInterceptor</span> interface and configure it as a <span>bean</span> in the <span>spring</span> container to support automatic weaving of global network interceptors.

Circuit Breaker and Degradation

In a distributed service architecture, performing circuit breaker degradation on unstable external services is one of the important measures to ensure high service availability. Since the stability of external services cannot be guaranteed, when external services are unstable, response times will increase. Consequently, the response time for the caller will also increase, threads will pile up, and eventually, it may exhaust the caller’s thread pool, leading to the entire service becoming unavailable. Therefore, we need to perform circuit breaker degradation on unstable weak dependency service calls to temporarily cut off unstable calls, preventing local instability from causing an overall service avalanche.

If you are preparing for an interview or job change, it is recommended to practice online at ddkk.com, covering over 10,000 Java interview questions, almost all mainstream technical interview questions, and the most comprehensive technical resources available on the market, along with high-quality tutorial series, provided for free.

<span>retrofit-spring-boot-starter</span> supports circuit breaker degradation functionality, based on Sentinel. Specifically, it supports circuit breaker resource self-discovery and annotation-based degradation rule configuration. To use circuit breaker degradation, simply perform the following operations:

1. Enable Circuit Breaker Degradation Functionality

By default, the circuit breaker degradation functionality is disabled, and you need to set the corresponding configuration items to enable it:

retrofit:
  # Whether to enable circuit breaker degradation
  enable-degrade: true
  # Circuit breaker degradation implementation method (currently only supports Sentinel)
  degrade-type: sentinel
  # Resource name parser
  resource-name-parser: com.github.lianjiatech.retrofit.spring.boot.degrade.DefaultResourceNameParser

The resource name parser is used to implement user-defined resource names, the default configuration is <span>DefaultResourceNameParser</span>, corresponding to the resource name format <span>HTTP_OUT:GET:http://localhost:8080/api/degrade/test</span>. Users can inherit from the <span>BaseResourceNameParser</span> class to implement their own resource name parser.

Additionally, since the circuit breaker degradation functionality is optional, enabling it requires users to manually introduce the Sentinel dependency:

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-core</artifactId>
    <version>1.6.3</version>
</dependency>

2. Configure Degradation Rules (Optional)

<strong><span>retrofit-spring-boot-starter</span></strong> supports annotation-based configuration of degradation rules, configured through the <strong><span>@Degrade</span></strong> annotation. The <span>@Degrade</span> annotation can be configured on interfaces or methods, with higher priority when configured on methods.

@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;
}

If the application project already supports configuring degradation rules through a configuration center, the annotation-based configuration method can be ignored.

3. Set Fallback or FallbackFactory in @RetrofitClient (Optional)

If <span>@RetrofitClient</span> does not set <span>fallback</span> or <span>fallbackFactory</span>, when a circuit breaker is triggered, it will directly throw a <span>RetrofitBlockException</span> exception. Users can customize the return value of the method when the circuit breaker is triggered by setting <span>fallback</span> or <span>fallbackFactory</span>. The <span>fallback</span> class must be an implementation of the current interface, and the <span>fallbackFactory</span> must be an implementation of <span>FallbackFactory<T></span>, with the generic parameter type being the current interface type. Additionally, the <span>fallback</span> and <span>fallbackFactory</span> instances must be configured as <span>Spring</span> container <span>Bean</span> instances.

<strong><span>fallbackFactory</span></strong> differs from <strong><span>fallback</span></strong> mainly in that it can perceive the cause of each circuit breaker (cause). Below is a reference example:

@Slf4j
@Service
public class HttpDegradeFallback implements HttpDegradeApi {
    @Override
    public 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 an instance that implements the retrofit interface.
     */
    @Override
    public HttpDegradeApi create(Throwable cause) {
        log.error("Circuit breaker triggered! ", cause.getMessage(), cause);
        return new HttpDegradeApi() {
            @Override
            public Result<Integer> test() {
                Result<Integer> fallback = new Result<>();
                fallback.setCode(100)
                        .setMsg("fallback")
                        .setBody(1000000);
                return fallback;
            }
        };
    }
}

HTTP Calls Between Microservices

To enable microservice calls, the following configuration is required:

Configure<strong><span>ServiceInstanceChooser</span></strong> as a <strong><span>Spring</span></strong> Container <strong><span>Bean</span></strong>

Users can implement the <span>ServiceInstanceChooser</span> interface to complete the service instance selection logic and configure it as a <span>Spring</span> container <span>Bean</span>. For <span>Spring Cloud</span> applications, <span>retrofit-spring-boot-starter</span> provides the <span>SpringCloudServiceInstanceChooser</span> implementation, and users only need to configure it as a <span>Spring</span> Bean.

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

Using<strong><span>@Retrofit</span></strong>’s <strong><span>serviceId</span></strong> and <strong><span>path</span></strong> Properties, HTTP Calls Between Microservices Can Be Achieved

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

Call Adapters and Data Converters

Call Adapters

<span>Retrofit</span> can adapt <span>Call<T></span> objects to the return value types of interface methods through call adapters <span>CallAdapterFactory</span>. <span>retrofit-spring-boot-starter</span> extends two <span>CallAdapterFactory</span> implementations:

1. <span>BodyCallAdapterFactory</span>

 *  Enabled by default, can be disabled by configuring `retrofit.enable-body-call-adapter=false`
 *  Synchronously executes HTTP requests, adapting the response body content to the return value type instance of the interface method.
 *  Besides `Retrofit.Call<T>`, `Retrofit.Response<T>`, `java.util.concurrent.CompletableFuture<T>`, other return types can use this adapter.

2. <span>ResponseCallAdapterFactory</span>

 *  Enabled by default, can be disabled by configuring `retrofit.enable-response-call-adapter=false`
 *  Synchronously executes HTTP requests, adapting the response body content to return `Retrofit.Response<T>`.
 *  If the method's return type is `Retrofit.Response<T>`, this adapter can be used.

Retrofit automatically selects the corresponding <strong><span>CallAdapterFactory</span></strong> based on the method return value type for adaptation! In addition to the default <strong><span>CallAdapterFactory</span></strong>, it supports various forms of method return value types:

  • <span>Call<T></span>

    : No adaptation processing, directly returns the <span>Call<T></span> object

  • <span>CompletableFuture<T></span>

    : Adapts the response body content to return a <span>CompletableFuture<T></span> object

  • <span>Void</span>

    : Can use <span>Void</span> if the return type is not a concern. If the HTTP status code is not 2xx, it throws an error!

  • <span>Response<T></span>

    : Adapts the response content to return a <span>Response<T></span> object

  • Any other Java type: Adapts the response body content to return a corresponding Java type object, if the HTTP status code is not 2xx, it throws an error!
/**
     * Call<T>
     * No adaptation processing, directly returns Call<T> object
     * @param id
     * @return
     */
    @GET("person")
    Call<Result<Person>> getPersonCall(@Query("id") Long id);
    /**
     *  CompletableFuture<T>
     *  Adapts the response body content to return a CompletableFuture<T> object
     * @param id
     * @return
     */
    @GET("person")
    CompletableFuture<Result<Person>> getPersonCompletableFuture(@Query("id") Long id);
    /**
     * Void
     * Can use Void if the return type is not a concern. If the HTTP status code is not 2xx, it throws an error!
     * @param id
     * @return
     */
    @GET("person")
    Void getPersonVoid(@Query("id") Long id);
    /**
     *  Response<T>
     *  Adapts the response content to return a Response<T> object
     * @param id
     * @return
     */
    @GET("person")
    Response<Result<Person>> getPersonResponse(@Query("id") Long id);
    /**
     * Any other Java type
     * Adapts the response body content to return a corresponding Java type object, if the HTTP status code is not 2xx, it throws an error!
     * @param id
     * @return
     */
    @GET("person")
    Result<Person> getPerson(@Query("id") Long id);

We can also extend and implement our own <strong><span>CallAdapter</span></strong> by inheriting from <strong><span>CallAdapter.Factory</span></strong>!

<span>retrofit-spring-boot-starter</span> supports configuring global call adapter factories through <span>retrofit.global-call-adapter-factories</span>, the factory instance is prioritized to be obtained from the Spring container, and if not obtained, it will be created using reflection. The default global call adapter factories are <span>[BodyCallAdapterFactory, ResponseCallAdapterFactory]</span>!

retrofit:
  # Global call adapter factory
  global-call-adapter-factories:
    - com.github.lianjiatech.retrofit.spring.boot.core.BodyCallAdapterFactory
    - com.github.lianjiatech.retrofit.spring.boot.core.ResponseCallAdapterFactory

For each Java interface, you can also specify the <span>CallAdapter.Factory</span> used by the current interface through the <span>@RetrofitClient</span> annotation’s <span>callAdapterFactories()</span>, the specified factory instance is still prioritized to be obtained from the Spring container.

Note: If the <strong><span>CallAdapter.Factory</span></strong> does not have a public no-argument constructor, please manually configure it as a <strong><span>Spring</span></strong> container Bean object!

Data Converters

<span>Retrofit</span> uses <span>Converter</span> to convert objects annotated with <span>@Body</span> into request bodies and to convert response body data into a <span>Java</span> object. The following converters can be selected:

  • 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

<span>retrofit-spring-boot-starter</span> supports configuring global data converter factories through <span>retrofit.global-converter-factories</span>, the converter factory instance is prioritized to be obtained from the Spring container, and if not obtained, it will be created using reflection. The default global data converter factory is <span>retrofit2.converter.jackson.JacksonConverterFactory</span>, and you can directly configure <span>spring.jackson.*</span> to set Jackson serialization rules, refer to Customize the Jackson ObjectMapper for configuration!

retrofit:
  # Global converter factory
  global-converter-factories:
    - retrofit2.converter.jackson.JacksonConverterFactory

For each Java interface, you can also specify the <span>Converter.Factory</span> used by the current interface through the <span>@RetrofitClient</span> annotation’s <span>converterFactories()</span>, the specified converter factory instance is still prioritized to be obtained from the Spring container.

Note: If the <strong><span>Converter.Factory</span></strong> does not have a public no-argument constructor, please manually configure it as a <strong><span>Spring</span></strong> container Bean object.

—END—This account has collected some learning materials, welcome to obtain: reply 888 to get learning e-books! reply 666 to get video learning resources!HttpClient is Outdated, OkHttp is Losing Ground! This Tool is the New King!Exciting RecommendationsA powerful intelligent operation and maintenance platform open-sourced by Tencent!A tool for taking private jobs, the fastest website building framework in the world! (79.8k stars)36.6k stars, a powerful low-code platform[Zero-code development] Magic-Boot open-source engine: Play enterprise-level development in IDE, efficiency skyrockets![Gitee Hot List] Deep analysis of the open-source low-code tool Jeecg-Boot: Build an enterprise-level system in 3 days!45.4k stars, build an enterprise-level management system in 3 minutes47.8k stars, a tool for taking private jobs, complete enterprise-level authentication in 10 minutes!11.4k stars, a low-intrusion operation and maintenance tool! One-click realization of automated deployment + monitoring!Development scaffolding recommendation, an enterprise-level rapid development platform!Development scaffolding recommendation, no more reinventing the wheel, let Java be “sweet”!Low-code tool, zero-code to build data dashboards and visual reports

Leave a Comment