The elegant http client tool of the spring-boot project, really fragrant!

Everyone knows that okhttp is a java version of http client tool open sourced by square company. In fact, square has also open sourced the retrofit tool based on okhttp to further encapsulate it to support the initiation of http requests through the interface. If your project is still using RestTemplate or okhttp directly, or HttpUtils based on them, then you can try Retrofit.

retrofit-spring-boot-starter realizes the rapid integration of Retrofit and spring-boot framework, and supports some functional enhancements, which greatly simplifies the development of http interface calls under the spring-boot project. Next, we directly use retrofit-spring-boot-starter to see how easy it is to send http requests to the spring-boot project.

Retrofit officially does not provide a starter for quick integration with spring-boot. Retrofit-spring-boot-starter is packaged by the author and has been used in the production environment and is very stable.

Project source code: retrofit-spring-boot-starter
introduces dependencies

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

Configure @RetrofitScan annotation

You can configure @RetrofitScan for the class with @Configuration, or directly configure it to the spring-boot startup 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 http interface

The interface must be marked with @RetrofitClient annotation! http related solutions can refer to the official document: retrofit official document.

@RetrofitClient(baseUrl = "${test.baseUrl}")
public interface HttpApi {
    
    

    @GET("person")
    Result<Person> getPerson(@Query("id") Long id);
}

Injection use

It can be used by injecting the interface into other services.

@Service
public class TestService {
    
    

    @Autowired
    private HttpApi httpApi;

    public void test() {
    
    
        // 通过httpApi发起http请求
    }
}

As long as through the above steps, you can send http requests through the interface, which is really simple. If you have used mybatis in the spring-boot project, I believe you will be more familiar with this way of using it. Next, we continue to introduce the more advanced functions of retrofit-spring-boot-starter.

Annotated Interceptor

In many cases, we hope that certain http requests under a certain interface will perform a unified interception processing logic. Annotated interceptors can be used at this time. The steps used are mainly divided into 2 steps:

Inherit the BasePathMatchInterceptor to write the interception processor;
use @Intercept to mark the interface.
The following is an example of splicing timestamp after the URL of the specified request to introduce how to use the annotation interceptor.

Inherit BasePathMatchInterceptor to write an interception processor

@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 @Intercept to mark 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 @Intercept configuration means: intercept requests under the /api/** path (exclude /api/test/savePerson) under the HttpApi interface, and the interception processor uses TimeStampInterceptor.

Extended annotation interceptor

Sometimes, we need to dynamically pass in some parameters in the interception annotation, and then need to use this parameter when performing interception. At this time, we can extend the implementation of custom intercept annotations. Custom intercept annotation must use @InterceptMark mark, and the annotation must include include(), exclude(), handler() attribute information. The steps used are mainly divided into 3 steps:

Custom interception annotations
inherit BasePathMatchInterceptor to write interception processor
interfaces and use custom intercept annotations;
for example, we need to dynamically add accessKeyId and accessKeySecret signature information in the request header to initiate http requests normally. At this time, we can customize a signature interceptor annotation@ Sign to achieve. The following is an example of custom @Sign interception annotations.

Custom @Sign annotation

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@InterceptMark
public @interface Sign {
    
    
    /**
     * 密钥key
     * 支持占位符形式配置。
     *
     * @return
     */
    String accessKeyId();

    /**
     * 密钥
     * 支持占位符形式配置。
     *
     * @return
     */
    String accessKeySecret();

    /**
     * 拦截器匹配路径
     *
     * @return
     */
    String[] include() default {
    
    "/**"};

    /**
     * 拦截器排除匹配,排除指定路径拦截
     *
     * @return
     */
    String[] exclude() default {
    
    };

    /**
     * 处理该注解的拦截器类
     * 优先从spring容器获取对应的Bean,如果获取不到,则使用反射创建一个!
     *
     * @return
     */
    Class<? extends BasePathMatchInterceptor> handler() default SignInterceptor.class;
}

The following 2 points need to be paid attention to when expanding custom interception annotations:

Custom intercept annotations must be marked with @InterceptMark.
The annotation must include include(), exclude(), handler() attribute information.
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);
    }
}

The above accessKeyId and accessKeySecret field values ​​will be automatically injected according to the accessKeyId() and accessKeySecret() values ​​annotated by @Sign. If @Sign specifies a string in the form of a placeholder, the configuration attribute value will be used for injection. 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);
}

In this way, the signature information can be automatically added to the request of 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, we can also configure multiple custom connection pools in the configuration file, and then specify the use through the poolName property of @RetrofitClient. For example, if we want to make all requests under a certain interface use the connection pool with poolName=test1, the code implementation is as follows:

1. Configure the connection pool.

retrofit:
   # 连接池配置
   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 be used through the poolName property of @RetrofitClient.

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

   @GET("person")
   Result<Person> getPerson(@Query("id") Long id);
}

Log print

In many cases, we want to record the http request log. Through the logLevel and logStrategy attributes of @RetrofitClient, you can specify the log printing level and log printing strategy of each interface. retrofit-spring-boot-starter supports 5 log printing levels (ERROR, WARN, INFO, DEBUG, TRACE), default INFO; supports 4 log printing strategies (NONE, BASIC, HEADERS, BODY), default BASIC. The meanings of the 4 log printing strategies are as follows:

NONE: No logs.
BASIC: Logs request and response lines.
HEADERS: Logs request and response lines and their respective headers.
BODY: Logs request and response lines and their respective headers and bodies (if present).
retrofit-spring-boot-starter By default, DefaultLoggingInterceptor is used to perform the real log printing function, and the bottom layer is okhttp's native HttpLoggingInterceptor. Of course, you can also customize your own log printing interceptor, you only need to inherit BaseLoggingInterceptor (for details, please refer to the implementation of DefaultLoggingInterceptor), and then configure it in the configuration file.

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

Http exception information formatter

When an HTTP request exception occurs, the original exception information may not be friendly to read. Therefore, retrofit-spring-boot-starter provides an Http exception information formatter to beautify the output of http request parameters. The DefaultHttpExceptionMessageFormatter is used by default for the request data format化. You can also customize it, just inherit BaseHttpExceptionMessageFormatter, and then configure it.

retrofit:
  # Http异常信息格式化器
  http-exception-message-formatter: com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultHttpExceptionMessageFormatter

Call adapter CallAdapter

Retrofit can adapt the Call object to the return value type of the interface method by calling the adapter CallAdapterFactory. Retrofit-spring-boot-starter extends two CallAdapterFactory implementations:

BodyCallAdapterFactory

It is enabled by default and can be disabled by configuring retrofit.enable-body-call-adapter=false to
execute the http request synchronously, and adapt the response body content to an instance of the return value type of the interface method.
Except Retrofit.Call, Retrofit.Response, java.util.concurrent.CompletableFuture, other return types can use this adapter.
ResponseCallAdapterFactory

It is enabled by default, and can be disabled by configuring retrofit.enable-response-call-adapter=false to
execute the http request synchronously, and adapt the response body content to Retrofit.Response to return.
If the return value type of the method is Retrofit.Response, you can use this adapter.
Retrofit automatically selects the corresponding CallAdapterFactory to perform adaptation processing according to the method return value type! In addition to Retrofit's default CallAdapterFactory, it can support various types of method return values:

Call: No adaptation processing is performed, and the Call object is returned directly.
CompletableFuture: The response body content is adapted to a CompletableFuture object. Return
Void: Void can be used regardless of the return type. If the http status code is not 2xx, just throw an error!
Response: Adapt the response content to the Response object and return
any other Java type: adapt the response body content to a corresponding Java type object and return. If the http status code is not 2xx, just throw an error!

 /**
     * Call<T>
     * 不执行适配处理,直接返回Call<T>对象
     * @param id
     * @return
     */
    @GET("person")
    Call<Result<Person>> getPersonCall(@Query("id") Long id);

    /**
     *  CompletableFuture<T>
     *  将响应体内容适配成CompletableFuture<T>对象返回
     * @param id
     * @return
     */
    @GET("person")
    CompletableFuture<Result<Person>> getPersonCompletableFuture(@Query("id") Long id);

    /**
     * Void
     * 不关注返回类型可以使用Void。如果http状态码不是2xx,直接抛错!
     * @param id
     * @return
     */
    @GET("person")
    Void getPersonVoid(@Query("id") Long id);

    /**
     *  Response<T>
     *  将响应内容适配成Response<T>对象返回
     * @param id
     * @return
     */
    @GET("person")
    Response<Result<Person>> getPersonResponse(@Query("id") Long id);

    /**
     * 其他任意Java类型
     * 将响应体内容适配成一个对应的Java类型对象返回,如果http状态码不是2xx,直接抛错!
     * @param id
     * @return
     */
    @GET("person")
    Result<Person> getPerson(@Query("id") Long id);

We can also implement our own CallAdapter by extending CallAdapter.Factory; then configure the custom CallAdapterFactory as a spring bean!

CallAdapter.Factory with custom configuration has higher priority!

Data transcoder Converter

Retrofi uses Converter to convert the object annotated with @Body into the request body and the response body data into a Java object. The following converters can be used:

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 for serialization conversion by default! If you need to use other serialization methods, introduce the corresponding dependency in the project, and then configure the corresponding ConverterFactory as a spring bean.

We can also implement our own Converter by extending Converter.Factory; then configure the custom Converter.Factory as a spring bean!

The custom-configured Converter.Factory has a higher priority!

BaseGlobalInterceptor

If we need to perform unified interception processing on the http request of the entire system, we can customize the implementation of the global interceptor BaseGlobalInterceptor and configure it as a bean in spring! For example, we need to bring source information for all http requests initiated in the entire system.

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

Guess you like

Origin blog.csdn.net/ncw8080/article/details/114007662