平时开发项目,避免不了其他服务接口调用,一般http客户端使用都是hutool的httputil、okhttp,但是还是觉得麻烦!发现“Retrofit”客户端工具非常好用,推荐给大家。
简介
Retrofit能够支持接口的方式发起http请求,类似于Feign申明式接口调用。
11.png
目前,java常用的开发框架是springboot,遗憾的是Retrofit官方并不支持。但是,有位开发者做了增强。
12.png
Springboot依赖使用
<dependency>
<groupId>com.github.lianjiatech</groupId>
<artifactId>retrofit-spring-boot-starter</artifactId>
<version>2.2.18</version>
</dependency>
使用
- application.yml 添加其他服务接口地址,可以自己模拟服务接口
thirdUrl:
zh: https://test:20441/api/
eye: http://test:2045/api/
eyeSkyToken: xxxxx
retrofit:
# 连接池配置
pool:
# test1连接池配置
test1:
# 最大空闲连接数
max-idle-connections: 3
# 连接保活时间(秒)
keep-alive-second: 100
# 是否禁用void返回值类型
disable-void-return-type: false
# 全局转换器工厂
global-converter-factories:
- com.github.lianjiatech.retrofit.spring.boot.core.BasicTypeConverterFactory
- retrofit2.converter.jackson.JacksonConverterFactory
# 全局调用适配器工厂
global-call-adapter-factories:
- com.github.lianjiatech.retrofit.spring.boot.core.BodyCallAdapterFactory
- com.github.lianjiatech.retrofit.spring.boot.core.ResponseCallAdapterFactory
# 日志打印配置
log:
# 启用日志打印
enable: true
# 日志打印拦截器
logging-interceptor: com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultLoggingInterceptor
# 全局日志打印级别
global-log-level: error
# 全局日志打印策略
global-log-strategy: basic
# 重试配置
retry:
# 是否启用全局重试
enable-global-retry: true
# 全局重试间隔时间
global-interval-ms: 1
# 全局最大重试次数
global-max-retries: 1
# 全局重试规则
global-retry-rules:
- response_status_not_2xx
- occur_io_exception
# 重试拦截器
retry-interceptor: com.github.lianjiatech.retrofit.spring.boot.retry.DefaultRetryInterceptor
# 熔断降级配置
degrade:
# 是否启用熔断降级
enable: true
# 熔断降级实现方式
degrade-type: sentinel
# 熔断资源名称解析器
resource-name-parser: com.github.lianjiatech.retrofit.spring.boot.degrade.DefaultResourceNameParser
# 全局连接超时时间,还有局部的,在@RetrofitClient注解上可以设置超时时间,针对当前接口生效,优先级更高。具体字段有connectTimeoutMs、readTimeoutMs、writeTimeoutMs、callTimeoutMs等
global-connect-timeout-ms: 5000
# 全局读取超时时间
global-read-timeout-ms: 5000
# 全局写入超时时间
global-write-timeout-ms: 5000
# 全局完整调用超时时间
global-call-timeout-ms: 0
- 创建请求接口类
/**
*
* 请求格式注解,请求实体是一个From表单
* @Field 必须结合@FormUrlEncoded注解一起使用
*
*/
@RetrofitClient(baseUrl = "${thirdUrl.zh}")
# 拦截器
@Intercept(handler = TimeStampInterceptor.class,include = "/api/**")
public interface HttpZhApi {
@FormUrlEncoded
@POST("Token")
String Token(@Field("UserName")String userName,@Field("APIPwd")String apiPwd);
}
注意:@Field 必须结合@FormUrlEncoded注解一起使用,否则post请求无法接收参数
- 请求接口同时,默认添加
timestamp
参数
/**
* 注解式拦截器
* 们希望某个接口下的某些http请求执行统一的拦截处理逻辑。为了支持这个功能
*/
@Component
public class TimeStampInterceptor extends BasePathMatchInterceptor {
@Override
protected 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);
}
}
- controller测试接口
@RestController
@RequestMapping("zhDemo")
public class ZhController {
@Autowired
HttpZhApi httpZhApi;
@RequestMapping("token")
public String getToken(String userName,String apiPwd){
return httpZhApi.Token(userName,apiPwd);
}
13.png
- 调整日志级别、打印策略
log:
# 启用日志打印
enable: true
# 日志打印拦截器
logging-interceptor: com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultLoggingInterceptor
# 全局日志打印级别
global-log-level: info
# 全局日志打印策略
global-log-strategy: body
修改日志策略global-log-strategy:body
响应结果
14.png
日志配置说明
- Retrofit支持四种日志打印策略
- NONE:不打印日志;
- BASIC:只打印日志请求记录;
- HEADERS:打印日志请求记录、请求和响应头信息;
- BODY:打印日志请求记录、请求和响应头信息、请求和响应体信息
- 全局超时时间
global-connect-timeout-ms: 5000
# 全局读取超时时间
global-read-timeout-ms: 5000
# 全局写入超时时间
global-write-timeout-ms: 5000
# 全局完整调用超时时间
global-call-timeout-ms: 0
- 重试规则
global-retry-rules
支持如下三种配置。- RESPONSE_STATUS_NOT_2XX:响应状态码不是2xx时执行重试;
- OCCUR_IO_EXCEPTION:发生IO异常时执行重试;
- OCCUR_EXCEPTION:发生任意异常时执行重试。
请求添加头token
- 定义token注解
@Target(ElementType.TYPE)
@Documented
@InterceptMark
public @interface EyeSkyToken {
/**
* 密钥key
* 支持占位符形式配置。
*
* @return
*/
String authorization();
/**
* 拦截器匹配路径
*
* @return
*/
String[] include() default {"/**"};
/**
* 拦截器排除匹配,排除指定路径拦截
*
* @return
*/
String[] exclude() default {};
/**
* 处理该注解的拦截器类
* 优先从spring容器获取对应的Bean,如果获取不到,则使用反射创建一个!
*
* @return
*/
Class<? extends BasePathMatchInterceptor> handler() default EyeSkyTokenInterceptor.class;
}
- 自定义拦截器,所有请求添加授权header
@Setter
public class EyeSkyTokenInterceptor extends BasePathMatchInterceptor {
private String authorization;
@Override
protected Response doIntercept(Chain chain) throws IOException {
Request request = chain.request();
Request newReq = request.newBuilder()
.addHeader("Authorization", authorization)
.build();
return chain.proceed(newReq);
}
}
- 申明式请求接口
@RetrofitClient(baseUrl = "${thirdUrl.eye}",poolName = "test1")
@EyeSkyToken(authorization = "${eyeSkyToken}")
public interface HttpEyeSkyApi {
@GET("baseinfoV2/2.0")
String getCompanyInfo(@QueryMap Map<String, Object> map);
}
Retrofit 请求参数支持Map,详细查看官网
- controller 测试接口调用
@Autowired
HttpEyeSkyApi eyeSkyApi;
@RequestMapping("/companyInfo")
public String getCompanyInfo(String companyName) throws UnsupportedEncodingException {
map.put("keyword",companyName);
return eyeSkyApi.getCompanyInfo(map);
}
对比使用HttpUtil,通过Retrofit在单体应用项目中,可以使用类似Feign申明式接口这种方式。
retrofit-spring-boot-starter
提供的功能远不止于此,它还能支持微服务间的调用和熔断降级!