Okhttp is an open source Java version of the HTTP client tool from Square. In fact, Square has also opened source Retrofit, a further encapsulation based on OKHTTP, to support making HTTP requests via an interface. If you are still using RestTemplate or OKHTTP directly in your project, or based on HttpUtils they encapsulate, you can try Retrofit. Retrofit-spring-boot-starter enables quick integration of Retrofit into the Spring-Boot framework and supports some enhancements that greatly simplify the development of HTTP interface calls under spring-Boot projects. Let’s go straight to retrofit-spring-boot-starter and see how easy it is for a spring-boot project to send HTTP requests. Retrofit officially doesn’t offer a starter for quick integration with Spring-Boot. Retrofit-spring-boot-starter was packaged by the authors and has been used in production environments and is very stable. It’s not easy to build a wheel. Retrofit-spring-boot-starter introduces dependencies

You can configure @RetrofitScan for a class with @Configuration, or directly for a spring-boot boot class, as follows: @SpringBootApplication @RetrofitScan(“com.github.lianjiatech.retrofit.spring.boot.test”) public class RetrofitTestApplication { public static void main(String[] args) { SpringApplication.run(RetrofitTestApplication.class, args); }} Define the HTTP interface interface must use the @retrofitClient annotation tag! For HTTP notes refer to the official documentation: Retrofit official documentation. @RetrofitClient(baseUrl = “${test.baseUrl}”) public interface HttpApi { @GET(“person”) Result getPerson(@Query(“id”) Long id); } injection can be used by injecting interfaces into other services. @Service public class TestService {

@Autowired
private HttpApi httpApi;

public void test() {
    
}
Copy the code

} It is really easy to send an HTTP request through the interface by following the above steps. If you have used MyBatis in a Spring-boot project, you will be familiar with this approach. Let’s move on to the more advanced features of Retrofit-spring-boot-starter. Annotated interceptors Many times, we want to perform uniform interception processing logic for certain HTTP requests on an interface. Annotated interceptors can be used at this point. The steps used are mainly divided into two steps: inherit BasePathMatchInterceptor to write interceptor processor; The interface is annotated using @Intercept. The following uses the annotated interceptor as an example to concatenate a TIMESTAMP after a specified request URL. Inherit BasePathMatchInterceptor to write interceptor handlers

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

} interfaces are annotated using @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);
Copy the code

}

The @Intercept configuration above says: Intercepting HttpApi/API/path (excluding/API /test/savePerson) requests using TimeStampInterceptor. Extending annotated interceptors Sometimes we need to dynamically pass in arguments to intercepting annotations and then use this argument to execute the interception. In such cases, we can extend the implementation of custom intercepting annotations. Custom InterceptMark annotations must use the @interceptmark tag and include include(), exclude(), and handler() attributes. The steps used are mainly divided into 3 steps: custom interception annotations inherit BasePathMatchInterceptor write interception processor interface using custom interception annotations; For example, we need to dynamically add accessKeyId and accessKeySecret signature information in the request header to initiate HTTP requests normally. In this case, we can define a signature interceptor annotation @sign to achieve this. The following uses custom @Sign interception annotations as an example. @Signature @Retention(RetentionPolicy.runtime) @Target(elementType.type) @Documented @Interceptmark public @Interface Sign {/ * Key * supports placeholder configuration. * * @return */ String accessKeyId();

/** * key * supports placeholder configuration. * * @return */ String accessKeySecret(); / interceptor matching path * * * * * @ * / return a String [] the include () default {" / * * "}; /** @return */ String[] exclude() default {}; /** * The interceptor class that handles this annotation * gets the corresponding Bean from the Spring container first, or creates one using reflection if it doesn't! * * @return */ Class<? extends BasePathMatchInterceptor> handler() default SignInterceptor.class;Copy the code

} Extension custom intercepting annotations have the following two points to note: custom intercepting annotations must use the @interceptmark flag. Annotations must include include(), exclude(), and handler() attributes.

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

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

} The above accessKeyId and accessKeySecret fields are automatically injected based on the accessKeyId() and accessKeySecret() values of the @sign annotation, or the configuration property value if @sign specifies a placeholder string. In addition, the accessKeyId and accessKeySecret fields must provide setter methods. Use @sign on the interface

@RetrofitClient(baseUrl = “
t e s t . b a s e U r l ) @ S i g n ( a c c e s s K e y I d = {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);
Copy the code

}

This will automatically add the signature information to the request for the specified URL.

Connection pool management By default, all HTTP requests sent through Retrofit will use the default connection pool of max-idle-connections=5 keep-alive-second=300. Of course, it is also possible to configure multiple custom connection pools in a configuration file and specify their use via the poolName attribute of @RetrofitClient. PoolName =test1; poolName=test1; poolName=test1; poolName=test1 Pool: test1: max-idle-connections: 3 keep-alive-second: 100 test2: max-idle-connections: 5 keep-alive-second: 50

  1. The poolName attribute of @RetrofitClient specifies the connection pool to use.

@RetrofitClient(baseUrl = “${test.baseUrl}”, poolName=”test1″) public interface HttpApi {

@GET("person")
Result<Person> getPerson(@Query("id") Long id);
Copy the code

}

Log Printing In many cases, we want to log HTTP requests. Using the logLevel and logStrategy properties of @RetrofitClient, you can specify the log printing level and log printing policy for each interface. Retrofit – spring – the boot – starter to support the five kinds of print log level (ERROR, WARN, INFO, DEBUG, TRACE), the default INFO; Four log printing policies (NONE,BASIC,HEADERS,BODY) are supported. The default is BASIC. The meanings of the four log printing policies are as follows: NONE: No logs. BASIC: logs Request and Response lines. HEADERS: Logs request and response lines and their respective headers. Logs request and response lines and their respective headers and bodies (if present). Retrofit – spring – the boot – starter default to the execution DefaultLoggingInterceptor real log printing function, its underlying is HttpLoggingInterceptor okhttp native. Of course, you can also realize your log custom print interceptors, only needs to inherit BaseLoggingInterceptor specific can consult DefaultLoggingInterceptor (implementation), then in the configuration file for the related configuration. retrofit:

Log print interceptor

logging-interceptor: Com. Making. Lianjiatech. Retrofit. Spring. The boot. The interceptor. DefaultLoggingInterceptor Http exception information formatter When abnormal HTTP requests, The original exception information may not be friendly to read, so Retrofit-spring-boot-starter provides an Http exception formatter to beautify the output Http request parameters, The default using DefaultHttpExceptionMessageFormatter request data format. You also can be customized, you just need to inherit BaseHttpExceptionMessageFormatter, related configuration again can. retrofit:

Http exception message formatter

http-exception-message-formatter: Com. Making. Lianjiatech. Retrofit. Spring. The boot. The interceptor. Call adapter CallAdapter DefaultHttpExceptionMessageFormatter Retrofit can adapt a Call object to the return value type of an interface method by calling the adapter CallAdapterFactory. Retrofit-spring-boot-starter extends 2 CallAdapterFactory implementations: BodyCallAdapterFactory is enabled by default. You can disable synchronous execution of HTTP requests by configuring retrofit.enable-body-call-adapter=false to match the response body content to an instance of the return value type of the interface method. In addition to Retrofit. Call, Retrofit. The Response, java.util.concurrent.Com pletableFuture besides, all the other return type can use the adapter. ResponseCallAdapterFactory enabled by default, it can be configured retrofit. The enable response – call – adapter = false close the synchronous HTTP requests, Adapt the Response body content to retrofit.response return. This adapter can be used if the return value type of the method is retrofit.Response. Retrofit automatically selects the corresponding CallAdapterFactory to perform adaptation based on the method return value type! With Retrofit’s default CallAdapterFactory, multiple forms of method return value types are supported: Call: No adaptation is performed, return the Call object CompletableFuture directly: Adaptation of the response body content into the CompletableFuture object returns Void: Void can be used regardless of the return type. If the HTTP status code is not 2xx, throw an error! Response: if the HTTP status code is not 2xx, throw an error.

/** * Call * does not perform adaptation, * @param ID * @return */ @get (“person”) Call<Result> getPersonCall(@query (“id”) Long ID);

/** * CompletableFuture<T> * Matching the response body to the CompletableFuture<T> object returns * @param id * @return */ @get ("person") CompletableFuture<Result<Person>> getPersonCompletableFuture(@Query("id") Long id); /** * Void * Regardless of the return type, you can use Void. If the HTTP status code is not 2xx, throw an error! * @param id * @return */ @GET("person") Void getPersonVoid(@Query("id") Long id); Return * @param id * @return */ @get ("person") Response<Result< person >> getPersonResponse(@Query("id") Long id); /** * If the HTTP status code is not 2xx, throw an error. * @param id * @return */ @GET("person") Result<Person> getPerson(@Query("id") Long id);Copy the code

We can also implement our own CallAdapter by inheriting the callAdapter.factory extension; Then configure your custom CallAdapterFactory as a Spring bean! Customizable callAdapter. Factory has a higher priority! The data transcoder converts the @Body annotation object into a request Body and the response Body data into a Java object. You can choose from one of the following options: 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 Retrofit – spring – the boot – starter used by default is Jackson serialize transformation! If you need to use other serialization methods, you can simply introduce the corresponding dependency into your project and configure the corresponding ConverterFactory as a Spring bean. We can also implement our own Converter by inheriting the Converter.Factory extension; Then configure your custom Converter.Factory as a Spring bean! Custom configuration Converter.Factory has higher priority!

If we need to implement a unified interceptor for HTTP requests throughout the system, we can customize the BaseGlobalInterceptor and configure it as a Bean in Spring! For example, HTTP requests that we need to make across the entire system are made with 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); }}

Conclusion This concludes the introduction of the most elegant HTTP client tools under the Spring-boot project. For more details, refer to the official documentation: Retrofit and Retrofit-spring-boot-Starter. For an explanation of how this works, see implementing your own lightweight HTTP call tool based on Retrofit.