Spring Cloud OpenFeign の特長

宣言的な REST クライアント: フェイグ

フェイグ [GitHub] (英語) は宣言型 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 クライアントを提供します。

ふりを含める方法

Feign をプロジェクトに含めるには、グループ org.springframework.cloud およびアーティファクト ID spring-cloud-starter-openfeign のスターターを使用します。現在の Spring Cloud リリーストレインを使用したビルドシステムのセットアップの詳細については、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();

	@GetMapping("/stores")
	Page<Store> getStores(Pageable pageable);

	@PostMapping(value = "/stores/{storeId}", consumes = "application/json",
				params = "mode=upsert")
	Store update(@PathVariable("storeId") Long storeId, Store store);

	@DeleteMapping("/stores/{storeId:\\d+}")
	void delete(@PathVariable Long storeId);
}

@FeignClient アノテーションでは、文字列値 (上記の "stores" ) は任意のクライアント名であり、Spring Cloud LoadBalancer クライアント [GitHub] (英語) の作成に使用されます。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))。

マルチモジュール設定で Spring Feign クライアント Bean をロードするには、パッケージを直接指定する必要があります。

FactoryBean オブジェクトは、初期コンテキストのリフレッシュが行われる前にインスタンス化される可能性があり、Spring Cloud OpenFeign クライアントのインスタンス化によってコンテキストのリフレッシュがトリガーされるため、FactoryBean クラス内で宣言しないでください。

属性解決モード

Feign クライアント Bean の作成中に、@FeignClient アノテーションを介して渡された値を解決します。4.x では、値は先行して解決されています。これはほとんどのユースケースに適したソリューションであり、AOT のサポートも可能になります。

属性を遅延解決する必要がある場合は、spring.cloud.openfeign.lazy-attributes-resolution プロパティ値を true に設定します。

Spring Cloud Contract テスト統合の場合、遅延属性解決を使用する必要があります。

偽のデフォルトを上書きする

Spring Cloud の Feign サポートの中心的な概念は、名前付きクライアントの概念です。各 Feign クライアントは、オンデマンドで リモートサーバーに接続するために連携して動作するコンポーネントのアンサンブルの一部であり、アンサンブルには、アプリケーション開発者が @FeignClient アノテーションを使用して指定する名前があります。Spring Cloud は、オンデマンドで FeignClientsConfiguration を使用して、名前付きクライアントごとに新しいアンサンブルを ApplicationContext として作成します。これには、(特に) feign.Decoderfeign.Encoderfeign.Contract が含まれます。@FeignClient アノテーションの contextId 属性を使用して、そのアンサンブルの名前をオーバーライドできます。

Spring Cloud を使用すると、@FeignClient を使用して ( FeignClientsConfiguration の上に) 追加の構成を宣言することで、Feign クライアントを完全に制御できます。例:

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

この場合、クライアントは、FeignClientsConfiguration にすでに存在するコンポーネントと FooConfiguration のコンポーネントから構成されます (後者は前者をオーバーライドします)。

FooConfiguration に @Configuration のアノテーションを付ける必要はありません。ただし、そうである場合は、指定すると feign.Decoderfeign.Encoderfeign.Contract などのデフォルトのソースになるため、この構成を含めるすべての @ComponentScan からそれを除外するように注意してください。これは、@ComponentScan または @SpringBootApplication とは別の重複しないパッケージに入れることで回避できます。あるいは、@ComponentScan で明示的に除外することもできます。
ApplicationContext アンサンブルの名前の変更に加えて、@FeignClient アノテーションの contextId 属性を使用すると、クライアント名のエイリアスがオーバーライドされ、そのクライアント用に作成された構成 Bean の名前の一部として使用されます。
以前は、url 属性を使用していましたが、name 属性は必要ありませんでした。name の使用が必須になりました。

プレースホルダーは、name および url 属性でサポートされています。

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

Spring Cloud OpenFeign は、デフォルトで feign (BeanType beanName: ClassName) に次の Bean を提供します。

  • Decoder feignDecoder: ResponseEntityDecoder (SpringDecoder をラップするもの)

  • Encoder feignEncoder: SpringEncoder

  • Logger feignLogger: Slf4jLogger

  • MicrometerObservationCapability micrometerObservationCapability: feign-micrometer がクラスパス上にあり、ObservationRegistry が使用可能な場合

  • MicrometerCapability micrometerCapability: feign-micrometer がクラスパス上にある場合、MeterRegistry は使用可能ですが、ObservationRegistry は使用できません

  • CachingCapability cachingCapability: @EnableCaching アノテーションが使用されている場合。spring.cloud.openfeign.cache.enabled 経由で無効にできます。

  • Contract feignContract: SpringMvcContract

  • Feign.Builder feignBuilder: FeignCircuitBreaker.Builder

  • Client feignClient: Spring Cloud または LoadBalancer がクラスパス上にある場合は、FeignBlockingLoadBalancerClient が使用されます。いずれもクラスパス上にない場合は、デフォルトの Feign クライアントが使用されます。

spring-cloud-starter-openfeign は spring-cloud-starter-loadbalancer をサポートします。ただし、オプションの依存関係であるため、使用する場合はプロジェクトに追加されていることを確認する必要があります。

OkHttpClient ベースの Feign クライアントと Http2Client Feign クライアントを使用するには、使用するクライアントがクラスパス上にあることを確認し、それぞれ spring.cloud.openfeign.okhttp.enabled または spring.cloud.openfeign.http2client.enabled を true に設定します。

Apache HttpClient 5 をサポートする Feign クライアントの場合、HttpClient 5 がクラスパス上にあることを確認するだけで十分ですが、spring.cloud.openfeign.httpclient.hc5.enabled を false に設定することで、Feign クライアントでの使用を無効にすることができます。Apache HC5 を使用するときは、Bean に org.apache.hc.client5.http.impl.classic.CloseableHttpClient のいずれかを指定して、使用する HTTP クライアントをカスタマイズできます。

spring.cloud.openfeign.httpclient.xxx プロパティに値を設定することで、HTTP クライアントをさらにカスタマイズできます。プレフィックスが httpclient のみのクライアントはすべてのクライアントで機能し、プレフィックスが httpclient.hc5 のクライアントは Apache HttpClient 5 に、プレフィックスが httpclient.okhttp のクライアントは OkHttpClient に、プレフィックスが httpclient.http2 のクライアントは Http2Client に機能します。カスタマイズできるプロパティの完全なリストは付録に記載されています。プロパティを使用して Apache HttpClient 5 を構成できない場合は、プログラムによる構成用の HttpClient5FeignConfiguration.HttpClientBuilderCustomizer インターフェースがあります。

Apache HTTP コンポーネント 5.4 は、HTTP/1.1 TLS アップグレードに関連する HttpClient のデフォルトを変更しました。ほとんどのプロキシサーバーは課題なくアップグレードを処理しますが、Envoy または Istio では問題が発生する可能性があります。以前の動作を復元する必要がある場合は、以下の例に示すように、HttpClient5FeignConfiguration.HttpClientBuilderCustomizer を使用して復元できます。
@Configuration
public class FooConfiguration {

	@Bean
	public HttpClient5FeignConfiguration.HttpClientBuilderCustomizer httpClientBuilder() {
		return (httpClientBuilder) -> {
			RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
			requestConfigBuilder.setProtocolUpgradeEnabled(false);
			httpClientBuilder.setDefaultRequestConfig(requestConfigBuilder.build());
		};
	}
}
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 がデフォルトで作成され、再試行が無効になります。この再試行動作は、一時的なネットワーク関連例外および ErrorDecoder からスローされた RetryableException として扱われる IOExceptions を自動的に再試行する Feign のデフォルト動作とは異なることに注意してください。

これらの型のいずれかの 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 仮名を使用して構成プロパティを作成できます。

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 クライアントを作成し、それらが同じサーバーを指すようにしながらも、それぞれ異なるカスタム構成を持つようにする場合は、これらの構成 Bean の名前の衝突を回避するために、@FeignClient の contextId 属性を使用する必要があります。

@FeignClient(contextId = "fooClient", name = "stores", configuration = FooConfiguration.class)
public interface FooClient {
	//..
}
@FeignClient(contextId = "barClient", name = "stores", configuration = BarConfiguration.class)
public interface BarClient {
	//..
}

親コンテキストから Bean を継承しないように FeignClient を構成することもできます。これを行うには、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.decode-slash の値を false に設定することで変更できます。
デフォルトでは、Feign クライアントはリクエストパスから末尾のスラッシュ / 文字を削除しません。この動作は、spring.cloud.openfeign.client.remove-trailing-slash の値を true に設定することで変更できます。リクエストパスから末尾のスラッシュを削除する動作は、次のメジャーリリースでデフォルトの動作になる予定です。

SpringEncoder 構成

当社が提供する SpringEncoder では、バイナリコンテンツ型には null 文字セットを設定し、その他すべてのコンテンツ型には UTF-8 を設定します。

代わりに、spring.cloud.openfeign.encoder.charset-from-content-type の値を true に設定することで、この動作を変更して、Content-Type ヘッダーの文字セットから文字セットを導出することができます。

タイムアウト処理

デフォルトと指定されたクライアントの両方でタイムアウトを構成できます。OpenFeign は 2 つのタイムアウトパラメーターで動作します。

  • connectTimeout は、サーバーの処理時間が長いために呼び出し元がブロックされることを防ぎます。

  • readTimeout は接続確立時から適用され、レスポンスを返すのに時間がかかりすぎる場合にトリガーされます。

サーバーが実行中でないか利用できない場合、パケットにより接続が拒否されます。通信はエラーメッセージまたはフォールバックで終了します。connectTimeout が非常に低く設定されている場合、これは connectTimeout  の前に発生する可能性があります。ルックアップの実行とそのようなパケットの受信にかかる時間が、この遅延の大きな部分を引き起こします。これは、DNS ルックアップを伴う リモートホストに基づいて変更される可能性があります。

偽のクライアントを手動で作成する

場合によっては、上記の方法では不可能な方法で Feign Client をカスタマイズすることが必要になる場合があります。この場合、フェーンビルダー API [GitHub] (英語) を使用してクライアントを作成できます。以下は、同じインターフェースを持つ 2 つの Feign Client を作成し、それぞれを別のリクエストインターセプターで構成する例です。

@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 は、デフォルトの Feign ネイティブアノテーションの代わりに、SpringMVC アノテーションのサポートを提供します。Spring MVC アノテーションとネイティブ Feign アノテーションを混在させることは推奨されません。

Builder`to configure FeignClient not to inherit beans from the parent context. You can do this by overriding calling `inheritParentContext(false) を Builder で使用することもできます。

Spring Cloud CircuitBreaker サポートを装う

Spring Cloud CircuitBreaker がクラスパス上にあり、spring.cloud.openfeign.circuitbreaker.enabled=true である場合、Feign はすべてのメソッドをサーキットブレーカーでラップします。

クライアントごとに Spring Cloud CircuitBreaker サポートを無効にするには、「プロトタイプ」スコープを持つバニラ 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 を使用すると、サーキットブレーカー名に古いパターンを保持できます。

Bean または CircuitBreakerNameResolver を指定すると、サーキットブレーカー名のパターンを変更できます。

@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) に設定します。

構成プロパティを使用した 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 に設定します。

Spring Cloud CircuitBreaker フォールバックを装う

Spring Cloud CircuitBreaker は、フォールバックの概念をサポートしています。フォールバックとは、回線が開いているとき、またはエラーが発生したときに実行されるデフォルトのコードパスです。特定の @FeignClient のフォールバックを有効にするには、fallback 属性をフォールバックを実装するクラス名に設定します。また、実装を Spring Bean として宣言する必要があります。

@FeignClient(name = "test", url = "http://localhost:${server.port}/", fallback = Fallback.class)
protected interface TestClient {

	@GetMapping("/hello")
	Hello getHello();

	@GetMapping("/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 {

	@GetMapping("/hello")
	Hello getHello();

	@GetMapping("/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";
	}

}

フェイグと @Primary

Feign を Spring Cloud CircuitBreaker フォールバックで使用する場合、ApplicationContext 内に同じ型の Bean が複数存在します。これにより、Bean が 1 つだけ、またはプライマリとしてマークされた 1 つが存在しないため、@Autowired が機能しなくなります。これを回避するために、Spring Cloud OpenFeign はすべての Feign インスタンスを @Primary としてマークし、Spring Framework がどの Bean を注入するかを認識できるようにします。場合によっては、これが望ましくない場合もあります。この動作をオフにするには、@FeignClient の primary 属性を false に設定します。

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

偽装相続サポート

Feign は、単一継承インターフェースを介してボイラープレート API をサポートしています。これにより、一般的な操作を便利な基本インターフェースにグループ化できます。

UserService.java
public interface UserService {

	@GetMapping("/users/{id}")
	User getUser(@PathVariable("id") long id);
}
UserResource.java
@RestController
public class UserResource implements UserService {

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

}
@FeignClient インターフェースはサーバーとクライアント間で共有すべきではなく、クラスレベルでの @RequestMapping による @FeignClient インターフェースへのアノテーション付けはサポートされなくなりました。

リクエスト / レスポンスの圧縮を装う

Feign リクエストに対してリクエストまたはレスポンスの GZIP 圧縮を有効にすることを検討してください。これを行うには、次のいずれかのプロパティを有効にします。

spring.cloud.openfeign.compression.request.enabled=true
spring.cloud.openfeign.compression.response.enabled=true

偽のリクエスト圧縮により、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

これらのプロパティを使用すると、圧縮メディア型と最小リクエストしきい値の長さを選択できます。

リクエストが spring.cloud.openfeign.compression.request.mime-types で設定された MIME 型と spring.cloud.openfeign.compression.request.min-request-size で設定されたサイズに一致する場合、spring.cloud.openfeign.compression.request.enabled=true によって、リクエストに圧縮ヘッダーが追加されます。ヘッダーの機能は、クライアントが圧縮された本文を期待していることをサーバーに通知することです。クライアントによって提供されたヘッダーに基づいて圧縮された本文を提供するのは、サーバー側アプリのロールです。

OkHttpClient は「透過的」圧縮を使用するため、content-encoding ヘッダーまたは accept-encoding ヘッダーが存在する場合は無効になります。クラスパス上に feign.okhttp.OkHttpClient が存在し、spring.cloud.openfeign.okhttp.enabled が true に設定されている場合は圧縮を有効にしません。

偽装伐採

ロガーは、作成された Feign クライアントごとに作成されます。デフォルトでは、ロガーの名前は、Feign クライアントの作成に使用されるインターフェースの完全なクラス名です。偽ロギングは DEBUG レベルにのみ応答します。

application.yml
logging.level.project.user.UserClient: DEBUG

クライアントごとに設定できる Logger.Level オブジェクトは、Feign にどれくらいの量のログを記録するかを指示します。選択肢は次のとおりです。

  • NONE、ログなし ( DEFAULT )。

  • BASIC、リクエストメソッドと URL、レスポンスステータスコードと実行時間のみをログに記録します。

  • HEADERS、リクエストヘッダーとレスポンスヘッダーとともに基本情報をログに記録します。

  • FULL、リクエストとレスポンスの両方のヘッダー、本文、メタデータをログに記録します。

例: 以下は Logger.Level を FULL に設定します。

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

偽装機能のサポート

Feign 機能はコア Feign コンポーネントを公開し、これらのコンポーネントを変更できるようにします。例: 機能は Client を取得し、それを修飾し、修飾されたインスタンスを Feign に返すことができます。Micrometer のサポートは、これに関する良い実際の例です。Micrometer サポートを参照してください。

1 つ以上の Capability Bean を作成し、@FeignClient 構成に配置すると、登録し、関連するクライアントの動作を変更できるようになります。

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

Micrometer サポート

次の条件がすべて当てはまる場合、MicrometerObservationCapability Bean が作成および登録され、Feign クライアントが Micrometer によって監視可能になります。

  • feign-micrometer はクラスパス上にあります

  • ObservationRegistry Bean が利用可能です

  • 偽の micrometer プロパティは true に設定されます (デフォルト)

    • spring.cloud.openfeign.micrometer.enabled=true (すべてのクライアントにとって)

    • spring.cloud.openfeign.client.config.feignName.micrometer.enabled=true (単一のクライアントの場合)

アプリケーションがすでに Micrometer を使用している場合、この機能を有効にするのはクラスパスに feign-micrometer を置くのと同じくらい簡単です。

次のいずれかの方法でこの機能を無効にすることもできます。

  • クラスパスから feign-micrometer を除外する

  • 偽の micrometer プロパティの 1 つを false に設定する

    • spring.cloud.openfeign.micrometer.enabled=false

    • spring.cloud.openfeign.client.config.feignName.micrometer.enabled=false

spring.cloud.openfeign.micrometer.enabled=false は、クライアントレベルのフラグ spring.cloud.openfeign.client.config.feignName.micrometer.enabled の値に関係なく、すべての Feign クライアントに対する Micrometer サポートを無効にします。クライアントごとに 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);
	}
}

Feign (メトリクスのみのサポート) で MicrometerCapability を使用することは引き続き可能ですが、Micrometer サポート (spring.cloud.openfeign.micrometer.enabled=false) を無効にして、MicrometerCapability Bean を作成する必要があります。

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

偽装キャッシング

@EnableCaching アノテーションが使用されている場合は、Feign クライアントがそのインターフェース上の @Cache* アノテーションを認識できるように、CachingCapability Bean が作成および登録されます。

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 を使用して機能を無効にすることもできます。

Spring @RequestMapping サポート

Spring Cloud OpenFeign は、Spring @RequestMapping アノテーションとその派生アノテーション ( @GetMapping@PostMapping など) のサポートを提供します。@RequestMapping アノテーション ( valuemethodparamsheadersconsumesproduces を含む) の属性は、リクエストの内容として SpringMvcContract によって解析されます。

次の例を考えてみましょう。

params 属性を使用してインターフェースを定義します。

@FeignClient("demo")
public interface DemoTemplate {

        @PostMapping(value = "/stores/{storeId}", params = "mode=upsert")
        Store update(@PathVariable("storeId") Long storeId, Store store);
}

上記の例では、リクエスト URL は /stores/storeId?mode=upsert に解決されます。
params 属性は、複数の key=value または 1 つの key のみの使用もサポートします。

  • params = { "key1=v1", "key2=v2" } の場合、リクエスト URL は /stores/storeId?key1=v1&key2=v2 として解析されます。

  • params = "key" の場合、リクエスト URL は /stores/storeId?key として解析されます。

偽の @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 を実装できます。

HATEOAS のサポート

Spring は、HATEOAS [Wikipedia] (英語) 原則、Spring ハテオアスおよび Spring Data REST に従う REST 表現を作成するための API をいくつか提供します。

プロジェクトで org.springframework.boot:spring-boot-starter-hateoas スターターまたは org.springframework.boot:spring-boot-starter-data-rest スターターを使用する場合、Feign HATEOAS サポートはデフォルトで有効になります。

HATEOAS サポートが有効な場合、Feign クライアントは HATEOAS 表現モデル EntityModel (Javadoc) CollectionModel (Javadoc) PagedModel (Javadoc) を直列化および逆直列化できます。

@FeignClient("demo")
public interface DemoTemplate {

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

Spring @MatrixVariable サポート

Spring Cloud OpenFeign は、Spring @MatrixVariable アノテーションのサポートを提供します。

マップがメソッド引数として渡される場合、マップのキーと値のペアを = で結合することによって @MatrixVariable パスセグメントが作成されます。

別のオブジェクトが渡された場合、@MatrixVariable アノテーション (定義されている場合) で提供される name またはアノテーション付き変数名のいずれかが、= を使用して提供されたメソッド引数と結合されます。

IMPORTANT

サーバー側では、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();
}

偽の CollectionFormat サポート

@CollectionFormat アノテーションを提供することで feign.CollectionFormat をサポートします。目的の feign.CollectionFormat をアノテーション値として渡すことで、Feign クライアントメソッド (またはすべてのメソッドに影響を与えるクラス全体) にアノテーションを付けることができます。

次の例では、メソッドを処理するためにデフォルトの EXPLODED の代わりに CSV 形式が使用されます。

@FeignClient(name = "demo")
protected interface DemoFeignClient {

    @CollectionFormat(feign.CollectionFormat.CSV)
    @GetMapping(path = "/test")
    ResponseEntity performRequest(String test);

}

リアクティブサポート

Spring Cloud、OpenFeign の開発が活発だった当時、OpenFeign プロジェクト [GitHub] (英語) Spring WebClient (Javadoc) などのリアクティブクライアントをサポートしていなかったため、Spring Cloud、OpenFeign にもそのようなサポートを追加できませんでした。

Spring Cloud OpenFeign プロジェクトは現在機能が完全であると見なされているため、アップストリームプロジェクトでサポートが利用可能になったとしても、サポートを追加する予定はありません。代わりに、Spring インターフェースクライアントに移行することをお勧めします。そこでは、ブロッキングスタックとリアクティブスタックの両方がサポートされています。

初期の初期化エラー

構成の処理や Bean の初期化中は、アプリケーションのライフサイクルの初期段階で Feign クライアントを使用しないことをお勧めします。Bean の初期化中のクライアントの使用はサポートされていません。

同様に、Feign クライアントの使用方法によっては、アプリケーションの起動時に初期化エラーが発生する場合があります。この問題を回避するには、クライアントをオートワイヤーするときに ObjectProvider を使用します。

@Autowired
ObjectProvider<TestFeignClient> testFeignClient;

Spring Data サポート

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 を参照してください。

Spring @RefreshScope サポート

Feign クライアントのリフレッシュが有効になっている場合、各 Feign クライアントは以下を使用して作成されます。

  • リフレッシュスコープの Bean としての feign.Request.Options。これは、connectTimeout や readTimeout などのプロパティを任意の Feign クライアントインスタンスに対してリフレッシュできることを意味します。

  • org.springframework.cloud.openfeign.RefreshableUrl でラップされた URL。これは、spring.cloud.openfeign.client.config.{feignName}.url プロパティで定義されている場合、Feign クライアントの URL を任意の Feign クライアントインスタンスに対してリフレッシュできることを意味します。

これらのプロパティは POST /actuator/refresh を通じてリフレッシュできます。

デフォルトでは、Feign クライアントのリフレッシュ動作は無効になっています。リフレッシュ動作を有効にするには、次のプロパティを使用します。

spring.cloud.openfeign.client.refresh-enabled=true
@FeignClient インターフェースに @RefreshScope アノテーションを付けないでください。

OAuth2 のサポート

OAuth2 サポートを有効にするには、spring-boot-starter-oauth2-client 依存関係をプロジェクトに追加し、次のフラグを設定します。

spring.cloud.openfeign.oauth2.enabled=true

フラグが true に設定され、oauth2 クライアントコンテキストリソースの詳細が存在する場合、クラス OAuth2AccessTokenInterceptor の Bean が作成されます。各リクエストの前に、インターセプターは必要なアクセストークンを解決し、それをヘッダーとして含めます。OAuth2AccessTokenInterceptor は OAuth2AuthorizedClientManager を使用して、OAuth2AccessToken を保持する OAuth2AuthorizedClient を取得します。ユーザーが spring.cloud.openfeign.oauth2.clientRegistrationId プロパティを使用して OAuth2 clientRegistrationId を指定した場合、それがトークンの取得に使用されます。トークンが取得されない場合、または clientRegistrationId が指定されていない場合は、url ホストセグメントから取得された serviceId が使用されます。

TIP

OAuth2 クライアント registrationId として serviceId を使用すると、負荷分散された Feign クライアントに便利です。負荷分散されていないクライアントの場合は、プロパティベースの clientRegistrationId が適切なアプローチです。

TIP

OAuth2AuthorizedClientManager のデフォルト設定を使用したくない場合は、構成内でこの型の Bean をインスタンス化するだけです。

負荷分散された 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());
		}
	};
}

複数のトランスフォーマーが定義されている場合、Bean が定義されている順序で適用されます。または、LoadBalancerFeignRequestTransformer.DEFAULT_ORDER を使用して順序を指定することもできます。

X 転送ヘッダーのサポート

X-Forwarded-Host および X-Forwarded-Proto のサポートは、次のフラグを設定することで有効にできます。

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

偽のクライアントに URL を提供するためにサポートされている方法

次のいずれかの方法で、Feign クライアントに URL を提供できます。

ケース サンプル 詳細

URL は @FeignClient アノテーションで提供されます。

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

URL は、負荷分散を行わずに、アノテーションの url 属性から解決されます。

URL は、@FeignClient アノテーションと構成プロパティで提供されます。

@FeignClient(name="testClient", url="http://localhost:8081") and the property defined in application.yml as spring.cloud.openfeign.client.config.testClient.url=http://localhost:8081

URL は、負荷分散を行わずに、アノテーションの url 属性から解決されます。構成プロパティで指定された URL は使用されないままになります。

URL は @FeignClient アノテーションでは提供されませんが、構成プロパティで提供されます。

@FeignClient(name="testClient") and the property defined in application.yml as spring.cloud.openfeign.client.config.testClient.url=http://localhost:8081

URL は、負荷分散を行わずに構成プロパティから解決されます。spring.cloud.openfeign.client.refresh-enabled=true の場合、構成プロパティで定義された URL は、Spring RefreshScope のサポートに従ってリフレッシュできます。

URL は、@FeignClient アノテーションにも構成プロパティにも提供されません。

@FeignClient(name="testClient")

URL は、負荷分散を使用して、アノテーションの name 属性から解決されます。

AOT およびネイティブイメージのサポート

Spring Cloud OpenFeign は、Spring AOT 変換とネイティブイメージをサポートしますが、リフレッシュモードが無効、Feign クライアントのリフレッシュが無効 (デフォルト設定)、遅延 @FeignClient 属性解決が無効 (デフォルト設定) の場合にのみサポートされます。

Spring Cloud OpenFeign クライアントを AOT またはネイティブイメージモードで実行する場合は、必ず spring.cloud.refresh.enabled を false に設定してください。
Spring Cloud OpenFeign クライアントを AOT またはネイティブイメージモードで実行する場合は、spring.cloud.openfeign.client.refresh-enabled が true に設定されていないことを確認してください。
Spring Cloud OpenFeign クライアントを AOT またはネイティブイメージモードで実行する場合は、spring.cloud.openfeign.lazy-attributes-resolution が true に設定されていないことを確認してください。
ただし、プロパティを使用して url 値を設定した場合は、-Dspring.cloud.openfeign.client.config.[clientId].url=[url] フラグを指定してイメージを実行することで、@FeignClient url 値をオーバーライドすることができます。オーバーライドを有効にするには、ビルド時に @FeignClient 属性ではなく、プロパティを介して url 値も設定する必要があります。

構成プロパティ

すべての Spring Cloud OpenFeign 関連の構成プロパティのリストを確認するには、付録ページを参照してください。