SpringBoot优雅的http客户端工具(Retrofit)

平时开发项目,避免不了其他服务接口调用,一般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支持四种日志打印策略
  1. NONE:不打印日志;
  2. BASIC:只打印日志请求记录;
  3. HEADERS:打印日志请求记录、请求和响应头信息;
  4. BODY:打印日志请求记录、请求和响应头信息、请求和响应体信息
  • 全局超时时间
 global-connect-timeout-ms: 5000
  # 全局读取超时时间
  global-read-timeout-ms: 5000
  # 全局写入超时时间
  global-write-timeout-ms: 5000
  # 全局完整调用超时时间
  global-call-timeout-ms: 0
  • 重试规则global-retry-rules支持如下三种配置。
    1. RESPONSE_STATUS_NOT_2XX:响应状态码不是2xx时执行重试;
    2. OCCUR_IO_EXCEPTION:发生IO异常时执行重试;
    3. 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提供的功能远不止于此,它还能支持微服务间的调用和熔断降级!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,546评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,224评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,911评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,737评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,753评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,598评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,338评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,249评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,696评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,888评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,013评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,731评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,348评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,929评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,048评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,203评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,960评论 2 355

推荐阅读更多精彩内容