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.
Retrofit-spring-boot-starter
Introduction of depend on
<dependency> <groupId>com.github.lianjiatech</groupId> <artifactId>retrofit-spring-boot-starter</artifactId> The < version > 2.0.2 < / version > < / dependency >Copy the code
Configure the @retrofitSCAN annotation
You can configure @RetrofitScan for @configuration classes, or directly for spring-boot boot classes, 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); }}
Copy the code
Defining an HTTP interface
Interfaces must be marked with @RetrofitClient annotations! For HTTP notes refer to the official documentation: Retrofit official documentation.
@RetrofitClient(baseUrl = "${test.baseUrl}")public interface HttpApi { @GET("person") Result<Person> getPerson(@Query("id") Long id); }Copy the code
Injection use
Inject the interface into another Service.
@Servicepublic class TestService { @Autowired private HttpApi httpApi; Public void test() {// Make an HTTP request via httpApi}}Copy the code
With just a few steps above, it’s really easy to send HTTP requests through the interface. 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 some HTTP requests under an interface. Annotated interceptors can be used at this point. The steps are mainly divided into two steps:
-
Inherit ‘base athmatchinterceptor’ to write interceptor processor;
-
The interface is annotated with ‘@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
@Componentpublic 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
The interface is 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 says: Intercept requests from HttpApi/API /** paths (excluding/API /test/savePerson) using TimeStampInterceptor.
Extend annotated interceptors
Sometimes, we need to pass in arguments dynamically in intercepting annotations, and then we need to use this argument in intercepting annotations. 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 are mainly divided into three steps:
-
Custom interception annotations
-
Inherit ‘BasePathMatchInterceptor’ to write interceptor processor
-
Using custom intercepting annotations on interfaces
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.
Custom @sign annotations
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documented@InterceptMarkpublic @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
There are two things to note about extending custom interceptor annotations:
-
The ‘custom intercepting annotation’ must use the ‘@interceptmark’ flag.
-
Annotations must include ‘include(), exclude(), and handler()’ attributes.
Implement SignInterceptor
@Componentpublic 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 = "${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 over 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;
-
Configure the connection pool.
Pool: test1: max-idle-connections: 3 keep-alive-second: 100 test2: max-idle-connections: 5 keep-alive-second: 50
-
The ‘poolName’ attribute of ‘@RetrofitClient’ specifies the connection pool to use.
@RetrofitClient(baseUrl = “${test.baseUrl}”, poolName=”test1″)public interface HttpApi { @GET(“person”) Result getPerson(@Query(“id”) Long id); }
Log print
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-boot-starter supports five log printing levels (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 HEADERS.
-
‘BODY’ : 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: # logging-interceptor: com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultLoggingInterceptorCopy the code
Http exception message formatter
When HTTP request exceptions occur, 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: com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultHttpExceptionMessageFormatterCopy the code
Call the adapter CallAdapter
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
-
This is enabled by default and can be turned off by configuring ‘retrofit.enable-body-call-adapter=false’
-
Synchronously executes the HTTP request, matching 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 `, other return type can use the adapter.
-
ResponseCallAdapterFactory
-
This is enabled by default and can be turned off by configuring ‘retrofit.enable-response-call-adapter=false’
-
Synchronously executes the HTTP request, returning the Response body content as retrofit.response.
-
This adapter can be used if the method returns a value of type ‘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’ : returns the ‘Call’ object without adaptation
-
‘CompletableFuture’ : Returns the response body content adapted into the ‘CompletableFuture’ object
-
‘Void’ : You can use ‘Void’ regardless of the return type. If the HTTP status code is not 2xx, throw an error!
-
‘Response’ : returns the Response content as a ‘Response’ object
-
Any other Java type: return the response body to a corresponding Java type object. If the HTTP status code is not 2xx, throw an error!
/** * Call<T> Call<T> object * @param ID * @return */ @get ("person") Call<Result< person >> 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!
Data Transcoder Converter
Retrofi uses Converter to convert the @Body annotation object into the request Body and the response Body data into a Java object. There are several options for Retrofi:
-
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
Retrofit-spring-boot-starter uses Jackson by default for serialization transformations! 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!
BaseGlobalInterceptor is the global interceptor
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.
@Componentpublic 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); }}
Copy the code