今天向外域提供fein接口,get请求忘记写入@requesParam注解导致,fein直接变成了post请求,引发了我的好奇,为什么Spring mvc可以,他不可以昵,让我们从源码开始吧,顺便解析完整的fein请求链路:
先给完整链路
Feign 请求完整链路:
1. ReflectiveFeign.FeignInvocationHandler.invoke()
→ 代理方法入口,确定请求的类及参数
2. SynchronousMethodHandler.invoke()
→ 通过 buildTemplateFromArgs.create(argv) 组装 RequestTemplate
3. feign.Contract.BaseContract.parseAndValidateMetadata()
→ 🔑 关键分叉点:判断参数是 param 还是 body
→ 未加 @RequestParam 的参数被识别为 body 参数
4. ReflectiveFeign.BuildFormEncodedTemplateFromArgs.resolve()
→ 未加注解走此路径 → encoder.encode() 将参数写入 body
→ 加了注解走 RequestTemplate.resolve() → 参数拼入 URL query
5. FeignBlockingLoadBalancerClient.execute()
→ 组装最终 URL,选择负载均衡客户端
6. JDK HttpURLConnection
→ 执行 connection.getOutputStream() 时
→ 🐛 JDK 内部惰性初始化机制强制将 Method 改写为 POST
1、走入fein-core的
1)ReflectiveFeign.FeignInvocationHandler.invoke的代理方法,确定请求的类及参数等,
直到进入选择对应的负载均衡客户端,我这边的默认是第二个
org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient类有buildRequest组装url,然后通过executeWithLoadBalancerLifecycleProcessing进入到
org.springframework.cloud.openfeign.loadbalancer.LoadBalancerUtils的
executeWithLoadBalancerLifecycleProcessing方法中feignClient.execute(feignRequest, options); 重新选择客户端调用,就进入到下面这张图了

是不是发现不对了,哈哈哈哈,回过头,发现是执行了
OutputStream out = connection.getOutputStream();
就会变,刚开始我也没有搞明白,查阅资料说是JDK HttpURLConnection 的内部惰性初始化 + 强制改写 Method 导致的,好了找到问题,然后又有疑惑,怎么办尼,那就试试正确的方法,看看是怎么解析的。你会发现他不会走到这里,so,说明了,还有一个地方,在确认request.body()是不是为null的地方,这里也是一个因素
好了正确的,我们又要回到
fein-core的SynchronousMethodHandler组装请求的方法
RequestTemplate template = buildTemplateFromArgs.create(argv);
RequestTemplate template = resolve(argv, mutable, varBuilder);

然后一直走encode方法,就会发现有一个写入body的,但是他需要判断outputMessage!=null,
不为 messageConverter instanceof GenericHttpMessageConverter
会把我们的值,自动判断为body


到这其实差不多就结束了,但是还会有个疑问
对的请求是怎样走的昵,我重新模拟了一下,只有没加注解的会走这个
,
feign.ReflectiveFeign.BuildFormEncodedTemplateFromArgs.resolve
然后走encoder.encode方法,没加的是不会,所以还能继续往上推,RequestTemplate mutable的resolve如何定义的,
feign.ReflectiveFeign.BuildTemplateByResolvingArgs.resolve
加上了注解就会走
feign.RequestTemplate.resolve
所以现在问题原因已经定位到了,获取这个配置,导致resolve方法不一样
RequestTemplate mutable = RequestTemplate.from(metadata.template());
那RequestTemplate是什么时候生成的昵,上代码
feign.Contract.BaseContract.parseAndValidateMetadata方法
判断是什么参数的地方,param还是body,前面遗留的为什么不加requestParam会走body参数,是不是真相大白了
