4.0.6
本项目通过自动配置以及与 Spring Environment 和其他 Spring 编程模型惯用法的绑定,为 Spring Boot 应用提供了 OpenFeign 集成。
1. 声明式 REST 客户端: Feign
Feign 是一个声明式的 Web 服务客户端。它使得编写 Web 服务客户端更加容易。要使用 Feign,创建一个接口并对其进行注解。它支持可插拔的注解,包括 Feign 注解和 JAX-RS 注解。Feign 还支持可插拔的编码器和解码器。Spring Cloud 添加了对 Spring MVC 注解的支持,并支持使用 Spring Web 中默认使用的相同 HttpMessageConverters。Spring Cloud 集成了 Eureka、Spring Cloud CircuitBreaker 以及 Spring Cloud LoadBalancer,以便在使用 Feign 时提供一个负载均衡的 http 客户端。
1.1. 如何引入 Feign
要在你的项目中包含 Feign,请使用 group 为 org.springframework.cloud、artifact id 为 spring-cloud-starter-openfeign 的 starter。有关如何使用当前的 Spring Cloud Release Train 设置构建系统的详细信息,请参阅Spring Cloud 项目页面。
Spring Boot 应用示例
@SpringBootApplication
@EnableFeignClients
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@FeignClient("stores")
public interface StoreClient {
@RequestMapping(method = RequestMethod.GET, value = "/stores")
List<Store> getStores();
@RequestMapping(method = RequestMethod.GET, value = "/stores")
Page<Store> getStores(Pageable pageable);
@RequestMapping(method = RequestMethod.POST, value = "/stores/{storeId}", consumes = "application/json")
Store update(@PathVariable("storeId") Long storeId, Store store);
@RequestMapping(method = RequestMethod.DELETE, value = "/stores/{storeId:\\d+}")
void delete(@PathVariable Long storeId);
}
在 @FeignClient 注解中,字符串值(上面的 "stores")是任意的客户端名称,用于创建一个 Spring Cloud LoadBalancer 客户端。你也可以使用 url 属性指定一个 URL(绝对值或仅主机名)。应用程序上下文中 bean 的名称是接口的完全限定名。要指定你自己的别名值,可以使用 @FeignClient 注解的 qualifiers 值。
上面的负载均衡客户端将需要发现 "stores" 服务的物理地址。如果你的应用是 Eureka 客户端,那么它将在 Eureka 服务注册中心解析服务。如果你不想使用 Eureka,你可以在外部配置中使用 SimpleDiscoveryClient 配置服务器列表。
Spring Cloud OpenFeign 支持 Spring Cloud LoadBalancer 阻塞模式的所有可用功能。你可以在项目文档中阅读更多信息。
要在用 @Configuration 注解的类上使用 @EnableFeignClients 注解,请确保指定客户端的位置,例如:@EnableFeignClients(basePackages = "com.example.clients") 或显式列出它们:@EnableFeignClients(clients = InventoryServiceFeignClient.class) |
1.1.1. 属性解析模式
在创建 Feign 客户端 bean 时,我们解析通过 @FeignClient 注解传递的值。从 4.x 版本开始,这些值是急切解析的。这对于大多数用例来说是一个很好的解决方案,并且也支持 AOT。
如果你需要延迟解析属性,请将 spring.cloud.openfeign.lazy-attributes-resolution 属性值设置为 true。
| 对于 Spring Cloud Contract 测试集成,应使用延迟属性解析。 |
1.2. 覆盖 Feign 默认配置
Spring Cloud 对 Feign 的支持中一个核心概念是命名客户端。每个 feign 客户端都是一组协同工作的组件的一部分,这些组件按需联系远程服务器,并且该集合有一个名称,你作为应用程序开发者使用 @FeignClient 注解为其指定。Spring Cloud 使用 FeignClientsConfiguration 为每个命名客户端按需创建一个新的集合作为 ApplicationContext。这包含(除其他外)一个 feign.Decoder、一个 feign.Encoder 和一个 feign.Contract。可以通过使用 @FeignClient 注解的 contextId 属性来覆盖该集合的名称。
Spring Cloud 允许你通过使用 @FeignClient 声明额外的配置(在 FeignClientsConfiguration 之上)来完全控制 feign 客户端。例如
@FeignClient(name = "stores", configuration = FooConfiguration.class)
public interface StoreClient {
//..
}
在这种情况下,客户端由 FeignClientsConfiguration 中已有的组件以及 FooConfiguration 中的任何组件组成(后者将覆盖前者)。
FooConfiguration 不需要用 @Configuration 注解。但是,如果注解了,那么请注意将其从任何可能包含此配置的 @ComponentScan 中排除,因为它在指定时将成为 feign.Decoder、feign.Encoder、feign.Contract 等的默认来源。这可以通过将其放在与任何 @ComponentScan 或 @SpringBootApplication 分开的、不重叠的包中,或者在 @ComponentScan 中明确排除来避免。 |
使用 @FeignClient 注解的 contextId 属性除了改变 ApplicationContext 集合的名称外,还会覆盖客户端名称的别名,并用作该客户端创建的配置 bean 名称的一部分。 |
以前,使用 url 属性时,不需要 name 属性。现在使用 name 是必需的。 |
在 name 和 url 属性中支持占位符。
@FeignClient(name = "${feign.name}", url = "${feign.url}")
public interface StoreClient {
//..
}
Spring Cloud OpenFeign 默认为 feign 提供以下 bean (BeanType beanName: ClassName)
-
DecoderfeignDecoder:ResponseEntityDecoder(封装了SpringDecoder) -
EncoderfeignEncoder:SpringEncoder -
LoggerfeignLogger:Slf4jLogger -
MicrometerObservationCapabilitymicrometerObservationCapability: 如果 classpath 中存在feign-micrometer并且ObservationRegistry可用 -
MicrometerCapabilitymicrometerCapability: 如果 classpath 中存在feign-micrometer,MeterRegistry可用且ObservationRegistry不可用 -
CachingCapabilitycachingCapability: 如果使用了@EnableCaching注解。可以通过spring.cloud.openfeign.cache.enabled禁用。 -
ContractfeignContract:SpringMvcContract -
Feign.BuilderfeignBuilder:FeignCircuitBreaker.Builder -
ClientfeignClient: 如果 classpath 中存在 Spring Cloud LoadBalancer,则使用FeignBlockingLoadBalancerClient。如果 classpath 中两者都不存在,则使用默认的 feign 客户端。
spring-cloud-starter-openfeign 支持 spring-cloud-starter-loadbalancer。然而,由于它是一个可选依赖,如果你想使用它,你需要确保它已被添加到你的项目中。 |
要使用 OkHttpClient 支持的 Feign 客户端,请确保 classpath 中存在 OKHttpClient 并将 spring.cloud.openfeign.okhttp.enabled 设置为 true。
对于 Apache HttpClient 5 支持的 Feign 客户端,只需确保 classpath 中存在 HttpClient 5 即可,但你仍然可以通过将 spring.cloud.openfeign.httpclient.hc5.enabled 设置为 false 来禁用其在 Feign 客户端中的使用。你可以在使用 Apache HC5 时提供 org.apache.hc.client5.http.impl.classic.CloseableHttpClient 类型的 bean 来定制使用的 HTTP 客户端。
你可以通过在 spring.cloud.openfeign.httpclient.xxx 属性中设置值来进一步定制 http 客户端。仅以前缀 httpclient 开头的属性适用于所有客户端,以前缀 httpclient.hc5 开头的适用于 Apache HttpClient 5,以前缀 httpclient.okhttp 开头的适用于 OkHttpClient。你可以在附录中找到可以定制的完整属性列表。
| 从 Spring Cloud OpenFeign 4 开始,不再支持 Feign Apache HttpClient 4。我们建议改用 Apache HttpClient 5。 |
Spring Cloud OpenFeign 默认情况下*不*为 feign 提供以下 bean,但仍会从应用程序上下文中查找这些类型的 bean 来创建 feign 客户端
-
Logger.Level -
Retryer -
ErrorDecoder -
Request.Options -
Collection<RequestInterceptor> -
SetterFactory -
QueryMapEncoder -
Capability(MicrometerObservationCapability和CachingCapability默认提供)
默认情况下会创建一个类型为 Retryer、值为 Retryer.NEVER_RETRY 的 bean,这将禁用重试。请注意,此重试行为与 Feign 的默认行为不同,Feign 默认行为会自动重试 IOExceptions(将其视为瞬时网络相关异常)以及从 ErrorDecoder 抛出的任何 RetryableException。
创建其中一种类型的 bean 并将其放入 @FeignClient 配置中(例如上面的 FooConfiguration)允许你覆盖描述的每个 bean。例如
@Configuration
public class FooConfiguration {
@Bean
public Contract feignContract() {
return new feign.Contract.Default();
}
@Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
return new BasicAuthRequestInterceptor("user", "password");
}
}
这将 SpringMvcContract 替换为 feign.Contract.Default,并在 RequestInterceptor 集合中添加一个 RequestInterceptor。
@FeignClient 也可以使用配置属性进行配置。
application.yml
spring:
cloud:
openfeign:
client:
config:
feignName:
url: http://remote-service.com
connectTimeout: 5000
readTimeout: 5000
loggerLevel: full
errorDecoder: com.example.SimpleErrorDecoder
retryer: com.example.SimpleRetryer
defaultQueryParameters:
query: queryValue
defaultRequestHeaders:
header: headerValue
requestInterceptors:
- com.example.FooRequestInterceptor
- com.example.BarRequestInterceptor
responseInterceptor: com.example.BazResponseInterceptor
dismiss404: false
encoder: com.example.SimpleEncoder
decoder: com.example.SimpleDecoder
contract: com.example.SimpleContract
capabilities:
- com.example.FooCapability
- com.example.BarCapability
queryMapEncoder: com.example.SimpleQueryMapEncoder
micrometer.enabled: false
此示例中的 feignName 指的是 @FeignClient 的 value,它也与 @FeignClient 的 name 和 @FeignClient 的 contextId 互为别名。在负载均衡场景中,它还对应于将用于检索实例的服务器应用的 serviceId。指定的解码器、重试器及其他类的类必须在 Spring 上下文中有 bean 或具有默认构造函数。
默认配置可以按照上述类似方式在 @EnableFeignClients 的 defaultConfiguration 属性中指定。区别在于此配置将应用于所有 feign 客户端。
如果你更喜欢使用配置属性来配置所有 @FeignClient,你可以使用 default feign 名称创建配置属性。
你可以使用 spring.cloud.openfeign.client.config.feignName.defaultQueryParameters 和 spring.cloud.openfeign.client.config.feignName.defaultRequestHeaders 来指定将随名为 feignName 的客户端的每个请求一起发送的查询参数和头部信息。
application.yml
spring:
cloud:
openfeign:
client:
config:
default:
connectTimeout: 5000
readTimeout: 5000
loggerLevel: basic
如果我们同时创建 @Configuration bean 和配置属性,配置属性将胜出。它会覆盖 @Configuration 的值。但如果你想将优先级更改为 @Configuration,可以将 spring.cloud.openfeign.client.default-to-properties 更改为 false。
如果我们想创建具有相同名称或 URL 的多个 feign 客户端,使它们指向同一服务器但每个都有不同的自定义配置,那么我们必须使用 @FeignClient 的 contextId 属性,以避免这些配置 bean 的名称冲突。
@FeignClient(contextId = "fooClient", name = "stores", configuration = FooConfiguration.class)
public interface FooClient {
//..
}
@FeignClient(contextId = "barClient", name = "stores", configuration = BarConfiguration.class)
public interface BarClient {
//..
}
也可以配置 FeignClient 不继承父上下文中的 bean。你可以通过在 FeignClientConfigurer bean 中覆盖 inheritParentConfiguration() 方法使其返回 false 来实现这一点
@Configuration
public class CustomConfiguration {
@Bean
public FeignClientConfigurer feignClientConfigurer() {
return new FeignClientConfigurer() {
@Override
public boolean inheritParentConfiguration() {
return false;
}
};
}
}
默认情况下,Feign 客户端不会编码斜杠 / 字符。你可以通过将 spring.cloud.openfeign.client.decodeSlash 的值设置为 false 来改变此行为。 |
1.2.1. SpringEncoder 配置
在我们提供的 SpringEncoder 中,我们为二进制内容类型设置 null 字符集,并为所有其他类型设置 UTF-8。
你可以通过将 spring.cloud.openfeign.encoder.charset-from-content-type 的值设置为 true 来修改此行为,转而从 Content-Type 头部中的字符集派生字符集。
1.3. 超时处理
我们可以为默认客户端和命名客户端配置超时。OpenFeign 使用两个超时参数
-
connectTimeout防止因服务器处理时间过长而阻塞调用者。 -
readTimeout从连接建立时开始应用,并在返回响应花费时间过长时触发。
如果服务器未运行或不可用,数据包会返回连接被拒绝。通信以错误消息或降级处理结束。如果 connectTimeout 设置得非常低,这可能发生在 connectTimeout *之前*。执行查找和接收此类数据包所需的时间构成了此延迟的很大一部分。它会根据涉及 DNS 查找的远程主机而变化。 |
1.4. 手动创建 Feign 客户端
在某些情况下,可能需要以一种无法使用上述方法的方式自定义你的 Feign 客户端。在这种情况下,你可以使用 Feign Builder API 创建客户端。下面是一个示例,它创建了两个具有相同接口的 Feign 客户端,但为每个客户端配置了单独的请求拦截器。
@Import(FeignClientsConfiguration.class)
class FooController {
private FooClient fooClient;
private FooClient adminClient;
@Autowired
public FooController(Client client, Encoder encoder, Decoder decoder, Contract contract, MicrometerObservationCapability micrometerObservationCapability) {
this.fooClient = Feign.builder().client(client)
.encoder(encoder)
.decoder(decoder)
.contract(contract)
.addCapability(micrometerObservationCapability)
.requestInterceptor(new BasicAuthRequestInterceptor("user", "user"))
.target(FooClient.class, "https://PROD-SVC");
this.adminClient = Feign.builder().client(client)
.encoder(encoder)
.decoder(decoder)
.contract(contract)
.addCapability(micrometerObservationCapability)
.requestInterceptor(new BasicAuthRequestInterceptor("admin", "admin"))
.target(FooClient.class, "https://PROD-SVC");
}
}
在上面的示例中,FeignClientsConfiguration.class 是 Spring Cloud OpenFeign 提供的默认配置。 |
PROD-SVC 是客户端将要请求的服务名称。 |
Feign Contract 对象定义了接口上哪些注解和值是有效的。自动装配的 Contract bean 提供对 SpringMVC 注解的支持,而不是默认的 Feign 原生注解。 |
你还可以使用 Builder 来配置 FeignClient 不继承父上下文中的 bean。你可以通过在 Builder 上调用 inheritParentContext(false) 来实现这一点。
1.5. Feign Spring Cloud CircuitBreaker 支持
如果 classpath 中存在 Spring Cloud CircuitBreaker 并且 spring.cloud.openfeign.circuitbreaker.enabled=true,Feign 将用断路器封装所有方法。
若要按客户端禁用 Spring Cloud CircuitBreaker 支持,请创建一个作用域为 "prototype" 的普通 Feign.Builder,例如
@Configuration
public class FooConfiguration {
@Bean
@Scope("prototype")
public Feign.Builder feignBuilder() {
return Feign.builder();
}
}
断路器名称遵循此模式 <feignClientClassName>#<calledMethod>(<parameterTypes>)。当调用接口为 FooClient 的 @FeignClient,并且被调用的无参接口方法为 bar 时,断路器名称将是 FooClient#bar()。
从 2020.0.2 版本开始,断路器名称模式已从 <feignClientName>_<calledMethod> 更改。使用 2020.0.4 中引入的 CircuitBreakerNameResolver,断路器名称可以保留旧模式。 |
提供一个 CircuitBreakerNameResolver 类型的 bean,你可以更改断路器名称模式。
@Configuration
public class FooConfiguration {
@Bean
public CircuitBreakerNameResolver circuitBreakerNameResolver() {
return (String feignClientName, Target<?> target, Method method) -> feignClientName + "_" + method.getName();
}
}
要启用 Spring Cloud CircuitBreaker 组,请将 spring.cloud.openfeign.circuitbreaker.group.enabled 属性设置为 true(默认为 false)。
1.6. 通过配置属性配置 CircuitBreakers
你可以通过配置属性配置 CircuitBreakers。
例如,如果你有此 Feign 客户端
@FeignClient(url = "https://:8080")
public interface DemoClient {
@GetMapping("demo")
String getDemo();
}
你可以通过以下方式使用配置属性对其进行配置
spring:
cloud:
openfeign:
circuitbreaker:
enabled: true
alphanumeric-ids:
enabled: true
resilience4j:
circuitbreaker:
instances:
DemoClientgetDemo:
minimumNumberOfCalls: 69
timelimiter:
instances:
DemoClientgetDemo:
timeoutDuration: 10s
如果你想切回到 Spring Cloud 2022.0.0 之前使用的断路器名称,可以将 spring.cloud.openfeign.circuitbreaker.alphanumeric-ids.enabled 设置为 false。 |
1.7. Feign Spring Cloud CircuitBreaker 降级处理
Spring Cloud CircuitBreaker 支持降级的概念:当断路器打开或发生错误时执行的默认代码路径。要为给定的 @FeignClient 启用降级,请将 fallback 属性设置为实现降级的类名。你还需要将你的实现声明为一个 Spring bean。
@FeignClient(name = "test", url = "https://:${server.port}/", fallback = Fallback.class)
protected interface TestClient {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
Hello getHello();
@RequestMapping(method = RequestMethod.GET, value = "/hellonotfound")
String getException();
}
@Component
static class Fallback implements TestClient {
@Override
public Hello getHello() {
throw new NoFallbackAvailableException("Boom!", new RuntimeException());
}
@Override
public String getException() {
return "Fixed response";
}
}
如果需要访问导致降级触发的原因,可以使用 @FeignClient 中的 fallbackFactory 属性。
@FeignClient(name = "testClientWithFactory", url = "https://:${server.port}/",
fallbackFactory = TestFallbackFactory.class)
protected interface TestClientWithFactory {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
Hello getHello();
@RequestMapping(method = RequestMethod.GET, value = "/hellonotfound")
String getException();
}
@Component
static class TestFallbackFactory implements FallbackFactory<FallbackWithFactory> {
@Override
public FallbackWithFactory create(Throwable cause) {
return new FallbackWithFactory();
}
}
static class FallbackWithFactory implements TestClientWithFactory {
@Override
public Hello getHello() {
throw new NoFallbackAvailableException("Boom!", new RuntimeException());
}
@Override
public String getException() {
return "Fixed response";
}
}
1.8. Feign 与 @Primary
在使用 Feign 和 Spring Cloud CircuitBreaker 降级时,ApplicationContext 中存在多个同类型的 bean。这会导致 @Autowired 无法正常工作,因为没有正好一个 bean,或者没有一个被标记为 primary。为了解决这个问题,Spring Cloud OpenFeign 将所有 Feign 实例标记为 @Primary,这样 Spring Framework 就会知道要注入哪个 bean。在某些情况下,这可能不理想。要关闭此行为,请将 @FeignClient 的 primary 属性设置为 false。
@FeignClient(name = "hello", primary = false)
public interface HelloClient {
// methods here
}
1.9. Feign 继承支持
Feign 通过单继承接口支持样板 API。这允许将常见操作分组到方便的基础接口中。
public interface UserService {
@RequestMapping(method = RequestMethod.GET, value ="/users/{id}")
User getUser(@PathVariable("id") long id);
}
@RestController
public class UserResource implements UserService {
}
package project.user;
@FeignClient("users")
public interface UserClient extends UserService {
}
@FeignClient 接口不应在服务器和客户端之间共享,并且不再支持在类级别使用 @RequestMapping 注解 @FeignClient 接口。 |
1.10. Feign 请求/响应压缩
你可以考虑为你的 Feign 请求启用请求或响应的 GZIP 压缩。你可以通过启用以下任一属性来实现
spring.cloud.openfeign.compression.request.enabled=true
spring.cloud.openfeign.compression.response.enabled=true
Feign 请求压缩为你提供了类似于你为 Web 服务器设置的选项
spring.cloud.openfeign.compression.request.enabled=true
spring.cloud.openfeign.compression.request.mime-types=text/xml,application/xml,application/json
spring.cloud.openfeign.compression.request.min-request-size=2048
这些属性允许你选择要压缩的媒体类型和最小请求阈值长度。
由于 OkHttpClient 使用“透明”压缩,如果在场有 content-encoding 或 accept-encoding 头部,透明压缩会被禁用,因此当 classpath 中存在 feign.okhttp.OkHttpClient 并且 spring.cloud.openfeign.okhttp.enabled 设置为 true 时,我们不启用压缩。 |
1.11. Feign 日志记录
为每个创建的 Feign 客户端都会创建一个 logger。默认情况下,logger 的名称是用于创建 Feign 客户端的接口的完整类名。Feign 日志记录仅响应 DEBUG 级别。
logging.level.project.user.UserClient: DEBUG
你可以按客户端配置的 Logger.Level 对象,它告诉 Feign 记录多少日志。选项有
-
NONE,无日志记录(默认)。 -
BASIC,仅记录请求方法和 URL,以及响应状态码和执行时间。 -
HEADERS,记录基本信息以及请求和响应头部。 -
FULL,记录请求和响应的头部、body 和元数据。
例如,以下会将 Logger.Level 设置为 FULL
@Configuration
public class FooConfiguration {
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
1.12. Feign Capability 支持
Feign 的 Capability 暴露了核心 Feign 组件,以便可以修改这些组件。例如,Capability 可以获取 Client,对其进行*装饰*,并将装饰后的实例返回给 Feign。对 Micrometer 的支持就是一个很好的实际示例。请参阅Micrometer 支持。
创建一个或多个 Capability bean 并将其放入 @FeignClient 配置中,可以注册它们并修改相关客户端的行为。
@Configuration
public class FooConfiguration {
@Bean
Capability customCapability() {
return new CustomCapability();
}
}
1.13. Micrometer 支持
如果以下所有条件都为真,则会创建一个 MicrometerObservationCapability bean 并进行注册,以便你的 Feign 客户端可以通过 Micrometer 进行观察
-
classpath 中存在
feign-micrometer -
存在
ObservationRegistrybean -
feign micrometer 属性设置为
true(默认)-
spring.cloud.openfeign.micrometer.enabled=true(适用于所有客户端) -
spring.cloud.openfeign.client.config.feignName.micrometer.enabled=true(适用于单个客户端)
-
如果你的应用已经使用了 Micrometer,启用此功能就像将 feign-micrometer 添加到 classpath 一样简单。 |
你也可以通过以下任一方式禁用此功能
-
从 classpath 中排除
feign-micrometer -
将其中一个 feign micrometer 属性设置为
false-
spring.cloud.openfeign.micrometer.enabled=false -
spring.cloud.openfeign.client.config.feignName.micrometer.enabled=false
-
spring.cloud.openfeign.micrometer.enabled=false 会禁用所有 Feign 客户端的 Micrometer 支持,而不管客户端级别标志 spring.cloud.openfeign.client.config.feignName.micrometer.enabled 的值如何。如果你想按客户端启用或禁用 Micrometer 支持,请不要设置 spring.cloud.openfeign.micrometer.enabled,而使用 spring.cloud.openfeign.client.config.feignName.micrometer.enabled。 |
你也可以通过注册自己的 bean 来定制 MicrometerObservationCapability
@Configuration
public class FooConfiguration {
@Bean
public MicrometerObservationCapability micrometerObservationCapability(ObservationRegistry registry) {
return new MicrometerObservationCapability(registry);
}
}
仍然可以使用 MicrometerCapability 与 Feign(仅指标支持),你需要禁用 Micrometer 支持 (spring.cloud.openfeign.micrometer.enabled=false) 并创建一个 MicrometerCapability bean
@Configuration
public class FooConfiguration {
@Bean
public MicrometerCapability micrometerCapability(MeterRegistry meterRegistry) {
return new MicrometerCapability(meterRegistry);
}
}
1.14. Feign 缓存
如果使用了 @EnableCaching 注解,则会创建一个 CachingCapability bean 并进行注册,以便你的 Feign 客户端识别其接口上的 @Cache* 注解
public interface DemoClient {
@GetMapping("/demo/{filterParam}")
@Cacheable(cacheNames = "demo-cache", key = "#keyParam")
String demoEndpoint(String keyParam, @PathVariable String filterParam);
}
你也可以通过属性 spring.cloud.openfeign.cache.enabled=false 禁用此功能。
1.15. Feign @QueryMap 支持
Spring Cloud OpenFeign 提供了一个等效的 @SpringQueryMap 注解,用于将 POJO 或 Map 参数注解为查询参数映射。
例如,Params 类定义了参数 param1 和 param2
// Params.java
public class Params {
private String param1;
private String param2;
// [Getters and setters omitted for brevity]
}
以下 feign 客户端使用 @SpringQueryMap 注解来使用 Params 类
@FeignClient("demo")
public interface DemoTemplate {
@GetMapping(path = "/demo")
String demoEndpoint(@SpringQueryMap Params params);
}
如果你需要对生成的查询参数映射进行更多控制,可以实现自定义的 QueryMapEncoder bean。
1.16. HATEOAS 支持
Spring 提供了一些 API 来创建遵循 HATEOAS 原则的 REST 表示,包括 Spring Hateoas 和 Spring Data REST。
如果你的项目使用了 org.springframework.boot:spring-boot-starter-hateoas starter 或 org.springframework.boot:spring-boot-starter-data-rest starter,Feign 的 HATEOAS 支持默认是启用的。
启用 HATEOAS 支持后,Feign 客户端允许序列化和反序列化 HATEOAS 表示模型:EntityModel、CollectionModel 和 PagedModel。
@FeignClient("demo")
public interface DemoTemplate {
@GetMapping(path = "/stores")
CollectionModel<Store> getStores();
}
1.17. Spring @MatrixVariable 支持
Spring Cloud OpenFeign 支持 Spring 的 @MatrixVariable 注解。
如果将 map 作为方法参数传递,则 @MatrixVariable 路径段是通过将 map 中的键值对用 = 连接起来创建的。
如果传递了不同的对象,则使用 = 将 @MatrixVariable 注解中提供的 name(如果定义)或被注解的变量名与提供的方法参数连接起来。
- 重要
-
尽管在服务器端,Spring 不要求用户将路径段占位符的名称与矩阵变量名称相同,但这在客户端会过于含糊不清,因此 Spring Cloud OpenFeign 要求你添加一个路径段占位符,其名称与
@MatrixVariable注解中提供的name(如果定义)或被注解的变量名匹配。
例如
@GetMapping("/objects/links/{matrixVars}")
Map<String, List<String>> getObjects(@MatrixVariable Map<String, List<String>> matrixVars);
请注意,变量名和路径段占位符都称为 matrixVars。
@FeignClient("demo")
public interface DemoTemplate {
@GetMapping(path = "/stores")
CollectionModel<Store> getStores();
}
1.18. Feign CollectionFormat 支持
我们通过提供 @CollectionFormat 注解来支持 feign.CollectionFormat。你可以通过将所需的 feign.CollectionFormat 作为注解值传递来用它注解 Feign 客户端方法(或整个类以影响所有方法)。
在以下示例中,使用 CSV 格式而不是默认的 EXPLODED 来处理方法。
@FeignClient(name = "demo")
protected interface DemoFeignClient {
@CollectionFormat(feign.CollectionFormat.CSV)
@GetMapping(path = "/test")
ResponseEntity performRequest(String test);
}
1.19. 响应式支持
由于 OpenFeign 项目目前不支持响应式客户端,例如 Spring WebClient,Spring Cloud OpenFeign 也不支持。核心项目一旦提供支持,我们就会在此添加支持。
在此之前,我们建议使用 feign-reactive 来支持 Spring WebClient。
1.19.1. 早期初始化错误
我们不建议在应用生命周期的早期阶段,即处理配置和初始化 bean 时使用 Feign 客户端。在 bean 初始化期间使用客户端是不受支持的。
同样,根据你使用 Feign 客户端的方式,启动应用时可能会看到初始化错误。为了解决这个问题,你可以在自动装配客户端时使用 ObjectProvider。
@Autowired
ObjectProvider<TestFeignClient> testFeignClient;
1.20. Spring Data 支持
如果 classpath 中存在 Jackson Databind 和 Spring Data Commons,则会自动添加用于 org.springframework.data.domain.Page 和 org.springframework.data.domain.Sort 的转换器。
要禁用此行为,请设置
spring.cloud.openfeign.autoconfiguration.jackson.enabled=false
详见 org.springframework.cloud.openfeign.FeignAutoConfiguration.FeignJacksonConfiguration。
1.21. Spring @RefreshScope 支持
如果启用了 Feign 客户端刷新,则每个 Feign 客户端将使用以下配置创建
-
将
feign.Request.Options作为刷新范围 (refresh-scoped) 的 bean。这意味着connectTimeout和readTimeout等属性可以在任何 Feign 客户端实例上进行刷新。 -
一个包装在
org.springframework.cloud.openfeign.RefreshableUrl下的 URL。这意味着 Feign 客户端的 URL(如果使用属性spring.cloud.openfeign.client.config.{feignName}.url定义)可以在任何 Feign 客户端实例上进行刷新。
你可以通过 POST /actuator/refresh 刷新这些属性。
默认情况下,Feign 客户端的刷新行为是禁用的。使用以下属性启用刷新行为
spring.cloud.openfeign.client.refresh-enabled=true
不要使用 @RefreshScope 注解来注解 @FeignClient 接口。 |
1.22. OAuth2 支持
可以通过向项目添加 spring-boot-starter-oauth2-client 依赖并设置以下标志来启用 OAuth2 支持
spring.cloud.openfeign.oauth2.enabled=true
当标志设置为 true 且 oauth2 客户端上下文资源详细信息存在时,会创建一个 OAuth2AccessTokenInterceptor 类的 bean。在每个请求之前,拦截器会解析所需的访问令牌并将其包含在头部中。OAuth2AccessTokenInterceptor 使用 OAuth2AuthorizedClientManager 来获取持有 OAuth2AccessToken 的 OAuth2AuthorizedClient。如果用户使用 spring.cloud.openfeign.oauth2.clientRegistrationId 属性指定了 OAuth2 clientRegistrationId,则将使用它来检索令牌。如果未检索到令牌或未指定 clientRegistrationId,则将使用从 url 主机段检索到的 serviceId。
- 提示
-
将
serviceId用作 OAuth2 client registrationId 对于负载均衡的 Feign 客户端很方便。对于非负载均衡的客户端,基于属性的clientRegistrationId是一个合适的方法。 - 提示
-
如果你不想使用
OAuth2AuthorizedClientManager的默认设置,你可以在配置中实例化一个此类型的 bean。
1.23. 转换负载均衡的 HTTP 请求
你可以使用选定的 ServiceInstance 来转换负载均衡的 HTTP 请求。
对于 Request,你需要实现并定义 LoadBalancerFeignRequestTransformer,如下所示
@Bean
public LoadBalancerFeignRequestTransformer transformer() {
return new LoadBalancerFeignRequestTransformer() {
@Override
public Request transformRequest(Request request, ServiceInstance instance) {
Map<String, Collection<String>> headers = new HashMap<>(request.headers());
headers.put("X-ServiceId", Collections.singletonList(instance.getServiceId()));
headers.put("X-InstanceId", Collections.singletonList(instance.getInstanceId()));
return Request.create(request.httpMethod(), request.url(), headers, request.body(), request.charset(),
request.requestTemplate());
}
};
}
如果定义了多个 transformer,它们将按照 bean 定义的顺序应用。或者,你可以使用 LoadBalancerFeignRequestTransformer.DEFAULT_ORDER 来指定顺序。
1.24. X-Forwarded Headers 支持
通过设置以下标志可以启用 X-Forwarded-Host 和 X-Forwarded-Proto 支持
spring.cloud.loadbalancer.x-forwarded.enabled=true
1.25. 支持的为 Feign 客户端提供 URL 的方式
你可以通过以下任一方式为 Feign 客户端提供 URL
| 情况 | 示例 | 详情 |
|---|---|---|
在 |
|
URL 从注解的 |
在 |
|
URL 从注解的 |
未在 |
|
URL 从配置属性解析,不进行负载均衡。如果 |
URL 既未在 |
|
URL 从注解的 |
1.26. AOT 和 Native Image 支持
Spring Cloud OpenFeign 支持 Spring AOT 转换和 Native Image,但仅在禁用刷新模式、禁用 Feign 客户端刷新(默认设置)和禁用延迟 @FeignClient 属性解析(默认设置)时支持。
如果你想在 AOT 或 Native Image 模式下运行 Spring Cloud OpenFeign 客户端,请确保将 spring.cloud.refresh.enabled 设置为 false。 |
如果你想在 AOT 或 Native Image 模式下运行 Spring Cloud OpenFeign 客户端,请确保 spring.cloud.openfeign.client.refresh-enabled 未设置为 true。 |
如果你想在 AOT 或 Native Image 模式下运行 Spring Cloud OpenFeign 客户端,请确保 spring.cloud.openfeign.lazy-attributes-resolution 未设置为 true。 |
但是,如果你通过属性设置了 url 值,则可以通过使用 -Dspring.cloud.openfeign.client.config.[clientId].url=[url] 标志运行镜像来覆盖 @FeignClient 的 url 值。为了启用覆盖,在构建时也必须通过属性而不是 @FeignClient 属性来设置 url 值。 |