Ditch OkHttp and HttpClient: This Lightweight HTTP Client Framework is Incredibly Useful!

Using <span>OkHttp</span>, <span>HttpClient</span>, or <span>RestTemplate</span> to initiate <span>HTTP</span> requests directly in a <span>SpringBoot</span> project is cumbersome and inconvenient for unified management. Therefore, I 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 enhanced features. The project has currently been updated to version <span>2.2.2</span> and will continue to be iterated and optimized.

GitHub project address: github.com/LianjiaTech…

Gitee project address: gitee.com/lianjiatech…

Introduction

<span>Retrofit</span> is a type-safe HTTP client for <span>Android</span> and <span>Java</span>, with its biggest feature being its support for initiating HTTP requests via <span>interfaces</span>. 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>.

<span>retrofit-spring-boot-starter</span> achieves quick integration of <span>Retrofit</span> with the <span>spring-boot</span> framework and supports many enhanced features, 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 <span>@RetrofitClient</span> annotation! For HTTP-related annotations, please 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 it!

@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>native Retrofit</span> annotations. For detailed information, please refer to the official documentation: Retrofit official documentation, below is a brief explanation.

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

Configuration Item Description

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

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 Enable circuit breaker and degradation
degrade-type sentinel Circuit breaker implementation method (currently only supports Sentinel)
resource-name-parser DefaultResourceNameParser Circuit breaker resource name parser for parsing 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
  # Enable circuit breaker and degradation
  enable-degrade: true
  # Circuit breaker 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>. In this case, you can define a static method that returns a type of <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 the end of a specified request URL.

Inherit <span>BasePathMatchInterceptor</span> 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 <span>@Intercept</span> 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>.

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 a custom intercept annotation. The <span>custom intercept annotation</span> must be marked with <span>@InterceptMark</span>, and it must include the properties include(), exclude(), handler() in the annotation. The steps to use it are mainly divided into 3 steps:

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

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

Custom <span>@Sign</span> 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 {"/**"};

    /**
     * Exclude matching for the interceptor, exclude specified paths from 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;
}

Extending the <span>custom intercept annotation</span> has the following 2 points to note:

  1. <span>Custom intercept annotation</span> must be marked with <span>@InterceptMark</span>.
  2. The annotation must include <span>include(), exclude(), handler()</span> property information.

Implement <span>SignInterceptor</span>

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

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

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

This way, signature information will be automatically added to requests for 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 specific 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 through the <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 <span>retrofit.enableLog</span> configuration can globally control whether logging is enabled. 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 using <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 a default of <span>INFO</span>; it supports 4 logging strategies (<span>NONE</span>, <span>BASIC</span>, <span>HEADERS</span>, <span>BODY</span>) with a default of <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 the <span>DefaultLoggingInterceptor</span> to perform the actual logging function, which is based on the native <span>HttpLoggingInterceptor</span> of <span>okhttp</span>. Of course, you can also implement your own logging interceptor by inheriting from <span>BaseLoggingInterceptor</span> (for specific implementation, refer to the <span>DefaultLoggingInterceptor</span> 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.<span>@Retry</span> supports retry count <span>maxRetries</span>, retry interval <span>intervalMs</span>, and retry rules <span>retryRules</span> 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, it automatically retries 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 Decoder

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>; the custom error decoder needs to implement the <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 Interceptor

If we need to perform unified interception processing for all HTTP requests in the system, we can customize and implement a global interceptor <span>BaseGlobalInterceptor</span> and configure it as a <span>Spring</span> container <span>bean</span>! 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 Interceptor

Simply implement the <span>NetworkInterceptor</span> interface and configure it as a <span>Spring</span> container <span>bean</span> 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 availability of services. 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.

<span>retrofit-spring-boot-starter</span> supports circuit breaker degradation functionality, based on Sentinel. Specifically, it supports automatic discovery of circuit breaker resources 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 the corresponding configuration items need to be set to enable it:

retrofit:
  # Enable circuit breaker degradation
  enable-degrade: true
  # Circuit breaker 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, with the default configuration being <span>DefaultResourceNameParser</span>, corresponding to the resource name format of <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)

<span>retrofit-spring-boot-starter</span> supports annotation-based configuration of degradation rules through the <span>@Degrade</span> annotation. The <span>@Degrade</span> annotation can be configured on interfaces or methods, with higher priority given to configurations 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 the <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 a 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>Beans</span>.

<span>fallbackFactory</span> differs from <span>fallback</span> 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 <span>ServiceInstanceChooser</span> as a <span>Spring</span> container <span>Bean</span>

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> <span>Bean</span>.

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

Use <span>@Retrofit</span>’s <span>serviceId</span> and <span>path</span> properties to implement HTTP calls between microservices

@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 type of interface methods through call adapters <span>CallAdapterFactory</span>. <span>retrofit-spring-boot-starter</span> extends two implementations of <span>CallAdapterFactory</span>:

  1. BodyCallAdapterFactory
    
  • Enabled by default, can be disabled by configuring <span>retrofit.enable-body-call-adapter=false</span>.
  • Synchronously executes HTTP requests, adapting the response body content to the return value type instance of the interface method.
  • Besides <span>Retrofit.Call<T></span>, <span>Retrofit.Response<T></span>, and <span>java.util.concurrent.CompletableFuture<T></span>, other return types can use this adapter.
  • ResponseCallAdapterFactory
    
    • Enabled by default, can be disabled by configuring <span>retrofit.enable-response-call-adapter=false</span>.
    • Synchronously executes HTTP requests, adapting the response body content to return a <span>Retrofit.Response<T></span>.
    • If the method’s return value type is <span>Retrofit.Response<T></span>, this adapter can be used.

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

    • <span>Call<T></span>: Does not perform adaptation, directly returns the <code><span>Call<T></span><span> object</span>
    • <span>CompletableFuture<T></span>: Adapts the response body content to return a <code><span>CompletableFuture<T></span><span> object</span>
    • <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 <code><span>Response<T></span><span> object</span>
    • 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>
         * Does not perform adaptation, 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
         * Does not focus on the return type, can use Void. 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 our own <span>CallAdapter</span> by inheriting from <span>CallAdapter.Factory</span>!

    <span>retrofit-spring-boot-starter</span> supports configuring global call adapter factories through <span>retrofit.global-call-adapter-factories</span>, with factory instances prioritized to be obtained from the Spring container; if not found, they 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>, with the specified factory instance still prioritized to be obtained from the Spring container.

    Note: If the <span>CallAdapter.Factory</span> does not have a public no-argument constructor, please manually configure it as a <span>Spring</span> container <span>Bean</span> 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 types of <span>Converter</span> 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>, with converter factory instances prioritized to be obtained from the Spring container; if not found, they 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. For configuration, please refer to Customize the Jackson ObjectMapper!

    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>, with the specified converter factory instance still prioritized to be obtained from the Spring container.

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

    Conclusion

    <span>retrofit-spring-boot-starter</span> is a lightweight <span>HTTP</span> client framework suitable for <span>SpringBoot</span> projects, which has been stably running online for over a year and has been adopted by several external companies.

    Author: 夜尽天明_

    https://juejin.cn/post/6898485806587969544

    ·················END·················

    Resource Links
    
    Tsinghua University senior's self-study Linux notes, top-level!
    New version of Bird Brother's Linux private cookbook materials
    Alibaba's big shots summarized "Illustrated Java" has become popular, full version PDF open for download!
    
    Alibaba officially launched! SpringBoot + SpringCloud full-color guide
    The strongest full-stack project of SpringBoot + Vue in China, no rebuttal accepted!
    

    Leave a Comment