@FeignClient 的接口是怎么变成能发 HTTP 请求的对象的?注解会变成什么样的请求?为什么只写 name 会启动失败?错误是启动时还是调用时暴露?这篇在 Spring Boot 4.0.8、Spring Cloud OpenFeign 5.0.3 上用一个会记录请求的后端逐项看。
应用刚启动、还没有发过任何 Feign 调用时,FeignClientFactory 里的上下文 = [orders](这个请求本身 31 ms,先把 DispatcherServlet 预热掉)
第一次调用 GET /get → {"ok": true},耗时 14 ms
后面 5 次调用耗时的中位数 2 ms
接口 OrdersApi 的实现类 = demo.$ProxyN,Proxy.isProxyClass = true,InvocationHandler = feign.ReflectiveFeign$FeignInvocationHandler
工厂 bean = FeignClientFactoryBean;FeignClientFactory 里的上下文 = [orders]
Encoder = …openfeign.support.SpringEncoder
Decoder = feign.optionals.OptionalDecoder
Contract = …openfeign.support.SpringMvcContract
Client = 无
Retryer = feign.Retryer$1
Logger = 无
ErrorDecoder = 无
Options = 无
default 方法:default 方法在本地执行JDK 动态代理;注入时创建;每个客户端名一个子上下文,里面有 Encoder、Decoder、Contract、Retryer。
api.get("42", true, "t-1") → GET /orders/42?verbose=true,X-Trace = t-1,Accept = */*,User-Agent = Java/21.0.12.1,HTTP 版本 HTTP/1.1
api.create(new Item("A1", 3)) → POST /orders,Content-Type = application/json,请求体 = {"id":"A1","qty":3};调用方拿到 Item[id=A1, qty=3]
api.search(@SpringQueryMap new Query("bob", 2)) → GET /search?name=bob&page=2路径、查询参数、请求头、JSON 请求体、@SpringQueryMap 展开。
只引入 spring-cloud-starter-openfeign(没有 loadbalancer):应用启动失败:Caused by: java.lang.IllegalStateException: No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-loadbalancer?
加上 spring-cloud-starter-loadbalancer:lb.get("7", false, "t-2") → 后端收到 GET /orders/7?verbose=false,X-Trace = t-2;调用方拿到 {"ok": true}
Client = …openfeign.loadbalancer.FeignBlockingLoadBalancerClient;FeignClientFactory 里的上下文 = [lb-orders]需要 loadbalancer starter,否则启动失败;有了之后 Client 是 FeignBlockingLoadBalancerClient。
应用启动失败:Caused by: java.lang.IllegalStateException: Method has too many Body parameters: public abstract demo.Main$Item demo.Main$OrdersApi.create(demo.Main$Item,demo.Main$Item)
创建代理时就报错,不会拖到第一次调用。