OpenFeign 参数传递详解:多种传参方式与最佳实践
仔细观察可以发现,Feign 客户端与服务提供者的接口在结构上非常相似。前面我们已经演示了如何从 URL 中提取参数,接下来将深入介绍其他几种常见的参数传递方式。以下示例侧重于展示代码的编写模式,并不涉及具体的业务逻辑实现。
传递单个参数
服务提供端 product-service 的接口定义如下:
@RequestMapping("/product")
@RestController
public class ProductController {
@RequestMapping("/p1")
public String p1(Integer id){
return "p1 接收到参数:" + id;
}
}
对应的 Feign 客户端接口:
@FeignClient(value = "product-service", path = "/product")
public interface ProductApi {
@RequestMapping("/p1")
String p1(@RequestParam("id") Integer id);
}
需要特别留意的是:使用 @RequestParam 进行参数绑定时,该注解是必不可少的。
服务调用方 order-service 的实现:
@RequestMapping("/feign")
@RestController
public class TestFeignController {
@Autowired
private ProductApi productApi;
@RequestMapping("/o1")
public String o1(Integer id) {
return productApi.p1(id);
}
}
验证远程调用的请求地址:https://127.0.0.1:8080/feign/o1?id=5

传递多个参数
当需要传递多个参数时,可以分别使用多个 @RequestParam 注解进行一一绑定。
服务提供端 product-service 的接口:
@RequestMapping("/p2")
public String p2(Integer id, String name) {
return "p2 接受到参数,id:" + id + ", name: " + name;
}
Feign 客户端的写法:
@RequestMapping("/p2")
String p2(@RequestParam("id") Integer id, @RequestParam("name") String name);
服务调用方 order-service 的代码:
@RequestMapping("/o2")
public String o2(Integer id, String name) {
return productApi.p2(id, name);
}
测试远程调用的请求示例:https://127.0.0.1:8080/feign/o2?id=5&name=zhangsan

传递对象参数
服务提供端 product-service 接收对象参数的方式:
@RequestMapping("/p3")
public String p3(ProductInfo productInfo) {
return "接收到对象,productInfo:" + productInfo;
}
Feign 客户端使用 @SpringQueryMap 注解传递对象:
@RequestMapping("/p3")
String p3(@SpringQueryMap ProductInfo productInfo);
服务调用方 order-service 的实现:
@RequestMapping("/o3")
public String o3(ProductInfo productInfo) {
return productApi.p3(productInfo);
}
远程调用测试地址:https://127.0.0.1:8080/feign/o3?id=5&productName=zhangsan

传递 JSON 数据
服务提供端 product-service 通过 @RequestBody 接收 JSON 格式的对象:
@RequestMapping("/p4")
public String p4(@RequestBody ProductInfo productInfo) {
return "接收到对象,productInfo: " + productInfo;
}
Feign 客户端同样使用 @RequestBody 传递 JSON 数据:
@RequestMapping("/p4")
String p4(@RequestBody ProductInfo productInfo);
}
服务调用方 order-service 的请求处理:
@RequestMapping("/o4")
public String o4(@RequestBody ProductInfo productInfo) {
System.out.println(productInfo.toString());
return productApi.p4(productInfo);
}
验证远程调用的请求地址:https://127.0.0.1:8080/feign/o4

Feign 最佳实践指南
所谓最佳实践,是指项目在持续演进中沉淀下来的最简洁、最高效的编码方式。从前面的示例中可以看出,Feign 客户端与服务提供者的 controller 在代码结构上高度相似——比如下面这对示例:
Feign客户端接口
@FeignClient(value = "product-service", path = "/product")
public interface ProductApi {
@RequestMapping("/{productId}")
ProductInfo getProductById(@PathVariable("productId") Integer productId);
}
- 服务提供端
controller实现
@RequestMapping("/product")
@RestController
public class ProductController {
@RequestMapping("/{productId}")
public ProductInfo getProductById(@PathVariable("productId") Integer productId) {
//...
}
是否存在更加简洁的编码方式?答案是肯定的。
Feign 接口继承方案
Feign 原生支持接口继承机制,通过它将公共的 API 定义抽取到统一的位置。服务提供者负责实现该接口,而服务消费者则通过继承该接口来定义 Feign 客户端,从而避免重复编写相似的代码。
创建独立模块
将公共接口封装到一个独立的 Jar 包中,供服务提供方和消费方共同引用。

添加项目依赖
org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-openfeign
定义公共接口
将 ProductApi 和 ProductInfo 迁移至 product-api 模块中:
package org.example;
import org.example.model.ProductInfo;
import org.springframework.cloud.openfeign.SpringQueryMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
public interface ProductInterface {
@RequestMapping("/{productId}")
ProductInfo getProductById(@PathVariable("productId") Integer productId);
@RequestMapping("/p1")
String p1(@RequestParam("id") Integer id);
@RequestMapping("/p2")
String p2(@RequestParam("id") Integer id, @RequestParam("name") String name);
@RequestMapping("/p3")
String p3(@SpringQueryMap ProductInfo productInfo);
@RequestMapping("/p4")
String p4(@RequestBody ProductInfo productInfo);
}
模块的目录结构展示如下:

以上就是 OpenFeign 在多种场景下参数传递方式的全面解析,更多关于 OpenFeign 参数传递的进阶技巧,请参考后续的专题文章。
