spring-cloud-openfeign.md 34.3 KB
Newer Older
茶陵後's avatar
茶陵後 已提交
1 2 3
# Spring Cloud OpenFeign

**3.1.1**
茶陵後's avatar
茶陵後 已提交
4 5 6

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

茶陵後's avatar
茶陵後 已提交
7
## [](#spring-cloud-feign)[1.声明式 REST 客户端:假装](#spring-cloud-feign)
茶陵後's avatar
茶陵後 已提交
8

茶陵後's avatar
茶陵後 已提交
9
[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 客户端。
茶陵後's avatar
茶陵後 已提交
10

茶陵後's avatar
茶陵後 已提交
11
### [](#netflix-feign-starter)[1.1.如何包含佯装](#netflix-feign-starter)
茶陵後's avatar
茶陵後 已提交
12

茶陵後's avatar
茶陵後 已提交
13
要在项目中包含假动作,请使用组合`org.springframework.cloud`和工件 ID`spring-cloud-starter-openfeign`的启动器。请参阅[Spring Cloud Project page](https://projects.spring.io/spring-cloud/),以获取有关使用当前 Spring Cloud发布系列设置构建系统的详细信息。
茶陵後's avatar
茶陵後 已提交
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49

示例 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`值。

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

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

茶陵後's avatar
茶陵後 已提交
54
|   |要在`@Configuration`-annotated-classes 上使用`@EnableFeignClients`注释,请确保指定客户机的位置,例如:`@EnableFeignClients(basePackages = "com.example.clients")`或显式列出它们:`@EnableFeignClients(clients = InventoryServiceFeignClient.class)`|
茶陵後's avatar
茶陵後 已提交
55 56
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

茶陵後's avatar
茶陵後 已提交
57
### [](#spring-cloud-feign-overriding-defaults)[1.2.覆盖假装默认值](#spring-cloud-feign-overriding-defaults)
茶陵後's avatar
茶陵後 已提交
58

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

茶陵後's avatar
茶陵後 已提交
61
Spring Cloud 允许你通过使用`@FeignClient`声明额外的配置(在`FeignClientsConfiguration`之上)来完全控制假客户端。示例:
茶陵後's avatar
茶陵後 已提交
62 63 64 65 66 67 68 69 70 71 72 73 74

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

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

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

茶陵後's avatar
茶陵後 已提交
75
|   |使用`contextId``@FeignClient`注释的`contextId`属性,除了更改<br/>的名称外,还将覆盖客户机名`ApplicationContext`的别名,并将其用作为该客户机创建的配置 Bean 名称的一部分。|
茶陵後's avatar
茶陵後 已提交
76 77 78 79 80 81 82 83 84 85 86 87 88 89
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

|   |以前,使用`url`属性时,不需要`name`属性。现在需要使用`name`。|
|---|----------------------------------------------------------------------------------------------------------|

`name``url`属性中支持占位符。

```
@FeignClient(name = "${feign.name}", url = "${feign.url}")
public interface StoreClient {
    //..
}
```

茶陵後's avatar
茶陵後 已提交
90
Spring Cloud OpenFeign 默认情况下为 Feign 提供以下 bean(`BeanType`beanname:`ClassName`):
茶陵後's avatar
茶陵後 已提交
91

茶陵後's avatar
茶陵後 已提交
92
* `Decoder`伪译码:`ResponseEntityDecoder`(它包含一个`SpringDecoder`
茶陵後's avatar
茶陵後 已提交
93

茶陵後's avatar
茶陵後 已提交
94
* `Encoder`feignencoder:`SpringEncoder`
茶陵後's avatar
茶陵後 已提交
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110

* `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`。然而,作为一个可选的依赖项,如果你想使用它,你需要确保它已被添加到你的项目中。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

茶陵後's avatar
茶陵後 已提交
111
通过将`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 客户端。
茶陵後's avatar
茶陵後 已提交
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128

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

* `Logger.Level`

* `Retryer`

* `ErrorDecoder`

* `Request.Options`

* `Collection<RequestInterceptor>`

* `SetterFactory`

* `QueryMapEncoder`

茶陵後's avatar
茶陵後 已提交
129
* `Capability`(默认情况下提供`MicrometerCapability``CachingCapability`
茶陵後's avatar
茶陵後 已提交
130

茶陵後's avatar
茶陵後 已提交
131
默认情况下创建了类型`Retryer``Retryer.NEVER_RETRY`的 Bean,这将禁用重试。请注意,这种重试行为与假装默认的行为不同,在这种情况下,它会自动重试 ioExceptions,将它们视为与网络相关的瞬态异常,以及从错误解码器中抛出的任何 RetryableException。
茶陵後's avatar
茶陵後 已提交
132

茶陵後's avatar
茶陵後 已提交
133
创建其中一种类型的 Bean 并将其放置在`@FeignClient`配置中(例如上面的`FooConfiguration`),允许你覆盖所描述的每个 bean。示例:
茶陵後's avatar
茶陵後 已提交
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183

```
@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
```

茶陵後's avatar
茶陵後 已提交
184
可以在`@EnableFeignClients`属性`defaultConfiguration`中以与上述类似的方式指定默认配置。不同之处在于,此配置将应用于*全部*假客户机。
茶陵後's avatar
茶陵後 已提交
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242

如果你更喜欢使用配置属性来配置所有`@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`来更改此行为。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------|

茶陵後's avatar
茶陵後 已提交
243
#### [](#springencoder-configuration)[1.2.1。`SpringEncoder`配置](#SpringEncoder-配置)
茶陵後's avatar
茶陵後 已提交
244 245 246 247 248

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

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

茶陵後's avatar
茶陵後 已提交
249
### [](#timeout-handling)[1.3.超时处理](#timeout-handling)
茶陵後's avatar
茶陵後 已提交
250 251 252 253 254 255 256

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

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

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

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

茶陵後's avatar
茶陵後 已提交
260
### [](#creating-feign-clients-manually)[1.4.手动创建假客户端](#creating-feign-clients-manually)
茶陵後's avatar
茶陵後 已提交
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298

在某些情况下,可能有必要以一种不可能使用上述方法的方式定制你的假装客户机。在这种情况下,你可以使用[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`是客户机将向其发出请求的服务的名称。|
|---|-----------------------------------------------------------------------------|

茶陵後's avatar
茶陵後 已提交
299
|   |feign`Contract`对象定义了哪些注释和值在接口上是有效的。<br/>Autowired`Contract` Bean 提供了对 SpringMVC 注释的支持,而不是<br/>默认的伪装原生注释。|
茶陵後's avatar
茶陵後 已提交
300 301 302 303
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

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

茶陵後's avatar
茶陵後 已提交
304
### [](#spring-cloud-feign-circuitbreaker)[1.5. Feign Spring Cloud CircuitBreaker Support](#spring-cloud-feign-circuitbreaker)
茶陵後's avatar
茶陵後 已提交
305

茶陵後's avatar
茶陵後 已提交
306
如果 Spring Cloud断路器在 Classpath 和`feign.circuitbreaker.enabled=true`上,Feign 将用一个断路器包装所有方法。
茶陵後's avatar
茶陵後 已提交
307

茶陵後's avatar
茶陵後 已提交
308
要在每个客户端的基础上禁用 Spring Cloud电路断路器支持,请使用“原型”范围创建一个普通的`Feign.Builder`,例如:
茶陵後's avatar
茶陵後 已提交
309 310 311 312 313 314 315 316 317 318 319 320

```
@Configuration
public class FooConfiguration {
    @Bean
    @Scope("prototype")
    public Feign.Builder feignBuilder() {
        return Feign.builder();
    }
}
```

茶陵後's avatar
茶陵後 已提交
321
断路器名称遵循此模式`<feignClientClassName>#<calledMethod>(<parameterTypes>)`。当调用带有`FooClient`接口的`@FeignClient`时,调用的接口方法没有参数是`bar`,那么断路器的名称将是`FooClient#bar()`
茶陵後's avatar
茶陵後 已提交
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337

|   |截至 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();
    }
}
```

茶陵後's avatar
茶陵後 已提交
338
要启用 Spring Cloud电路断路器组,请将`feign.circuitbreaker.group.enabled`属性设置为`true`(默认情况下`false`)。
茶陵後's avatar
茶陵後 已提交
339

茶陵後's avatar
茶陵後 已提交
340
### [](#spring-cloud-feign-circuitbreaker-fallback)[1.6. Feign Spring Cloud CircuitBreaker Fallbacks](#spring-cloud-feign-circuitbreaker-fallback)
茶陵後's avatar
茶陵後 已提交
341

茶陵後's avatar
茶陵後 已提交
342
Spring Cloud电路断路器支持回退的概念:当电路打开或存在错误时执行的默认代码路径。要为给定的`@FeignClient`启用回退,请将`fallback`属性设置为实现回退的类名。你还需要将你的实现声明为 Spring  Bean。
茶陵後's avatar
茶陵後 已提交
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411

```
@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";
        }

    }
```

茶陵後's avatar
茶陵後 已提交
412
### [](#feign-and-primary)[1.7。假装和`@Primary`](# 假装和主要)
茶陵後's avatar
茶陵後 已提交
413

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

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

茶陵後's avatar
茶陵後 已提交
423
### [](#spring-cloud-feign-inheritance)[1.8.假装继承支持](#spring-cloud-feign-inheritance)
茶陵後's avatar
茶陵後 已提交
424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456

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 {

}
```

茶陵後's avatar
茶陵後 已提交
457
|   |`@FeignClient`接口不应在服务器和客户机之间共享,并且不再支持在类级别上注释`@FeignClient`带有`@RequestMapping`的接口。|
茶陵後's avatar
茶陵後 已提交
458 459
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

茶陵後's avatar
茶陵後 已提交
460
### [](#feign-requestresponse-compression)[1.9.假装请求/响应压缩](#feign-requestresponse-compression)
茶陵後's avatar
茶陵後 已提交
461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478

你可以考虑为你的假请求启用请求或响应 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
```

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

茶陵後's avatar
茶陵後 已提交
479
### [](#feign-logging)[1.10.伪测井](#feign-logging)
茶陵後's avatar
茶陵後 已提交
480

茶陵後's avatar
茶陵後 已提交
481
日志记录器是为每个创建的假客户端创建的。默认情况下,日志记录器的名称是用于创建假客户端的接口的完整类名。假装日志记录只响应`DEBUG`级别。
茶陵後's avatar
茶陵後 已提交
482 483 484 485 486 487 488

应用程序.yml

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

茶陵後's avatar
茶陵後 已提交
489
你可以为每个客户机配置的`Logger.Level`对象告诉你要记录多少日志。选择如下:
茶陵後's avatar
茶陵後 已提交
490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510

* `NONE`,没有日志记录(** 默认 **)。

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

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

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

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

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

茶陵後's avatar
茶陵後 已提交
511
### [](#feign-capability-support)[1.11.佯装能力支持](#feign-capability-support)
茶陵後's avatar
茶陵後 已提交
512

茶陵後's avatar
茶陵後 已提交
513
伪装功能公开了核心伪装组件,以便可以修改这些组件。例如,这些功能可以使用`Client`*装饰*,并将修饰过的实例返回给 feign。对 Metrics 库的支持就是一个很好的实例。见[假装度量](#feign-metrics)
茶陵後's avatar
茶陵後 已提交
514 515 516 517 518 519 520 521 522 523 524 525 526

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

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

茶陵後's avatar
茶陵後 已提交
527
### [](#feign-metrics)[1.12.假装度量](#feign-metrics)
茶陵後's avatar
茶陵後 已提交
528

茶陵後's avatar
茶陵後 已提交
529
如果以下所有条件都是真的,则创建并注册一个`MicrometerCapability` Bean,以便你的假客户机将度量数据发布到 Micrometer:
茶陵後's avatar
茶陵後 已提交
530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553

* `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`

茶陵後's avatar
茶陵後 已提交
554
|   |`feign.metrics.enabled=false`禁用对**全部**冒充客户端的度量支持,而不管客户端级别标志的值是多少:`feign.client.config.feignName.metrics.enabled`<br/>如果你想在每个客户端启用或禁用 Merics,请不要设置,而使用`feign.client.config.feignName.metrics.enabled`。|
茶陵後's avatar
茶陵後 已提交
555 556 557 558 559 560 561 562 563 564 565 566 567 568
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

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

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

茶陵後's avatar
茶陵後 已提交
569
### [](#feign-caching)[1.13.假装缓存](#feign-caching)
茶陵後's avatar
茶陵後 已提交
570 571 572 573 574 575 576 577 578 579 580 581 582 583

如果使用`@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`禁用该功能。

茶陵後's avatar
茶陵後 已提交
584
### [](#feign-querymap-support)[1.14.假装 @QueryMap 支持](#feign-querymap-support)
茶陵後's avatar
茶陵後 已提交
585 586 587

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

茶陵後's avatar
茶陵後 已提交
588
Spring Cloud OpenFeign 提供了等价的`@SpringQueryMap`注释,其用于将 POJO 或 MAP 参数注释为查询参数映射。
茶陵後's avatar
茶陵後 已提交
589

茶陵後's avatar
茶陵後 已提交
590
例如,`Params`类定义参数`param1``param2`:
茶陵後's avatar
茶陵後 已提交
591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614

```
// 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。

茶陵後's avatar
茶陵後 已提交
615
### [](#hateoas-support)[1.15.Hateoas 支持](#hateoas-support)
茶陵後's avatar
茶陵後 已提交
616 617 618 619 620

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 支持。

茶陵後's avatar
茶陵後 已提交
621
当启用 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)
茶陵後's avatar
茶陵後 已提交
622 623 624 625 626 627 628 629 630 631

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

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

茶陵後's avatar
茶陵後 已提交
632
### [](#spring-matrixvariable-support)[1.16. Spring @MatrixVariable Support](#spring-matrixvariable-support)
茶陵後's avatar
茶陵後 已提交
633 634 635 636 637

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

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

茶陵後's avatar
茶陵後 已提交
638
如果传递了一个不同的对象,那么`@MatrixVariable`注释(如果已定义)中提供的`name`或注释的变量名将使用`=`与提供的方法参数连接。
茶陵後's avatar
茶陵後 已提交
639 640 641

IMPORTANT

茶陵後's avatar
茶陵後 已提交
642
尽管如此,在服务器端, Spring 并不要求用户将路径段占位符的名称与矩阵变量的名称相同,因为在客户端,该名称将过于模棱两可, Spring Cloud OpenFeign 要求你添加一个路径段占位符,其名称与`name`注释(如果已定义)中提供的`@MatrixVariable`或注释的变量名称相匹配。
茶陵後's avatar
茶陵後 已提交
643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661

例如:

```
@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();
}
```

茶陵後's avatar
茶陵後 已提交
662
### [](#feign-collectionformat-support)[1.17。feign`CollectionFormat`support](#feign-collectionformat-support)
茶陵後's avatar
茶陵後 已提交
663

茶陵後's avatar
茶陵後 已提交
664
我们通过提供`@CollectionFormat`注释来支持`feign.CollectionFormat`。你可以通过传递所需的`feign.CollectionFormat`作为注释值,用它来注释一个假客户机方法(或整个类以影响所有方法)。
茶陵後's avatar
茶陵後 已提交
665 666 667 668 669 670 671 672 673 674 675 676 677 678

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

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

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

}
```

茶陵後's avatar
茶陵後 已提交
679
|   |在发送`Pageable`作为查询参数时设置`CSV`格式,以便对其进行正确编码。|
茶陵後's avatar
茶陵後 已提交
680 681
|---|-----------------------------------------------------------------------------------------------------------|

茶陵後's avatar
茶陵後 已提交
682
### [](#reactive-support)[1.18.反应性支持](#reactive-support)
茶陵後's avatar
茶陵後 已提交
683 684 685

由于[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 也不支持。我们将在此添加对它的支持,只要它在核心项目中可用。

茶陵後's avatar
茶陵後 已提交
686
在此之前,我们建议使用[假装反应](https://github.com/Playtika/feign-reactive)作为 Spring WebClient 支持。
茶陵後's avatar
茶陵後 已提交
687

茶陵後's avatar
茶陵後 已提交
688
#### [](#early-initialization-errors)[1.18.1.早期初始化错误](#early-initialization-errors)
茶陵後's avatar
茶陵後 已提交
689 690 691 692 693 694 695 696

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

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

茶陵後's avatar
茶陵後 已提交
697
### [](#spring-data-support)[1.19. Spring Data Support](#spring-data-support)
茶陵後's avatar
茶陵後 已提交
698 699 700 701 702 703 704

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

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

茶陵後's avatar
茶陵後 已提交
705
### [](#spring-refreshscope-support)[1.20。 Spring `@RefreshScope`支持](# Spring-refreshScope-支持)
茶陵後's avatar
茶陵後 已提交
706

茶陵後's avatar
茶陵後 已提交
707
如果启用了假客户机刷新,那么每个假客户机都是以`feign.Request.Options`作为刷新范围来创建的 Bean。这意味着诸如`connectTimeout``readTimeout`之类的属性可以通过`POST /actuator/refresh`针对任何伪装客户端实例进行刷新。
茶陵後's avatar
茶陵後 已提交
708 709 710 711 712 713 714 715 716 717

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

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

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

茶陵後's avatar
茶陵後 已提交
718
### [](#oauth2-support)[1.21.OAuth2 支持](#oauth2-support)
茶陵後's avatar
茶陵後 已提交
719 720 721 722 723 724 725 726 727 728 729 730 731

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

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

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

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

茶陵後's avatar
茶陵後 已提交
732
## [](#configuration-properties)[2.配置属性](#configuration-properties)
茶陵後's avatar
茶陵後 已提交
733 734

要查看所有 Spring Cloud OpenFeign 相关配置属性的列表,请检查[附录页](appendix.html)
茶陵後's avatar
茶陵後 已提交
735 736

如果{{{i[’GoogleAnalyticsObject’]=r;i[r]=i[r]|function(){q=i[r].push(参数)},i[r].l=1\*new date();a=s.createElement(o),m=s.getelementsbyName(0);a.parentsName(1);a.A.SRC=g;m.M.analytnode(gua,m.com.com);(google=document=’,’,’’’’’’’’),’documents’,’’.’’’’’’’’’’’,’’’’’’