1. 概述
在本教程中,我们将介绍 Spring Cloud OpenFeign —— 一个用于 Spring Boot 应用程序的声明式 REST 客户端。
Feign 提供了可插拔的注解支持,使得编写 Web 服务客户端变得更加简单,支持 Feign 注解和 JAX-RS 注解。
此外,Spring Cloud 还支持 Spring MVC 注解,并允许使用与 Spring Web 中相同的 HttpMessageConverters。
使用 Feign 的一个显著优势是:我们无需编写任何调用服务的代码,只需要定义一个接口即可。
2. 依赖配置
首先,我们创建一个 Spring Boot Web 项目,并在 pom.xml 文件中添加 spring-cloud-starter-openfeign 依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
同时,还需要添加 spring-cloud-dependencies:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
可以在 Maven Central 上找到 spring-cloud-starter-openfeign 和 spring-cloud-dependencies 的最新版本。
3. Feign 客户端
接下来,我们需要在主类上添加 @EnableFeignClients 注解:
@SpringBootApplication
@EnableFeignClients
public class ExampleApplication {
public static void main(String[] args) {
SpringApplication.run(ExampleApplication.class, args);
}
}
通过这个注解,Spring Boot 会自动扫描标记为 Feign 客户端的接口。
然后,使用 @FeignClient 注解声明一个 Feign 客户端:
@FeignClient(value = "jplaceholder", url = "https://jsonplaceholder.typicode.com/")
public interface JSONPlaceHolderClient {
@RequestMapping(method = RequestMethod.GET, value = "/posts")
List<Post> getPosts();
@RequestMapping(method = RequestMethod.GET, value = "/posts/{postId}", produces = "application/json")
Post getPostById(@PathVariable("postId") Long postId);
}
在这个例子中,我们配置了一个客户端用于访问 JSONPlaceholder APIs。
@FeignClient 注解中的 value 是客户端名称(任意命名),url 指定了 API 的基础 URL。
由于这是一个 Feign 客户端,我们可以使用 Spring Web 注解来声明要调用的接口。
4. 配置
非常重要的一点是:每个 Feign 客户端都由一组可定制的组件构成。
Spring Cloud 会为每个命名的客户端按需创建默认的组件集合,使用 FeignClientsConfiguration 类进行配置,我们可以在下一节中看到如何自定义这些组件。
该类包含以下 Bean:
- Decoder – ResponseEntityDecoder,封装了 SpringDecoder,用于解码响应
- Encoder – SpringEncoder,用于编码请求体
- Logger – Slf4jLogger 是 Feign 默认的日志记录器
- Contract – SpringMvcContract,提供注解处理能力
- Feign-Builder – HystrixFeign.Builder,用于构建组件
- Client – LoadBalancerFeignClient 或默认的 Feign 客户端
4.1. 自定义 Bean 配置
如果我们想自定义其中某些 Bean,可以创建一个 Configuration 类,并将其添加到 @FeignClient 注解中:
@FeignClient(value = "jplaceholder",
url = "https://jsonplaceholder.typicode.com/",
configuration = ClientConfiguration.class)
public class ClientConfiguration {
@Bean
public OkHttpClient client() {
return new OkHttpClient();
}
}
在这个例子中,我们告诉 Feign 使用 OkHttpClient 替代默认客户端以支持 HTTP/2。
Feign 支持多种客户端,例如 ApacheHttpClient,它会在请求中发送更多头部信息,比如 Content-Length,某些服务器可能需要这些信息。
使用这些客户端时,别忘了在 pom.xml 中添加相应的依赖:
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
可以在 Maven Central 上找到 feign-okhttp 和 feign-httpclient 的最新版本。
4.2. 使用属性配置
除了使用 Configuration 类,我们也可以使用 application.properties 或 application.yaml 文件来配置 Feign 客户端,如下例所示:
feign:
client:
config:
default:
connectTimeout: 5000
readTimeout: 5000
loggerLevel: basic
通过这个配置,我们将每个客户端的连接超时和读取超时设置为 5 秒,日志级别为 basic。
我们也可以使用特定客户端名称进行配置:
feign:
client:
config:
jplaceholder:
如果同时配置了 Configuration Bean 和属性文件,属性配置会覆盖 Bean 中的设置。
5. 拦截器
添加拦截器是 Feign 提供的另一个实用功能。
拦截器可以执行各种隐式任务,比如身份验证和日志记录,适用于每个 HTTP 请求/响应。
在本节中,我们将实现自己的拦截器,同时使用 Spring Cloud OpenFeign 提供的现成拦截器,两者都会 为每个请求添加基本的身份验证头。
5.1. 实现 RequestInterceptor
让我们实现一个自定义请求拦截器:
@Bean
public RequestInterceptor requestInterceptor() {
return requestTemplate -> {
requestTemplate.header("user", username);
requestTemplate.header("password", password);
requestTemplate.header("Accept", ContentType.APPLICATION_JSON.getMimeType());
};
}
要将拦截器添加到请求链中,只需将该 Bean 添加到 Configuration 类中,或像之前一样在属性文件中声明:
feign:
client:
config:
default:
requestInterceptors:
com.baeldung.cloud.openfeign.JSONPlaceHolderInterceptor
5.2. 使用 BasicAuthRequestInterceptor
或者,我们可以使用 Spring Cloud OpenFeign 提供的 BasicAuthRequestInterceptor 类:
@Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
return new BasicAuthRequestInterceptor("username", "password");
}
简单粗暴,现在所有请求都会包含基本身份验证头。
6. Hystrix 支持
Feign 支持 Hystrix,如果我们启用了它,就可以实现降级逻辑(fallback pattern)。
在降级模式中,当远程服务调用失败时,服务消费者不会抛出异常,而是执行备用代码路径,尝试通过其他方式完成操作。
要启用 Hystrix,只需在配置文件中添加 feign.hystrix.enabled=true。
然后我们可以实现降级方法,当服务失败时会被调用:
@Component
public class JSONPlaceHolderFallback implements JSONPlaceHolderClient {
@Override
public List<Post> getPosts() {
return Collections.emptyList();
}
@Override
public Post getPostById(Long postId) {
return null;
}
}
为了让 Feign 知道我们提供了降级类,还需要在 @FeignClient 注解中设置 fallback 类:
@FeignClient(value = "jplaceholder",
url = "https://jsonplaceholder.typicode.com/",
fallback = JSONPlaceHolderFallback.class)
public interface JSONPlaceHolderClient {
// APIs
}
7. 日志
每个 Feign 客户端都会默认创建一个日志记录器。
要启用日志,可以在 application.properties 文件中使用客户端接口的包名进行声明:
logging.level.com.baeldung.cloud.openfeign.client: DEBUG
或者,如果只想为某个特定客户端启用日志,可以使用完整类名:
logging.level.com.baeldung.cloud.openfeign.client.JSONPlaceHolderClient: DEBUG
注意:Feign 的日志仅响应 DEBUG 级别。
我们可以为每个客户端配置不同的 Logger.Level,以控制日志输出内容:
public class ClientConfiguration {
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.BASIC;
}
}
可选的日志级别有四种:
- NONE – 不记录日志(默认)
- BASIC – 只记录请求方法、URL 和响应状态
- HEADERS – 记录基础信息以及请求和响应头
- FULL – 记录请求和响应的 body、headers 和 metadata
8. 错误处理
Feign 默认的错误处理器 ErrorDecoder.default 会抛出 FeignException。
这种行为并不总是最合适的,所以 我们可以使用 CustomErrorDecoder 来自定义异常处理:
public class CustomErrorDecoder implements ErrorDecoder {
@Override
public Exception decode(String methodKey, Response response) {
switch (response.status()){
case 400:
return new BadRequestException();
case 404:
return new NotFoundException();
default:
return new Exception("Generic error");
}
}
}
然后,像之前一样,通过在 Configuration 类中添加 Bean 来替换默认的 ErrorDecoder:
public class ClientConfiguration {
@Bean
public ErrorDecoder errorDecoder() {
return new CustomErrorDecoder();
}
}
9. 总结
在本文中,我们讨论了 Spring Cloud OpenFeign 及其在简单示例应用中的实现。
我们还了解了如何配置客户端、为请求添加拦截器,以及使用 Hystrix 和 ErrorDecoder 进行错误处理。
一如既往,本文中的所有代码示例都可以在 GitHub 上找到。