注解详解
OpenFeign 支持两种风格的注解:Spring MVC 注解和原生 Feign 注解。本章将深入讲解如何使用这些注解定义各种类型的 HTTP 请求。
Spring MVC 注解
Spring Cloud OpenFeign 默认使用 SpringMvcContract,这意味着你可以在 Feign 接口上使用熟悉的 Spring MVC 注解。
@GetMapping / @PostMapping / @PutMapping / @DeleteMapping
这些注解用于指定 HTTP 方法和请求路径:
@FeignClient(name = "user-service")
public interface UserClient {
// GET 请求
@GetMapping("/users")
List<User> getAllUsers();
@GetMapping("/users/{id}")
User getUserById(@PathVariable("id") Long id);
// POST 请求
@PostMapping("/users")
User createUser(@RequestBody User user);
// PUT 请求
@PutMapping("/users/{id}")
User updateUser(@PathVariable("id") Long id, @RequestBody User user);
// DELETE 请求
@DeleteMapping("/users/{id}")
void deleteUser(@PathVariable("id") Long id);
}
@RequestMapping
@RequestMapping 是更通用的注解,可以指定 HTTP 方法:
@FeignClient(name = "user-service")
public interface UserClient {
@RequestMapping(method = RequestMethod.GET, value = "/users")
List<User> getAllUsers();
@RequestMapping(method = RequestMethod.POST, value = "/users",
consumes = "application/json", produces = "application/json")
User createUser(@RequestBody User user);
// 同时支持多种 HTTP 方法(不推荐在 Feign 中使用)
@RequestMapping(value = "/users/{id}", method = {RequestMethod.GET, RequestMethod.POST})
User getUserOrPost(@PathVariable("id") Long id);
}
@PathVariable
@PathVariable 用于绑定 URL 路径中的变量:
@FeignClient(name = "order-service")
public interface OrderClient {
// 单个路径变量
@GetMapping("/orders/{orderId}")
Order getOrder(@PathVariable("orderId") Long orderId);
// 多个路径变量
@GetMapping("/users/{userId}/orders/{orderId}")
Order getUserOrder(
@PathVariable("userId") Long userId,
@PathVariable("orderId") Long orderId
);
// 路径变量支持正则表达式
@GetMapping("/orders/{orderId:\\d+}") // 只匹配数字
Order getNumericOrder(@PathVariable("orderId") Long orderId);
}
使用要点:
value属性指定路径变量的名称,必须与 URL 中的占位符一致- 如果方法参数名与路径变量名相同,可以省略
value属性(但建议显式指定,避免编译时丢失参数名)
@RequestParam
@RequestParam 用于绑定 URL 查询参数:
@FeignClient(name = "user-service")
public interface UserClient {
// 单个查询参数
@GetMapping("/users/search")
User findByUsername(@RequestParam("username") String username);
// 多个查询参数
@GetMapping("/users/search")
List<User> searchUsers(
@RequestParam("name") String name,
@RequestParam("status") String status
);
// 请求 URL: /users/search?name=xxx&status=xxx
// 可选参数
@GetMapping("/users")
List<User> getUsers(
@RequestParam(value = "page", required = false, defaultValue = "1") Integer page,
@RequestParam(value = "size", required = false, defaultValue = "10") Integer size
);
// 集合参数
@GetMapping("/users/by-ids")
List<User> getUsersByIds(@RequestParam("ids") List<Long> ids);
// 请求 URL: /users/by-ids?ids=1&ids=2&ids=3
// Map 参数(动态查询参数)
@GetMapping("/users/search")
List<User> searchUsers(@RequestParam Map<String, String> params);
}
使用要点:
required属性默认为true,如果参数可能为空,需设置为falsedefaultValue属性设置默认值,设置了默认值后required自动变为false- 使用
Map类型可以传递动态查询参数
@RequestBody
@RequestBody 用于传递请求体,通常用于 POST 和 PUT 请求:
@FeignClient(name = "user-service")
public interface UserClient {
// 传递 JSON 对象
@PostMapping("/users")
User createUser(@RequestBody User user);
// 传递字符串
@PostMapping("/users/{id}/notes")
void addNote(@PathVariable("id") Long id, @RequestBody String note);
// 传递 Map
@PostMapping("/users/batch")
List<User> batchCreate(@RequestBody List<User> users);
}
使用要点:
- 请求体会被序列化为 JSON(默认使用 Jackson)
- 一个方法只能有一个
@RequestBody参数
@RequestHeader
@RequestHeader 用于添加请求头:
@FeignClient(name = "api-service")
public interface ApiClient {
// 单个请求头
@GetMapping("/api/data")
String getData(@RequestHeader("Authorization") String token);
// 多个请求头
@GetMapping("/api/data")
String getData(
@RequestHeader("Authorization") String token,
@RequestHeader("X-Request-Id") String requestId
);
// 所有请求头
@GetMapping("/api/headers")
String getHeaders(@RequestHeader Map<String, String> headers);
}
@SpringQueryMap
@SpringQueryMap 用于将 POJO 对象转换为查询参数:
public class UserQuery {
private String name;
private String email;
private Integer status;
private Integer page = 1;
private Integer size = 10;
// getter、setter 省略
}
@FeignClient(name = "user-service")
public interface UserClient {
@GetMapping("/users/search")
List<User> searchUsers(@SpringQueryMap UserQuery query);
// 请求 URL: /users/search?name=xxx&email=xxx&status=1&page=1&size=10
}
与 @RequestParam Map 的区别:
@SpringQueryMap支持类型安全的 POJO@RequestParam Map更灵活,但没有类型检查
@MatrixVariable
@MatrixVariable 用于处理矩阵变量(路径段中的键值对):
@FeignClient(name = "api-service")
public interface ApiClient {
// 矩阵变量示例:/api/cars;color=red;year=2024
@GetMapping("/api/cars/{filter}")
List<Car> getCars(@MatrixVariable("color") String color,
@MatrixVariable("year") Integer year);
// 使用 Map 接收所有矩阵变量
@GetMapping("/api/cars/{filter}")
List<Car> getCars(@MatrixVariable Map<String, String> filter);
}
使用 @MatrixVariable 时,路径占位符名称必须与变量名称一致。
原生 Feign 注解
除了 Spring MVC 注解,OpenFeign 还支持原生注解。要使用原生注解,需要自定义 Contract:
配置原生 Contract
public class FeignConfig {
@Bean
public Contract feignContract() {
return new Contract.Default(); // 使用原生 Feign Contract
}
}
@RequestLine
@RequestLine 用于指定 HTTP 方法和路径:
@FeignClient(name = "user-service", configuration = FeignConfig.class)
public interface UserClient {
@RequestLine("GET /users")
List<User> getAllUsers();
@RequestLine("GET /users/{id}")
User getUserById(@Param("id") Long id);
@RequestLine("POST /users")
User createUser(User user); // 无需 @RequestBody
}
@Param
@Param 用于绑定路径变量或查询参数:
@FeignClient(name = "user-service", configuration = FeignConfig.class)
public interface UserClient {
@RequestLine("GET /users/{id}")
User getUserById(@Param("id") Long id);
@RequestLine("GET /users?name={name}&status={status}")
List<User> searchUsers(@Param("name") String name, @Param("status") String status);
// 支持自定义展开器
@RequestLine("GET /users/{id}")
User getUserById(@Param(value = "id", expander = IdExpander.class) Long id);
}
@Headers
@Headers 用于添加请求头:
@FeignClient(name = "api-service", configuration = FeignConfig.class)
public interface ApiClient {
// 方法级别
@RequestLine("GET /api/data")
@Headers({"Authorization: Bearer {token}", "Content-Type: application/json"})
String getData(@Param("token") String token);
// 接口级别(应用于所有方法)
@Headers("Accept: application/json")
public interface ApiClient {
@RequestLine("GET /api/users")
List<User> getUsers();
}
}
@Body
@Body 用于指定请求体模板:
@FeignClient(name = "user-service", configuration = FeignConfig.class)
public interface UserClient {
@RequestLine("POST /users")
@Headers("Content-Type: application/json")
@Body("%7B\"name\": \"{name}\", \"email\": \"{email}\"%7D")
User createUser(@Param("name") String name, @Param("email") String email);
}
混合使用注解
在实际项目中,推荐统一使用 Spring MVC 注解,因为它们更直观,且与 Spring 生态无缝集成。但如果需要调用非 Spring 服务,原生注解可能更灵活。
// 推荐:统一使用 Spring MVC 注解
@FeignClient(name = "user-service")
public interface UserClient {
@GetMapping("/users/{id}")
User getUserById(@PathVariable("id") Long id);
}
// 特殊场景:使用原生注解
@FeignClient(name = "legacy-service", configuration = NativeFeignConfig.class)
public interface LegacyClient {
@RequestLine("GET /api/v1/users/{id}")
LegacyUser getUser(@Param("id") String id);
}
文件上传
上传单个文件
@FeignClient(name = "file-service")
public interface FileClient {
@PostMapping(value = "/files/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
FileResponse uploadFile(@RequestPart("file") MultipartFile file);
// 携带额外参数
@PostMapping(value = "/files/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
FileResponse uploadFile(
@RequestPart("file") MultipartFile file,
@RequestParam("category") String category
);
}
上传多个文件
@FeignClient(name = "file-service")
public interface FileClient {
@PostMapping(value = "/files/batch-upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
List<FileResponse> uploadFiles(@RequestPart("files") MultipartFile[] files);
}
使用 byte 数组
如果不想依赖 MultipartFile,可以使用 byte[]:
@FeignClient(name = "file-service")
public interface FileClient {
@PostMapping(value = "/files/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
FileResponse uploadFile(
@RequestPart("file") byte[] file,
@RequestPart("filename") String filename
);
}
文件下载
下载为 byte 数组
@FeignClient(name = "file-service")
public interface FileClient {
@GetMapping("/files/{id}/download")
ResponseEntity<byte[]> downloadFile(@PathVariable("id") Long id);
}
下载为 Resource
@FeignClient(name = "file-service")
public interface FileClient {
@GetMapping("/files/{id}/download")
ResponseEntity<Resource> downloadFile(@PathVariable("id") Long id);
}
使用示例:
@Service
public class FileService {
private final FileClient fileClient;
public void downloadAndSave(Long fileId, String savePath) throws IOException {
ResponseEntity<Resource> response = fileClient.downloadFile(fileId);
if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
try (InputStream is = response.getBody().getInputStream();
FileOutputStream fos = new FileOutputStream(savePath)) {
IOUtils.copy(is, fos);
}
}
}
}
小结
本章详细介绍了 OpenFeign 支持的各种注解:
| 注解 | 用途 | 示例 |
|---|---|---|
@GetMapping 等 | 定义 HTTP 方法和路径 | @GetMapping("/users") |
@PathVariable | 绑定路径变量 | @PathVariable("id") Long id |
@RequestParam | 绑定查询参数 | @RequestParam("name") String name |
@RequestBody | 传递请求体 | @RequestBody User user |
@RequestHeader | 添加请求头 | @RequestHeader("Auth") String token |
@SpringQueryMap | POJO 转查询参数 | @SpringQueryMap UserQuery query |
@RequestPart | 文件上传 | @RequestPart("file") MultipartFile file |
下一章将深入学习 OpenFeign 的配置机制,掌握如何自定义各种行为。