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);
    }

}
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 註解中,字串值(上面的 "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.Decoderfeign.Encoderfeign.Contract 等的預設來源。這可以透過將其放在與任何 @ComponentScan@SpringBootApplication 分開的、不重疊的包中,或者在 @ComponentScan 中明確排除來避免。
使用 @FeignClient 註解的 contextId 屬性除了改變 ApplicationContext 集合的名稱外,還會覆蓋客戶端名稱的別名,並用作該客戶端建立的配置 bean 名稱的一部分。
以前,使用 url 屬性時,不需要 name 屬性。現在使用 name 是必需的。

nameurl 屬性中支援佔位符。

@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-micrometerMeterRegistry 可用且 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 (MicrometerObservationCapabilityCachingCapability 預設提供)

預設情況下會建立一個型別為 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 指的是 @FeignClientvalue,它也與 @FeignClientname@FeignClientcontextId 互為別名。在負載均衡場景中,它還對應於將用於檢索例項的伺服器應用的 serviceId。指定的解碼器、重試器及其他類的類必須在 Spring 上下文中有 bean 或具有預設建構函式。

預設配置可以按照上述類似方式在 @EnableFeignClientsdefaultConfiguration 屬性中指定。區別在於此配置將應用於所有 feign 客戶端。

如果你更喜歡使用配置屬性來配置所有 @FeignClient,你可以使用 default feign 名稱建立配置屬性。

你可以使用 spring.cloud.openfeign.client.config.feignName.defaultQueryParametersspring.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 客戶端,使它們指向同一伺服器但每個都有不同的自定義配置,那麼我們必須使用 @FeignClientcontextId 屬性,以避免這些配置 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。在某些情況下,這可能不理想。要關閉此行為,請將 @FeignClientprimary 屬性設定為 false。

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

1.9. Feign 繼承支援

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 介面不應在伺服器和客戶端之間共享,並且不再支援在類級別使用 @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-encodingaccept-encoding 頭部,透明壓縮會被停用,因此當 classpath 中存在 feign.okhttp.OkHttpClient 並且 spring.cloud.openfeign.okhttp.enabled 設定為 true 時,我們不啟用壓縮。

1.11. Feign 日誌記錄

為每個建立的 Feign 客戶端都會建立一個 logger。預設情況下,logger 的名稱是用於建立 Feign 客戶端的介面的完整類名。Feign 日誌記錄僅響應 DEBUG 級別。

application.yml
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 類定義了引數 param1param2

// 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 HateoasSpring Data REST

如果你的專案使用了 org.springframework.boot:spring-boot-starter-hateoas starter 或 org.springframework.boot:spring-boot-starter-data-rest starter,Feign 的 HATEOAS 支援預設是啟用的。

啟用 HATEOAS 支援後,Feign 客戶端允許序列化和反序列化 HATEOAS 表示模型:EntityModelCollectionModelPagedModel

@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.Pageorg.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。這意味著 connectTimeoutreadTimeout 等屬性可以在任何 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 來獲取持有 OAuth2AccessTokenOAuth2AuthorizedClient。如果使用者使用 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-HostX-Forwarded-Proto 支援

spring.cloud.loadbalancer.x-forwarded.enabled=true

1.25. 支援的為 Feign 客戶端提供 URL 的方式

你可以透過以下任一方式為 Feign 客戶端提供 URL

情況 示例 詳情

@FeignClient 註解中提供 URL。

@FeignClient(name="testClient", url="https://:8081")

URL 從註解的 url 屬性解析,不進行負載均衡。

@FeignClient 註解和配置屬性中提供 URL。

@FeignClient(name="testClient", url="https://:8081")application.yml 中定義的屬性 spring.cloud.openfeign.client.config.testClient.url=https://:8081

URL 從註解的 url 屬性解析,不進行負載均衡。配置屬性中提供的 URL 未被使用。

未在 @FeignClient 註解中提供 URL,但在配置屬性中提供。

@FeignClient(name="testClient")application.yml 中定義的屬性 spring.cloud.openfeign.client.config.testClient.url=https://:8081

URL 從配置屬性解析,不進行負載均衡。如果 spring.cloud.openfeign.client.refresh-enabled=true,則配置屬性中定義的 URL 可以如Spring RefreshScope 支援中所述進行重新整理。

URL 既未在 @FeignClient 註解中提供,也未在配置屬性中提供。

@FeignClient(name="testClient")

URL 從註解的 name 屬性解析,進行負載均衡。

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] 標誌執行映象來覆蓋 @FeignClienturl 值。為了啟用覆蓋,在構建時也必須透過屬性而不是 @FeignClient 屬性來設定 url 值。

2. 配置屬性

要檢視所有 Spring Cloud OpenFeign 相關的配置屬性列表,請查閱附錄頁面