How do you apply configuration (encoders, interceptors, log level) to one specific Feign client without affecting all of them?
answer
- each client = own child context
- configuration = XConfig.class targets one
- NEVER @Configuration in scanned package (leaks global)
- defaultConfiguration for all clients
- properties override @Bean by default
basics
~20 sUse @FeignClient(configuration = MyConfig.class). Each client gets its own child application context, so beans in that config apply only to that client. Critically, don't put @Configuration + component-scan on it, or it leaks to everyone.
solid answer
~40 sEach Feign client runs in its own isolated child application context. You target one client with @FeignClient(name = "x", configuration = XConfig.class); beans defined there (Encoder, Decoder, RequestInterceptor, Logger.Level, ErrorDecoder, Contract, etc.) override the defaults for that client only. The big gotcha: the config class must NOT be picked up by your main @ComponentScan — if it's annotated @Configuration and sits in a scanned package, it becomes a global default for every Feign client, defeating the isolation. Keep it outside the scan path or don't annotate it @Configuration. To set global defaults for all clients, use @EnableFeignClients(defaultConfiguration = ...). Property-based config (spring.cloud.openfeign.client.config.<name>...) is the non-code alternative and, by default, properties win over the @Configuration beans.
code
java · 19 lines// NOTE: no @Configuration, and kept OUT of the main component-scan package
public class OrderFeignConfig {
@Bean
Logger.Level orderLogLevel() { return Logger.Level.FULL; }
@Bean
RequestInterceptor authHeader() {
return template -> template.header("X-Api-Key", "secret");
}
@Bean
ErrorDecoder orderErrorDecoder() { return new OrderErrorDecoder(); }
}
@FeignClient(name = "order-service", configuration = OrderFeignConfig.class)
interface OrderClient {
@GetMapping("/orders/{id}")
OrderDto get(@PathVariable long id);
}go deeper
Know the configuration attribute scopes settings to one client.
Name the configurable beans (RequestInterceptor, Logger.Level, ErrorDecoder) and the child-context idea.
Explain the @Configuration-leak gotcha and default vs per-client precedence.
Reason about property-vs-code precedence, defaultConfiguration governance, and contextId-scoped config for shared service ids.
**Isolated child contexts.** Spring Cloud gives **each** Feign client its own **child `ApplicationContext`** (a 'named context'), keyed by the client's `name`/`contextId`. Beans in that child context configure only that client. This is the same named-context mechanism SCLB uses per service. **Per-client configuration.** Pass a config class: `@FeignClient(name = "order-service", configuration = OrderFeignConfig.class)`. Inside it you declare `@Bean`s that Feign recognizes: - `Encoder` / `Decoder` — body serialization. - `Contract` — annotation dialect (e.g. use JAX-RS instead of Spring MVC). - `RequestInterceptor` — mutate every outgoing request (add auth headers, correlation ids). - `Logger.Level` — `NONE`/`BASIC`/`HEADERS`/`FULL` (also needs the client's logger at DEBUG to emit). - `ErrorDecoder` — map non-2xx responses to exceptions. - `Request.Options` — connect/read timeouts (though properties are usually cleaner). - `Retryer` — Feign-level retry policy. **THE gotcha — don't let it leak.** The config class **must not be reachable by your application's `@ComponentScan`**. If it carries `@Configuration` and lives in a scanned package, Spring registers its beans in the **parent** context too, making them **global defaults for every Feign client**. The docs explicitly warn to place such classes in a non-overlapping package, or omit `@Configuration`. Symptom: an interceptor meant for one client mysteriously fires on all of them. **Global defaults.** To intentionally configure all clients, use `@EnableFeignClients(defaultConfiguration = GlobalFeignConfig.class)`. Per-client `configuration` then overrides those defaults for that client. **Property-based configuration.** You can skip code entirely: `spring.cloud.openfeign.client.config.order-service.loggerLevel=full`, `...requestInterceptors[0]=...`, etc. The special client name `default` sets defaults for all. **Precedence:** by default, **properties override `@Configuration` beans**; you can flip that with `spring.cloud.openfeign.client.config.default.default-to-properties=false` so Java config wins. **contextId reuse.** Per-client config is resolved by the context id, so if two clients share a `name`, give them distinct `contextId`s to configure them independently. **Older property prefix.** Legacy code uses `feign.client.config.<name>...`; modern Spring Cloud uses `spring.cloud.openfeign.client.config.<name>...`. Prefer the new prefix.
- You added @Configuration to your per-client config class and now the interceptor runs for every Feign client. Why?Because @Configuration in a component-scanned package registers those beans in the parent context as global Feign defaults. Move the class outside the scan path or drop @Configuration so it's only used when referenced via the configuration attribute.
- If both a @Bean and a property set the log level for a client, which wins?By default properties win (default-to-properties=true). Set spring.cloud.openfeign.client.config.default.default-to-properties=false to make Java @Configuration take precedence.
saying these in an interview costs you the question
- Putting @Configuration on the per-client config class inside the scanned package
- Thinking configuration = X.class merges into a global context
- Assuming @Bean always beats properties (it's the opposite by default)