⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 http://www.iocoder.cn/Fight/Throw-away-okhttp-httpClient-try-this-lightweight-HTTP-client-framework-blowing-up/ 「芋道源码」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

它解决了 Spring Boot 写 HTTP 调用的什么问题

OkHttp、HttpClient、RestTemplate 在 Spring Boot 里裸写都有一个共同毛病:模板代码到处复制、配置散落、没有统一的拦截/重试/降级管理。每个 HTTP 调用都得自己拼 URL、装请求头、解响应、捕异常,工程到后期就是一锅杂烩。

retrofit-spring-boot-starter 的解法很直接——把 HTTP 调用变成 Java 接口声明:你定义一个接口、加几个注解,框架帮你完成请求构造、序列化、拦截、重试、熔断的全套流程。

它叫 retrofit-spring-boot-starter,由链家技术团队开源。

  1. Spring Boot 4.x 项目,使用 retrofit-spring-boot-starter 4.x
  2. Spring Boot 3.x 项目,使用 retrofit-spring-boot-starter 3.x
  3. Spring Boot 1.x/2.x 项目,使用 retrofit-spring-boot-starter 2.x

仓库地址:

放到生态里看:和 OkHttp / Feign / WebClient 比,它的位置在哪

说白了:

  • 同样是声明式:服务间调用用 OpenFeign(绑 Spring Cloud 体系);外部 HTTP 调用用 retrofit-spring-boot-starter(更轻、更通用)
  • 不是声明式党:裸用 OkHttp 也行——但要团队自己沉淀拦截器/重试/降级模板,工程成本不低

快速上手:三步从依赖到调用

第一步:引入依赖

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

第二步:定义接口

接口必须用 @RetrofitClient 注解标记,方法用 Retrofit 原生的 @GET、@POST 等修饰:

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

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

路径慎用 / 开头:如果 baseUrl=http://localhost:8080/api/test/,方法路径写 person 拼出来是 http://localhost:8080/api/test/person;写 /person 拼出来是 http://localhost:8080/person——两种语义完全不同。

第三步:注入使用

像普通 Spring Bean 一样注入即可:

@Service
public class TestService {

@Autowired
private HttpApi httpApi;

public void test() {
// 直接调用 httpApi
}
}

默认按 Spring Boot 主类的扫描路径自动注册,需要自定义路径用 @RetrofitScan。

HTTP 注解全家桶(直接复用 Retrofit)

不用学新东西——retrofit-spring-boot-starter 直接复用了 Retrofit 的所有原生注解:

分类 支持的注解
请求方式 @GET @HEAD @POST @PUT @DELETE @OPTIONS @HTTP
请求头 @Header @HeaderMap @Headers
Query 参数 @Query @QueryMap @QueryName
Path 参数 @Path
form-encoded 参数 @Field @FieldMap @FormUrlEncoded
请求体 @Body
文件上传 @Multipart @Part @PartMap
URL 参数 @Url

配置全景:一份能直接抄的 yaml

应用只需要写要修改的项,其它走默认值即可——下面是完整可配置项清单,建议照着这份对照修改:

retrofit:
# 全局转换器工厂(@Body / Response Body 的序列化引擎)
global-converter-factories:
- com.github.lianjiatech.retrofit.spring.boot.core.BasicTypeConverterFactory
- retrofit2.converter.jackson.JacksonConverterFactory
# 全局调用适配器工厂(组件已内置,请勿重复配置)
global-call-adapter-factories:

# 全局日志打印配置
global-log:
enable: true # 启用日志打印
log-level: info # 全局日志打印级别
log-strategy: basic # NONE / BASIC / HEADERS / BODY

# 全局重试配置
global-retry:
enable: false # 是否启用全局重试
interval-ms: 100 # 重试间隔(毫秒)
max-retries: 2 # 最大重试次数
retry-rules: # 重试规则
- response_status_not_2xx # 响应非 2xx 重试
- occur_io_exception # IO 异常重试

# 全局超时时间配置
global-timeout:
read-timeout-ms: 10000
write-timeout-ms: 10000
connect-timeout-ms: 10000
call-timeout-ms: 0 # 完整调用超时,0=不限

# 熔断降级配置(默认 none,按需开启 sentinel / resilience4j)
degrade:
degrade-type: none # none / sentinel / resilience4j

# 全局 Sentinel 降级(degrade-type=sentinel 时生效)
global-sentinel-degrade:
enable: false
count: 1000 # 阈值:响应时间(ms) / 异常比例(0-1) / 异常数量(1-N)
time-window: 5 # 熔断时长(秒)
grade: 0 # 降级策略:0=平均响应时间 / 1=异常比例 / 2=异常数量

# 全局 Resilience4j 降级(degrade-type=resilience4j 时生效)
global-resilience4j-degrade:
enable: false
circuit-breaker-config-name: defaultCircuitBreakerConfig

# 自动把 PathMatchInterceptor 设为 prototype scope(基本不用动)
auto-set-prototype-scope-for-path-math-interceptor: true

实战建议:

  • global-log.log-strategy 生产用 BASIC——BODY 调试时开,生产留着会把请求体打满日志文件
  • global-retry.enable 默认 false——开之前先确认接口幂等
  • degrade-type 选哪个:阿里系 / 国内业务用 sentinel(自带 dashboard);已经在用 Spring Cloud Resilience 全家桶的用 resilience4j

5 个真的会用上的高频特性

starter 提供的特性多到每个都讲就成了文档堆。真正高频用到的就这 5 个——其它(自定义 Client、注解式拦截、错误解码器、调用适配器、元注解、动态 URL 等)按需查官方文档即可。

特性 1:日志打印——从全局到声明式

全局日志默认开启,四档可选:

级别 输出内容
NONE 不打日志
BASIC 请求/响应行
HEADERS + 请求头/响应头
BODY + 请求体/响应体(调试神器,生产慎用)

只想对部分接口开 BODY 级日志?方法/接口上加 @Logging:

@Logging(logStrategy = LogStrategy.BODY)
@POST("savePerson")
Result<Person> savePerson(@Body Person person);

需要”同一请求的多行日志聚合到一行输出”——配 AggregateLoggingInterceptor:

@Bean
public LoggingInterceptor loggingInterceptor(RetrofitProperties retrofitProperties) {
return new AggregateLoggingInterceptor(retrofitProperties.getGlobalLog());
}

特性 2:请求重试——三种规则按场景挑

全局重试默认关闭,三种规则可选:

规则 触发条件
RESPONSE_STATUS_NOT_2XX 响应非 2xx 重试
OCCUR_IO_EXCEPTION IO 异常重试(网络抖动场景必开)
OCCUR_EXCEPTION 任意异常重试(慎用,业务异常也会重试)

只对部分接口重试,加 @Retry:

@Retry(intervalMs = 200, maxRetries = 3, retryRules = {RetryRule.OCCUR_IO_EXCEPTION})
@GET("person")
Result<Person> getPerson(@Query("id") Long id);

重试踩坑:写接口接收方一定要做幂等——重试 = 同一个请求被发多次,没幂等就等着出账户重复扣款这种事故。

特性 3:熔断降级——Sentinel / Resilience4j 任选

外部 HTTP 服务不稳是常态,没熔断的应用就是定时炸弹。这个 starter 把 Sentinel / Resilience4j 都接好了,配好 degrade-type 就能用:

retrofit:
degrade:
degrade-type: sentinel # 或 resilience4j

接口/方法上加 @SentinelDegrade 或 @Resilience4jDegrade 即可。两种实现选哪个?

维度 Sentinel Resilience4j
阿里系生态 ✅ 阿里出品 国外项目
控制台 自带 dashboard 需自己接 Micrometer
配置粒度 简单(响应时间 / 异常比例 / 异常数) 更细(滑动窗口可定制)
推荐场景 国内业务、想要 dashboard 看一眼的 已经在用 Spring Cloud Resilience 全家桶的

特性 4:数据转换器——默认 Jackson 够用

Retrofit 用 Converter 把 @Body 对象 ↔ JSON。这个 starter 默认用 JacksonConverterFactory——99% 的项目不用动。

如果团队约定用 fastjson 或 gson,加全局配置:

retrofit:
global-converter-factories:
- com.github.lianjiatech.retrofit.spring.boot.core.BasicTypeConverterFactory
- retrofit2.converter.jackson.JacksonConverterFactory

可选 Converter 列表:Gson / Jackson / Moshi / Protobuf / Wire / SimpleXML / JAXB / fastJson。

改 Jackson 配置(比如全局忽略未知字段):直接覆盖 JacksonConverterFactory 的 Bean 配置即可——不用改 starter。

特性 5:文件上传——@Multipart + MultipartBody.Part

接口声明:

@POST("upload")
@Multipart
Void upload(@Part MultipartBody.Part file);

调用代码——构造 MultipartBody.Part:

public ResponseEntity importTerminology(MultipartFile file) throws IOException {
// 文件名做 URLEncoder,避免中文乱码
String fileName = URLEncoder.encode(
Objects.requireNonNull(file.getOriginalFilename()), "utf-8");
okhttp3.RequestBody requestBody = okhttp3.RequestBody.create(
MediaType.parse("multipart/form-data"), file.getBytes());
MultipartBody.Part part = MultipartBody.Part.createFormData("file", fileName, requestBody);
apiService.upload(part);
return ok().build();
}

实战注意点:

  • 文件名做 URLEncoder——服务端拿到带中文的文件名才不会乱码
  • 大文件流式上传——别一次性 getBytes() 读全部内容到内存,几 GB 文件会 OOM
  • 超时调高——上传比普通请求慢得多,记得加 @RetrofitClient(writeTimeout = 60_000) 之类的 override

一句话收口

starter 还有一堆冷门特性(自定义 OkHttpClient、注解式拦截器、错误解码器、调用适配器、元注解、动态 URL、GET 带 Body 绕过……),90% 项目用不上。

先把上面 5 个高频特性玩熟,再按需查官方文档——别一上来就背完整个特性列表,那是给自己增加 cognitive load。

工程的本质从来不是”用了多少特性”,而是”在合适的时机用对的特性”。


附图:OkHttp 源码硬编码了 “GET 不允许带 Body” 的限制(讨论:https://github.com/square/okhttp/issues/3154):

偶尔遇到 ES 这种”GET 带 query body”的遗留接口,用小写 method = "get" 就能绕过——按需查文档。

文章目录
  1. 1. 它解决了 Spring Boot 写 HTTP 调用的什么问题
  2. 2. 放到生态里看:和 OkHttp / Feign / WebClient 比,它的位置在哪
  3. 3. 快速上手:三步从依赖到调用
    1. 3.0.1. 第一步:引入依赖
    2. 3.0.2. 第二步:定义接口
    3. 3.0.3. 第三步:注入使用
  • 4. HTTP 注解全家桶(直接复用 Retrofit)
  • 5. 配置全景:一份能直接抄的 yaml
  • 6. 5 个真的会用上的高频特性
    1. 6.0.1. 特性 1:日志打印——从全局到声明式
    2. 6.0.2. 特性 2:请求重试——三种规则按场景挑
    3. 6.0.3. 特性 3:熔断降级——Sentinel / Resilience4j 任选
    4. 6.0.4. 特性 4:数据转换器——默认 Jackson 够用
    5. 6.0.5. 特性 5:文件上传——@Multipart + MultipartBody.Part
  • 7. 一句话收口