- springboot:整合retrofit实现本地接口调用远程服务
- 一、简介
- 二、springboot整合retrofit
- 1.导入依赖
- 2.编写远程测试接口
- 3.编写本地测试接口
- 3.1 retrofit的配置信息
- 3.2 本地和测试接口
- 3.3 测试
- 4.编写拦截器
- 5.自定义注解拦截器
- 三、连接池管理
- 四、日志打印
- 五、HTTP异常信息格式化器
okhttp是一款由square公司开源的java版本http客户端工具。square公司还开源了基于okhttp进一步封装的retrofit工具,用来支持通过接口的方式发起http请求。 retrofit-spring-boot-starter实现了Retrofit与SpringBoot框架快速整合,并且支持了部分功能增强,从而极大的简化spring-boot项目下http接口调用开发
二、springboot整合retrofit 1.导入依赖2.编写远程测试接口com.github.lianjiatech retrofit-spring-boot-starter 2.2.22
package com.hl.springbootmybatis.controller;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.json.JSONUtil;
import lombok.Data;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/test/retrofit/remote")
public class RemoteRetrofitController {
@GetMapping("/t1")
public List t1(@RequestParam("name") String name) {
return ListUtil.of(name);
}
@DeleteMapping("/t2/{name}")
public List t2(@PathVariable("name") String name) {
return ListUtil.of(name);
}
@PostMapping("/t3")
public Map t3(@RequestBody Map params) {
return params;
}
@PutMapping("/t4")
public Map t4(@RequestBody Map params) {
return params;
}
@PostMapping("/t5")
public Map t5(@RequestParam Map params) {
return params;
}
@PostMapping("/t6")
public String t6(RetrofitTestModel model) {
return JSONUtil.toJsonStr(model);
}
@PostMapping("/t7")
public String t7(String name) {
return name;
}
@PostMapping("/t8")
public String t8(@RequestHeader("name") String name) {
return name;
}
@PostMapping("/t9")
public String t9(@RequestHeader("name") String name, @RequestHeader("token") String token) {
return name + ";" + token;
}
@GetMapping("/t10/{name}")
public List t10(@PathVariable("name") String name) {
return ListUtil.of(name);
}
@Data
public static class RetrofitTestModel {
private String name;
}
}
3.编写本地测试接口
3.1 retrofit的配置信息
###############retrofit配置############### #连接池相关配置 retrofit.global-connect-timeout-ms= 5000 retrofit.pool.test.max-idle-connection=3 retrofit.pool.test.keep-alive-second=100 #日志打印拦截器配置,可以继承BaseLoggingInterceptor实现自己的日志记录方式 retrofit.logging-interceptor=com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultLoggingInterceptor #异常格式化处理,可以继承BaseHttpExceptionMessageFormatter,实现自己的异常格式化 retrofit。http-exception-message-formatter=com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultHttpExceptionMessageFormatter3.2 本地和测试接口
新建接口,添加@RetrofitClient注解,配置baseUrl和poolName,poolName对应上一步的配置
package com.yolo.springbootretrofit.config;
import com.github.lianjiatech.retrofit.spring.boot.annotation.RetrofitClient;
import retrofit2.http.*;
import java.util.List;
import java.util.Map;
@RetrofitClient(baseUrl = "http://localhost:9093/",poolName = "test")
public interface HttpClient {
@GET("test/retrofit/remote/t1")
List t1(@Query("name") String name);
@DELETE("test/retrofit/remote/t2/{name}")
String t2(@Path("name") String name);
@POST("test/retrofit/remote/t3")
Map t3(@Body Map params);
@PUT("test/retrofit/remote/t4")
Map t4(@Body Map params);
@POST("test/retrofit/remote/t5")
Map t5(@QueryMap Map params);
@FormUrlEncoded
@POST("test/retrofit/remote/t6")
Map t6(@FieldMap Map model);
@FormUrlEncoded
@POST("test/retrofit/remote/t7")
String t7(@Field("name") String name);
@POST("test/retrofit/remote/t8")
String t8(@Header("name") String name);
@POST("test/retrofit/remote/t9")
@Headers({"name:zhangsan", "token:lisi"})
String t9();
@GET()
List t10(@Url String url) ;
}
3.3 测试
package com.yolo.springbootretrofit.controller;
import com.yolo.springbootretrofit.config.HttpClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/test/retrofit")
public class RetrofitTestController {
@Resource
private HttpClient httpClient;
@GetMapping("/t1")
public List t1() {
return httpClient.t1("zhangsan");
}
@GetMapping("t2")
public String t2() {
return httpClient.t2("zhangsan");
}
@GetMapping("t3")
public Map t3() {
Map map = new HashMap<>();
map.put("name", "zhangsan");
return httpClient.t3(map);
}
@GetMapping("t4")
public Map t4() {
Map map = new HashMap<>();
map.put("name", "zhangsan");
return httpClient.t4(map);
}
@GetMapping("t5")
public Map t5() {
Map map = new HashMap<>();
map.put("name", "zhangsan");
return httpClient.t5(map);
}
@GetMapping("t6")
public Map t6() {
Map map = new HashMap<>();
map.put("name", "zhangsan");
return httpClient.t6(map);
}
@GetMapping("t7")
public String t7() {
return httpClient.t7("zhangsan");
}
@GetMapping("t8")
public String t8() {
return httpClient.t8("zhangsan");
}
@GetMapping("t9")
public String t9() {
return httpClient.t9();
}
@GetMapping("t10")
public List t10() {
String s = "http://localhost:9093/test/retrofit/remote/t10/lisi";
return httpClient.t10(s);
}
}
4.编写拦截器
可是通过编写拦截器来拦截retrofit的请求,在请求中添加一些统一的处理,拦截器需要继承BasePathMatchInterceptor并重写doIntercept方法
接口上使用@Intercept进行标注
package com.yolo.springbootretrofit.config;
import com.github.lianjiatech.retrofit.spring.boot.interceptor.BasePathMatchInterceptor;
import okhttp3.Headers;
import okhttp3.HttpUrl;
import okhttp3.Request;
import okhttp3.Response;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Component
public class SimpleInterceptor extends BasePathMatchInterceptor {
@Override
protected Response doIntercept(Chain chain) throws IOException {
Request request = chain.request();
HttpUrl url = request.url();
url = url.newBuilder().addQueryParameter("time", String.valueOf(System.currentTimeMillis()))
.build();
Headers headers = request.headers();
headers = headers.newBuilder().add("add-header", "lalala").build();
request = request.newBuilder().headers(headers)
.url(url).build();
return chain.proceed(request);
}
}
在接口类中添加@Interceptor注解,handler属性为上面定义的拦截器,include属性为拦截的URL,exclude为不拦截的URL
@Intercept(handler = SimpleInterceptor.class, include = {"/demo/test
String accessKeyId();
String accessKeySecret();
String[] include() default {"
String[] exclude() default {};
Class extends BasePathMatchInterceptor> handler() default SignInterceptor.class;
}
accessKeyId和accessKeySecret字段值会依据@Sign注解的accessKeyId()和accessKeySecret()值自动注入,如果@Sign指定的是占位符形式的字符串,则会取配置属性值进行注入。另外,accessKeyId和accessKeySecret字段必须提供setter方法
package com.yolo.springbootretrofit.config;
import com.github.lianjiatech.retrofit.spring.boot.interceptor.BasePathMatchInterceptor;
import okhttp3.Request;
import okhttp3.Response;
import org.springframework.stereotype.Component;
import java.io.IOException;
@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);
}
}
接口上使用@Sign
@RetrofitClient(baseUrl = "${test.baseUrl}")
@Sign(accessKeyId = "${test.accessKeyId}", accessKeySecret = "${test.accessKeySecret}", exclude = {"/api/test/person"})
public interface HttpApi {
@GET("person")
Result getPerson(@Query("id") Long id);
@POST("savePerson")
Result savePerson(@Body Person person);
}
三、连接池管理
默认情况下,所有通过Retrofit发送的http请求都会使用max-idle-connections=5 keep-alive-second=300的默认连接池。当然,我们也可以在配置文件中配置多个自定义的连接池,然后通过@RetrofitClient的poolName属性来指定使用。比如我们要让某个接口下的请求全部使用poolName=test1的连接池
1.配置连接池
retrofit:
# 连接池配置
pool:
test1:
max-idle-connections: 3
keep-alive-second: 100
test2:
max-idle-connections: 5
keep-alive-second: 50
2.通过@RetrofitClient的poolName属性来指定使用的连接池
@RetrofitClient(baseUrl = "${test.baseUrl}", poolName="test1")
public interface HttpApi {
@GET("person")
Result getPerson(@Query("id") Long id);
}
四、日志打印
很多情况下,我们希望将http请求日志记录下来。通过@RetrofitClient的logLevel和logStrategy属性,您可以指定每个接口的日志打印级别以及日志打印策略。retrofit-spring-boot-starter支持了5种日志打印级别(ERROR, WARN, INFO, DEBUG, TRACE),默认INFO;支持了4种日志打印策略(NONE, BASIC, HEADERS, BODY),默认BASIC。4种日志打印策略含义如下:
- 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默认使用了DefaultLoggingInterceptor执行真正的日志打印功能,其底层就是okhttp原生的HttpLoggingInterceptor。当然,你也可以自定义实现自己的日志打印拦截器,只需要继承BaseLoggingInterceptor(具体可以参考DefaultLoggingInterceptor的实现),然后在配置文件中进行相关配置即可。
retrofit: # 日志打印拦截器 logging-interceptor: com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultLoggingInterceptor五、HTTP异常信息格式化器
当出现http请求异常时,原始的异常信息可能阅读起来并不友好,因此retrofit-spring-boot-starter提供了HTTP异常信息格式化器,用来美化输出http请求参数,默认使用DefaultHttpExceptionMessageFormatter进行请求数据格式化。你也可以进行自定义,只需要继承BaseHttpExceptionMessageFormatter,再进行相关配置即可
retrofit: # Http异常信息格式化器 http-exception-message-formatter: com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultHttpExceptionMessageFormatter



