# Spring Cloud OpenFeign

**3.1.1**

该项目通过自动配置和绑定到 Spring 环境和其他 Spring 编程模型习惯用法，为 Spring 引导应用程序提供 OpenFeign 集成。

## 1.声明式 REST 客户端：假装

[Feign](https://github.com/OpenFeign/feign)是一个声明性 Web 服务客户机。它使编写 Web 服务客户机变得更加容易。要使用 feign 创建一个界面并对其进行注释。它具有可插入的注释支持，包括伪装注释和 JAX-RS 注释。Feign 还支持可插拔的编码器和解码器。 Spring Cloud 增加了对 Spring MVC 注释的支持，并支持使用与 Spring Web 中默认使用的相同的`HttpMessageConverters`。 Spring Cloud集成了 Eureka、 Spring Cloud电路断路器，以及 Spring Cloud负载平衡器，以在使用 Feign 时提供负载平衡的 HTTP 客户端。

### 1.1.如何包含佯装

要在项目中包含假动作，请使用组合`org.springframework.cloud`和工件 ID`spring-cloud-starter-openfeign`的启动器。请参阅[Spring Cloud Project page](https://projects.spring.io/spring-cloud/)，以获取有关使用当前 Spring Cloud发布系列设置构建系统的详细信息。

示例 Spring 启动应用程序

```
@SpringBootApplication
@EnableFeignClients
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}
```

Storeclient.java

```
@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`注释中，字符串值（上面的“存储”）是一个任意的客户端名称，用于创建[Spring Cloud LoadBalancer client](https://github.com/spring-cloud/spring-cloud-commons/blob/main/spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/blocking/client/BlockingLoadBalancerClient.java)。你还可以使用`url`属性（绝对值或只是一个主机名）指定一个 URL。应用程序上下文中 Bean 的名称是接口的完全限定名称。要指定你自己的别名值，你可以使用`qualifiers`注释的`@FeignClient`值。

上面的负载平衡器客户机将希望发现“存储”服务的物理地址。如果你的应用程序是 Eureka 客户机，那么它将解析 Eureka 服务注册中心中的服务。如果不想使用 Eureka，可以使用[`SimpleDiscoveryClient`]（https://DOCS. Spring.io/ Spring-cloud-commons/DOCS/current/reference/html/#SimpleDiscoveryclient）在外部配置中配置一个服务器列表。

Spring Cloud OpenFeign 支持用于 Spring Cloud LoadBalancer 的阻塞模式的所有可用功能。你可以在[项目文件](https://docs.spring.io/spring-cloud-commons/docs/current/reference/html/#spring-cloud-loadbalancer)中阅读有关它们的更多信息。

|   |要在`@Configuration`-annotated-classes 上使用`@EnableFeignClients`注释，请确保指定客户机的位置，例如：`@EnableFeignClients(basePackages = "com.example.clients")`或显式列出它们：`@EnableFeignClients(clients = InventoryServiceFeignClient.class)`|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

### 1.2.覆盖假装默认值

Spring Cloud 的假装支持中的一个核心概念是命名客户端。每个假客户机都是组件集合的一部分，这些组件组合在一起工作，以便按需联系远程服务器，并且该集合有一个名称，你可以使用`@FeignClient`注释将其命名为应用程序开发人员。 Spring Cloud使用`FeignClientsConfiguration`按需为每个命名客户端创建一个新的集成，作为`ApplicationContext`。其中包括`feign.Decoder`、`feign.Encoder`和`feign.Contract`。通过使用`@FeignClient`注释的`contextId`属性，可以覆盖该集合的名称。

Spring Cloud 允许你通过使用`@FeignClient`声明额外的配置（在`FeignClientsConfiguration`之上）来完全控制假客户端。示例：

```
@FeignClient(name = "stores", configuration = FooConfiguration.class)
public interface StoreClient {
    //..
}
```

在这种情况下，客户机是由已经在`FeignClientsConfiguration`中的组件以及`FooConfiguration`中的任何组件组成的（后者将覆盖前者）。

|   |`FooConfiguration`不需要用`@Configuration`进行注释。但是，如果是，那么请注意将它从任何`@ComponentScan`中排除，否则将包括此配置，因为当指定时，它将成为`feign.Decoder`、`feign.Encoder`、`feign.Contract`等的默认源。这可以通过将其放在一个单独的、不重叠的包中来避免，而不是使用`@ComponentScan`或`@SpringBootApplication`，或者可以在`@ComponentScan`中显式地排除它。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

|   |使用`contextId``@FeignClient`注释的`contextId`属性，除了更改<br/>的名称外，还将覆盖客户机名`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`伪译码：`ResponseEntityDecoder`（它包含一个`SpringDecoder`）

* `Encoder`feignencoder:`SpringEncoder`

* `Logger`伪装者：`Slf4jLogger`

* `MicrometerCapability`MicrometerCapability:如果`feign-micrometer`在 Classpath 上并且`MeterRegistry`是可用的

* `CachingCapability`缓存能力：如果使用`@EnableCaching`注释。可以通过`feign.cache.enabled`禁用。

* `Contract`假装合同：`SpringMvcContract`

* `Feign.Builder`FeignBuilder:`FeignCircuitBreaker.Builder`

* `Client`FeignClient:如果 Spring Cloud LoadBalancer 在 Classpath 上，则使用`FeignBlockingLoadBalancerClient`。如果它们都不在 Classpath 上，则使用默认的假装客户端。

|   |`spring-cloud-starter-openfeign`支持`spring-cloud-starter-loadbalancer`。然而，作为一个可选的依赖项，如果你想使用它，你需要确保它已被添加到你的项目中。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

通过将`feign.okhttp.enabled`或`feign.httpclient.enabled`或`feign.httpclient.hc5.enabled`分别设置为`true`，并将它们设置在 Classpath 上，可以使用 OkHtttpClient 和 ApacheHttpClient 和 ApachehtpClient5 假客户端。你可以通过在使用 Apache 时提供`org.apache.http.impl.client.CloseableHttpClient`的 Bean 或在使用 OK HTTP 时提供`okhttp3.OkHttpClient`或在使用 Apache HC5 时提供`org.apache.hc.client5.http.impl.classic.CloseableHttpClient`的 Bean 来定制所使用的 HTTP 客户端。

Spring Cloud OpenFeign*不是*默认情况下提供以下 bean 用于伪装，但仍然从应用程序上下文中查找这些类型的 bean 以创建伪装客户端：

* `Logger.Level`

* `Retryer`

* `ErrorDecoder`

* `Request.Options`

* `Collection<RequestInterceptor>`

* `SetterFactory`

* `QueryMapEncoder`

* `Capability`（默认情况下提供`MicrometerCapability`和`CachingCapability`）

默认情况下创建了类型`Retryer`的`Retryer.NEVER_RETRY`的 Bean，这将禁用重试。请注意，这种重试行为与假装默认的行为不同，在这种情况下，它会自动重试 ioExceptions，将它们视为与网络相关的瞬态异常，以及从错误解码器中抛出的任何 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`还可以使用配置属性进行配置。

应用程序.yml

```
feign:
    client:
        config:
            feignName:
                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
                decode404: false
                encoder: com.example.SimpleEncoder
                decoder: com.example.SimpleDecoder
                contract: com.example.SimpleContract
                capabilities:
                    - com.example.FooCapability
                    - com.example.BarCapability
                queryMapEncoder: com.example.SimpleQueryMapEncoder
                metrics.enabled: false
```

可以在`@EnableFeignClients`属性`defaultConfiguration`中以与上述类似的方式指定默认配置。不同之处在于，此配置将应用于*全部*假客户机。

如果你更喜欢使用配置属性来配置所有`@FeignClient`，那么你可以使用`default`假名创建配置属性。

你可以使用`feign.client.config.feignName.defaultQueryParameters`和`feign.client.config.feignName.defaultRequestHeaders`来指定查询参数和标题，这些参数和标题将与名为`feignName`的客户机的每个请求一起发送。

应用程序.yml

```
feign:
    client:
        config:
            default:
                connectTimeout: 5000
                readTimeout: 5000
                loggerLevel: basic
```

如果我们同时创建`@Configuration` Bean 和配置属性，配置属性将会胜出。它将覆盖`@Configuration`值。但如果要将优先级更改为`@Configuration`，则可以将`feign.client.default-to-properties`更改为`false`。

如果我们想要创建多个具有相同名称或 URL 的假客户机，那么它们将指向相同的服务器，但每个服务器具有不同的自定义配置，那么我们必须使用`contextId`属性的`@FeignClient`，以避免这些配置 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.client.decodeSlash`的值设置为`false`来更改此行为。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------|

#### 1.2.1.`SpringEncoder`配置

在我们提供的`SpringEncoder`中，我们为二进制内容类型设置`null`字符集，为所有其他类型设置`UTF-8`字符集。

通过将`feign.encoder.charset-from-content-type`的值设置为`true`，可以修改此行为以从`Content-Type`头字符集派生字符集。

### 1.3.超时处理

我们可以在默认值和指定的客户机上配置超时。OpenFeign 使用两个超时参数：

* `connectTimeout`可以防止由于服务器处理时间过长而阻塞调用方。

* `readTimeout`从建立连接的时间开始应用，并在返回响应花费太长时间时触发。

|   |在服务器不运行或可用的情况下，数据包将导致*连接被拒绝*。通信以错误消息或回退结束。如果设置得很低，*在此之前*`connectTimeout`就会发生这种情况。执行查找和接收这样的数据包所需的时间造成了该延迟的很大一部分。它可能会根据涉及 DNS 查找的远程主机进行更改。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

### 1.4.手动创建假客户端

在某些情况下，可能有必要以一种不可能使用上述方法的方式定制你的假装客户机。在这种情况下，你可以使用[Feign Builder API](https://github.com/OpenFeign/feign/#basics)创建客户机。下面是一个示例，该示例使用相同的接口创建两个假客户机，但将每个客户机配置为单独的请求拦截器。

```
@Import(FeignClientsConfiguration.class)
class FooController {

    private FooClient fooClient;

    private FooClient adminClient;

    @Autowired
    public FooController(Client client, Encoder encoder, Decoder decoder, Contract contract, MicrometerCapability micrometerCapability) {
        this.fooClient = Feign.builder().client(client)
                .encoder(encoder)
                .decoder(decoder)
                .contract(contract)
                .addCapability(micrometerCapability)
                .requestInterceptor(new BasicAuthRequestInterceptor("user", "user"))
                .target(FooClient.class, "https://PROD-SVC");

        this.adminClient = Feign.builder().client(client)
                .encoder(encoder)
                .decoder(decoder)
                .contract(contract)
                .addCapability(micrometerCapability)
                .requestInterceptor(new BasicAuthRequestInterceptor("admin", "admin"))
                .target(FooClient.class, "https://PROD-SVC");
    }
}
```

|   |在上面的示例中`FeignClientsConfiguration.class`是由 Spring Cloud OpenFeign 提供的默认配置<br/>。|
|---|---------------------------------------------------------------------------------------------------------------------------|

|   |`PROD-SVC`是客户机将向其发出请求的服务的名称。|
|---|-----------------------------------------------------------------------------|

|   |feign`Contract`对象定义了哪些注释和值在接口上是有效的。<br/>Autowired`Contract` Bean 提供了对 SpringMVC 注释的支持，而不是<br/>默认的伪装原生注释。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

你还可以使用`Builder`来配置 FeignClient，使其不从父上下文继承 bean。你可以通过在`Builder`上重写调用`inheritParentContext(false)`来实现此目的。

### 1.5. Feign Spring Cloud CircuitBreaker Support

如果 Spring Cloud断路器在 Classpath 和`feign.circuitbreaker.enabled=true`上，Feign 将用一个断路器包装所有方法。

要在每个客户端的基础上禁用 Spring Cloud电路断路器支持，请使用“原型”范围创建一个普通的`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>`变为<br/>使用 2020.0.4 引入的`CircuitBreakerNameResolver`，断路器名称可以保留旧模式。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

提供 Bean 的`CircuitBreakerNameResolver`，可以更改断路器名称模式。

```
@Configuration
public class FooConfiguration {
    @Bean
    public CircuitBreakerNameResolver circuitBreakerNameResolver() {
        return (String feignClientName, Target<?> target, Method method) -> feignClientName + "_" + method.getName();
    }
}
```

要启用 Spring Cloud电路断路器组，请将`feign.circuitbreaker.group.enabled`属性设置为`true`（默认情况下`false`）。

### 1.6. Feign Spring Cloud CircuitBreaker Fallbacks

Spring Cloud电路断路器支持回退的概念：当电路打开或存在错误时执行的默认代码路径。要为给定的`@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.7.假装和`@Primary`

当使用与 Spring Cloud电路断路器后备假动作时，在`ApplicationContext`中有多个相同类型的 bean。这将导致`@Autowired`不工作，因为没有一个 Bean，或一个标记为主要的。为了解决这个问题， Spring Cloud OpenFeign 将所有的 Feign 实例标记为`@Primary`，因此 Spring Framework 将知道要注入哪个 Bean。在某些情况下，这可能是不可取的。要关闭此行为，请将`@FeignClient`的`primary`属性设置为 false。

```
@FeignClient(name = "hello", primary = false)
public interface HelloClient {
    // methods here
}
```

### 1.8.假装继承支持

Feign 通过单继承接口支持样板 API。这允许将公共操作分组到方便的基本接口中。

userservice.java

```
public interface UserService {

    @RequestMapping(method = RequestMethod.GET, value ="/users/{id}")
    User getUser(@PathVariable("id") long id);
}
```

userresource.java

```
@RestController
public class UserResource implements UserService {

}
```

userclient.java

```
package project.user;

@FeignClient("users")
public interface UserClient extends UserService {

}
```

|   |`@FeignClient`接口不应在服务器和客户机之间共享，并且不再支持在类级别上注释`@FeignClient`带有`@RequestMapping`的接口。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

### 1.9.假装请求/响应压缩

你可以考虑为你的假请求启用请求或响应 gzip 压缩。你可以通过启用其中一个属性来实现这一点：

```
feign.compression.request.enabled=true
feign.compression.response.enabled=true
```

Feign Request Compression 为你提供与你为 Web 服务器设置的设置类似的设置：

```
feign.compression.request.enabled=true
feign.compression.request.mime-types=text/xml,application/xml,application/json
feign.compression.request.min-request-size=2048
```

这些属性允许你对压缩媒体类型和最小请求阈值长度进行选择。

### 1.10.伪测井

日志记录器是为每个创建的假客户端创建的。默认情况下，日志记录器的名称是用于创建假客户端的接口的完整类名。假装日志记录只响应`DEBUG`级别。

应用程序.yml

```
logging.level.project.user.UserClient: DEBUG
```

你可以为每个客户机配置的`Logger.Level`对象告诉你要记录多少日志。选择如下：

* `NONE`，没有日志记录（** 默认 **）。

* `BASIC`，只记录请求方法和 URL 以及响应状态代码和执行时间。

* `HEADERS`，记录基本信息以及请求和响应头。

* `FULL`，记录请求和响应的标题、主体和元数据。

例如，下面将`Logger.Level`设置为`FULL`:

```
@Configuration
public class FooConfiguration {
    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }
}
```

### 1.11.佯装能力支持

伪装功能公开了核心伪装组件，以便可以修改这些组件。例如，这些功能可以使用`Client`、*装饰*，并将修饰过的实例返回给 feign。对 Metrics 库的支持就是一个很好的实例。见[假装度量](#feign-metrics)。

创建一个或多个`Capability`bean 并将其放置在`@FeignClient`配置中，可以注册它们并修改所涉及的客户机的行为。

```
@Configuration
public class FooConfiguration {
    @Bean
    Capability customCapability() {
        return new CustomCapability();
    }
}
```

### 1.12.假装度量

如果以下所有条件都是真的，则创建并注册一个`MicrometerCapability` Bean，以便你的假客户机将度量数据发布到 Micrometer:

* `feign-micrometer`在 Classpath 上

* a`MeterRegistry` Bean 可用

* 假装度量属性设置为`true`（默认情况下）

  * `feign.metrics.enabled=true`（适用于所有客户）

  * `feign.client.config.feignName.metrics.enabled=true`（对于单个客户端）

|   |如果你的应用程序已经使用了 Micrometer，那么启用度量就像将`feign-micrometer`放在 Classpath 上一样简单。|
|---|-----------------------------------------------------------------------------------------------------------------------------|

你还可以通过以下方式禁用该功能：

* 从你的 Classpath 中排除`feign-micrometer`

* 将一个假的度量属性设置为`false`

  * `feign.metrics.enabled=false`

  * `feign.client.config.feignName.metrics.enabled=false`

|   |`feign.metrics.enabled=false`禁用对**全部**冒充客户端的度量支持，而不管客户端级别标志的值是多少：`feign.client.config.feignName.metrics.enabled`。<br/>如果你想在每个客户端启用或禁用 Merics，请不要设置，而使用`feign.client.config.feignName.metrics.enabled`。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

你还可以通过注册自己的 Bean 来自定义`MicrometerCapability`:

```
@Configuration
public class FooConfiguration {
    @Bean
    public MicrometerCapability micrometerCapability(MeterRegistry meterRegistry) {
        return new MicrometerCapability(meterRegistry);
    }
}
```

### 1.13.假装缓存

如果使用`@EnableCaching`注释，则创建并注册一个`CachingCapability` Bean，以便你的假客户端在其接口上识别`@Cache*`注释：

```
public interface DemoClient {

    @GetMapping("/demo/{filterParam}")
    @Cacheable(cacheNames = "demo-cache", key = "#keyParam")
    String demoEndpoint(String keyParam, @PathVariable String filterParam);
}
```

你还可以通过属性`feign.cache.enabled=false`禁用该功能。

### 1.14.假装 @QueryMap 支持

OpenFeign`@QueryMap`注释支持将 POJO 用作 GET 参数映射。遗憾的是，缺省的 OpenFeign QueryMap 注释与 Spring 不兼容，因为它缺少`value`属性。

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]
}
```

下面的假客户机通过使用`@SpringQueryMap`注释来使用`Params`类：

```
@FeignClient("demo")
public interface DemoTemplate {

    @GetMapping(path = "/demo")
    String demoEndpoint(@SpringQueryMap Params params);
}
```

如果需要对生成的查询参数映射进行更多控制，则可以实现自定义的`QueryMapEncoder` Bean。

### 1.15.Hateoas 支持

Spring 提供了一些 API 来创建遵循[HATEOAS](https://en.wikipedia.org/wiki/HATEOAS)原则、[Spring Hateoas](https://spring.io/projects/spring-hateoas)和[Spring Data REST](https://spring.io/projects/spring-data-rest)的 REST 表示。

如果你的项目使用`org.springframework.boot:spring-boot-starter-hateoas`starter 或`org.springframework.boot:spring-boot-starter-data-rest`starter，则默认情况下会启用 feignhateoas 支持。

当启用 Hateoas 支持时，允许 Feign 客户端序列化和反序列化 Hateoas 表示模型：[实体模型](https://docs.spring.io/spring-hateoas/docs/1.0.0.M1/apidocs/org/springframework/hateoas/EntityModel.html)，[集合模型](https://docs.spring.io/spring-hateoas/docs/1.0.0.M1/apidocs/org/springframework/hateoas/CollectionModel.html)和[PagedModel](https://docs.spring.io/spring-hateoas/docs/1.0.0.M1/apidocs/org/springframework/hateoas/PagedModel.html)。

```
@FeignClient("demo")
public interface DemoTemplate {

    @GetMapping(path = "/stores")
    CollectionModel<Store> getStores();
}
```

### 1.16. Spring @MatrixVariable Support

Spring Cloud OpenFeign 提供了对 Spring `@MatrixVariable`注释的支持。

如果将映射作为方法参数传递，则通过将映射中的键值对与`=`连接起来来创建`@MatrixVariable`路径段。

如果传递了一个不同的对象，那么`@MatrixVariable`注释（如果已定义）中提供的`name`或注释的变量名将使用`=`与提供的方法参数连接。

IMPORTANT

尽管如此，在服务器端， Spring 并不要求用户将路径段占位符的名称与矩阵变量的名称相同，因为在客户端，该名称将过于模棱两可， Spring Cloud OpenFeign 要求你添加一个路径段占位符，其名称与`name`注释（如果已定义）中提供的`@MatrixVariable`或注释的变量名称相匹配。

例如：

```
@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.17.feign`CollectionFormat`support

我们通过提供`@CollectionFormat`注释来支持`feign.CollectionFormat`。你可以通过传递所需的`feign.CollectionFormat`作为注释值，用它来注释一个假客户机方法（或整个类以影响所有方法）。

在下面的示例中，使用`CSV`格式而不是默认的`EXPLODED`来处理该方法。

```
@FeignClient(name = "demo")
protected interface PageableFeignClient {

    @CollectionFormat(feign.CollectionFormat.CSV)
    @GetMapping(path = "/page")
    ResponseEntity performRequest(Pageable page);

}
```

|   |在发送`Pageable`作为查询参数时设置`CSV`格式，以便对其进行正确编码。|
|---|-----------------------------------------------------------------------------------------------------------|

### 1.18.反应性支持

由于[OpenFeign 项目](https://github.com/OpenFeign/feign)目前不支持反应式客户端，例如[Spring WebClient](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/reactive/function/client/WebClient.html)， Spring Cloud OpenFeign 也不支持。我们将在此添加对它的支持，只要它在核心项目中可用。

在此之前，我们建议使用[假装反应](https://github.com/Playtika/feign-reactive)作为 Spring WebClient 支持。

#### 1.18.1.早期初始化错误

在启动应用程序时，你可能会看到初始化错误，这取决于你如何使用你的假客户机。要解决这个问题，你可以在自动连接客户机时使用`ObjectProvider`。

```
@Autowired
ObjectProvider<TestFeignClient> testFeignClient;
```

### 1.19. Spring Data Support

可以考虑启用用于支持`org.springframework.data.domain.Page`和`org.springframework.data.domain.Sort`解码的 Jackson 模块。

```
feign.autoconfiguration.jackson.enabled=true
```

### 1.20. Spring `@RefreshScope`支持

如果启用了假客户机刷新，那么每个假客户机都是以`feign.Request.Options`作为刷新范围来创建的 Bean。这意味着诸如`connectTimeout`和`readTimeout`之类的属性可以通过`POST /actuator/refresh`针对任何伪装客户端实例进行刷新。

默认情况下，feign 客户端中的刷新行为是禁用的。使用以下属性启用刷新行为：

```
feign.client.refresh-enabled=true
```

|   |不要使用`@RefreshScope`注释对`@FeignClient`接口进行注释。|
|---|---------------------------------------------------------------------------------|

### 1.21.OAuth2 支持

可以通过设置以下标志来启用 OAuth2 支持：

```
feign.oauth2.enabled=true
```

当标志设置为 true，并且出现了 OAuth2 客户机上下文资源详细信息时，将创建 Bean 类`OAuth2FeignRequestInterceptor`。在每个请求之前，拦截器解析所需的访问令牌，并将其作为报头。有时，当为假装客户机启用负载平衡时，你可能也希望使用负载平衡来获取访问令牌。为此，你应该确保负载平衡器位于 Classpath（ Spring-cloud-starter-loadbalancer）上，并通过设置以下标志显式地启用 OAuth2FeignRequestInterceptor 的负载平衡：

```
feign.oauth2.load-balanced=true
```

## 2.配置属性

要查看所有 Spring Cloud OpenFeign 相关配置属性的列表，请检查[附录页](appendix.html)。