本文目录导读:

Feign 是一个声明式的 HTTP 客户端,它允许你通过定义 Java 接口和注解来调用远程 Web 服务,而无需编写复杂的 HTTP 请求代码,下面我来系统性地介绍 Feign 声明式客户端的使用方法。
核心概念
声明式客户端的核心思路是:你只需要定义一个接口,Feign 会动态生成它的实现类,你通过注解描述请求的 URL、方法、参数和请求体,Feign 自动完成 HTTP 调用。
基本用法
添加依赖
<!-- Spring Cloud Feign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
启用 Feign
在启动类上添加 @EnableFeignClients 注解:
@SpringBootApplication
@EnableFeignClients
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
定义声明式接口
创建接口,使用 @FeignClient 注解指定服务名,方法上使用 Spring MVC 注解:
@FeignClient(name = "user-service", url = "http://localhost:8081")
public interface UserClient {
// GET 请求
@GetMapping("/users/{id}")
User getUserById(@PathVariable("id") Long id);
// POST 请求
@PostMapping("/users")
User createUser(@RequestBody User user);
// 带查询参数
@GetMapping("/users")
List<User> getUsers(@RequestParam("page") int page,
@RequestParam("size") int size);
// 路径参数和请求头
@GetMapping("/users/{id}/details")
UserDetail getUserDetail(@PathVariable("id") Long id,
@RequestHeader("Authorization") String token);
}
调用接口
注入客户端接口,直接调用方法:
@Service
public class UserService {
@Autowired
private UserClient userClient;
public User getUser(Long id) {
// 调用远程服务,就像调用本地方法一样
return userClient.getUserById(id);
}
public List<User> getUsers() {
return userClient.getUsers(1, 10);
}
}
进阶配置
自定义配置
为单个 Feign 客户端配置:
@Configuration
public class FeignConfig {
@Bean
public Logger.Level feignLoggerLevel() {
return Logger.Level.FULL; // 打印完整请求/响应日志
}
@Bean
public RequestInterceptor requestInterceptor() {
return requestTemplate -> {
// 为所有请求添加 Header
requestTemplate.header("X-Request-Id", UUID.randomUUID().toString());
};
}
}
// 关联配置到特定客户端
@FeignClient(name = "user-service", configuration = FeignConfig.class)
public interface UserClient {
// ...
}
超时和重试配置
# application.yml
feign:
client:
config:
default: # 全局配置
connectTimeout: 5000
readTimeout: 5000
loggerLevel: FULL
user-service: # 特定服务配置
connectTimeout: 3000
readTimeout: 10000
负载均衡(集成 Ribbon)
@FeignClient(name = "user-service") // 不指定 url,使用服务发现
public interface UserClient {
// ...
}
此时需要配置 Ribbon:
user-service:
ribbon:
listOfServers: localhost:8081,localhost:8082
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
熔断降级(集成 Hystrix)
feign:
hystrix:
enabled: true
实现降级类:
@Component
public class UserClientFallback implements UserClient {
@Override
public User getUserById(Long id) {
return new User(); // 返回默认用户或 null
}
@Override
public User createUser(User user) {
return null; // 创建失败时返回 null
}
}
// 在 Feign 客户端中指定 fallback
@FeignClient(name = "user-service", fallback = UserClientFallback.class)
public interface UserClient {
// ...
}
支持的注解
Feign 支持以下常用的 Spring MVC 注解:
| 注解 | 用途 |
|---|---|
@RequestMapping |
通用请求映射 |
@GetMapping |
GET 请求 |
@PostMapping |
POST 请求 |
@PutMapping |
PUT 请求 |
@DeleteMapping |
DELETE 请求 |
@PathVariable |
路径参数 |
@RequestParam |
查询参数 |
@RequestBody |
请求体 |
@RequestHeader |
请求头 |
@RequestPart |
文件上传 |
复杂场景示例
文件上传
@FeignClient(name = "file-service")
public interface FileClient {
@PostMapping(value = "/files/upload",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
String uploadFile(@RequestPart("file") MultipartFile file);
}
动态 URL
@FeignClient(name = "dynamic-service")
public interface DynamicClient {
@RequestMapping(method = RequestMethod.GET, value = "/api/data")
String getData(@RequestParam("url") String dynamicUrl);
}
参数编码
@FeignClient(name = "search-service")
public interface SearchClient {
@GetMapping("/search")
List<Result> search(@SpringQueryMap SearchParam param);
}
// 查询参数对象
@Data
public class SearchParam {
private String q;
private int page;
private int size;
}
最佳实践
-
统一接口设计:为每个远程服务定义一个 Feign Client 接口,放在单独的包中。
-
使用 DTO:为远程调用定义专门的 DTO 类,避免直接暴露内部实体。
-
错误处理:
- 使用
fallback实现断路器 - 使用
ErrorDecoder自定义异常处理
- 使用
-
日志配置:
logging: level: com.example.feign.client: DEBUG -
参数校验:在客户端接口上使用 Bean Validation 注解。
Feign 的声明式客户端极大地简化了远程服务调用,让你像调用本地方法一样调用 HTTP 服务,掌握这些用法,可以显著提高微服务间的通信效率和代码可维护性。
标签: Feign声明式客户端