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
)
-
Decoder
feignDecoder:ResponseEntityDecoder
(封装了SpringDecoder
) -
Encoder
feignEncoder:SpringEncoder
-
Logger
feignLogger:Slf4jLogger
-
MicrometerObservationCapability
micrometerObservationCapability: 如果 classpath 中存在feign-micrometer
并且ObservationRegistry
可用 -
MicrometerCapability
micrometerCapability: 如果 classpath 中存在feign-micrometer
,MeterRegistry
可用且ObservationRegistry
不可用 -
CachingCapability
cachingCapability: 如果使用了@EnableCaching
注解。可以通过spring.cloud.openfeign.cache.enabled
禁用。 -
Contract
feignContract:SpringMvcContract
-
Feign.Builder
feignBuilder:FeignCircuitBreaker.Builder
-
Client
feignClient: 如果 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 = "http://localhost: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 = "http://localhost:${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 = "http://localhost:${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
-
存在
ObservationRegistry
bean -
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 值。 |