Overview
OpenFeign is a declarative HTTP client for Java that simplifies service-to-service communication in distributed systems. Spring Cloud extends Feign with auto-configuration, integration with Spring MVC annotations, load balancing via Spring Cloud LoadBalancer, circuit breaking, and observability support.
GitHub: https://github.com/spring-cloud/spring-cloud-openfeign
Official Docs: https://docs.spring.io/spring-cloud-openfeign/docs/current/reference/html/
Integration with Spring Boot
1. Add the Starter Dependency
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
Ensure version compatibility between Spring Boot and Spring Cloud — refer to the official compatibility matrix.
2. Enable Feign Clients
Annotate your main application class:
@SpringBootApplication
@EnableFeignClients
public class MyApp {
public static void main(String[] args) {
SpringApplication.run(MyApp.class, args);
}
}
3. Define a Feign Client Interface
@FeignClient(name = "inventory-service", path = "/api/inventory")
public interface InventoryClient {
@GetMapping("/items/{id}")
Item getItem(@PathVariable Long id);
@PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE)
Item createItem(@RequestBody Item item);
@DeleteMapping("/items/{id}")
void deleteItem(@PathVariable Long id);
}
name: Logical service identifier (used for service discovery if Eureka or LoadBalancer is present).url: Optional absolute URL (bypasses discovery).path: Base path prefix applied to all method-level mappings.
Customizing Client Behavior
Override Default Beans Per Client
Each @FeignClient gets its own Spring context. To customize components for a specific client, define a configuration class and reference it:
@FeignClient(
name = "inventory-service",
configuration = InventoryClientConfig.class
)
public interface InventoryClient { /* ... */ }
@Configuration
public class InventoryClientConfig {
@Bean
public Encoder feignEncoder() {
return new JacksonEncoder();
}
@Bean
public RequestInterceptor authInterceptor() {
return template -> template.header("Authorization", "Bearer token");
}
}
⚠️ Avoid @Configuration on shared config classes unless intended globally — use @Configuration only when explicitly needed and ensure proper scoping.
Configure via Properties
Apply settings per client or globally using application.yml:
feign:
client:
config:
default:
connectTimeout: 3000
readTimeout: 6000
loggerLevel: BASIC
inventory-service:
connectTimeout: 5000
decode404: true
To apply globally, use default as the feignName. Property-based config takes precedence over Java-config unless feign.client.default-to-properties=false is set.
Multiple Clients with Same Name
Use contextId to isolate bean definitions:
@FeignClient(contextId = "adminInventory", name = "inventory-service", configuration = AdminConfig.class)
public interface AdminInventoryClient { /* ... */ }
@FeignClient(contextId = "publicInventory", name = "inventory-service", configuration = PublicConfig.class)
public interface PublicInventoryClient { /* ... */ }
Disable Parent Context Inheritance
Prevent a client from inheriting beans from the parent ApplicationContext:
@Bean
public FeignClientConfigurer feignClientConfigurer() {
return () -> false;
}
Timeouts and Retries
Timeout Configuration
Three primary approaches:
-
Declarative (YAML):
feign: client: config: default: connectTimeout: 2000 readTimeout: 8000 -
Per-client via
Request.Options:@Bean public Request.Options customOptions() { return new Request.Options(2_000, 8_000); } -
Per-request via method parameter:
@GetMapping("/status") Status getStatus(Request.Options options); // Call site client.getStatus(new Request.Options(1_500, 5_000));
Retry Strategy
Feign uses Retryer to control retry behavior. By default, Retryer.NEVER_RETRY disables retries.
Custom retry logic:
@Bean
public Retryer retryer() {
// maxAttempts=3, initialInterval=100ms, maxInterval=1000ms
return new Retryer.Default(100, 1000, 3);
}
For fine-grained control, implement Retryer:
public class AdaptiveRetryer implements Retryer {
private final int maxRetries;
private int attempt;
public AdaptiveRetryer(int maxRetries) {
this.maxRetries = maxRetries;
this.attempt = 0;
}
@Override
public void continueOrPropagate(RetryableException e) {
if (++attempt > maxRetries) throw e;
try {
Thread.sleep((long) Math.pow(2, attempt) * 100); // exponential backoff
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
throw e;
}
}
@Override
public Retryer clone() {
return new AdaptiveRetryer(maxRetries);
}
}
Logging
Feign logs are tied to SLF4J categories matching the client’s fully qualified interface name. Enable debug logging to see request/response details:
logging:
level:
com.example.InventoryClient: DEBUG
Log levels:
NONE: No logging.BASIC: Method, URL, status, duration.HEADERS: Includes headers.FULL: Full body and metadata.
Set level programmatically:
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
Error Handling and Fallbacks
When integrated with Spring Cloud CircuitBreaker, fallbacks execute on circuit open or call failure.
Static Fallback Class
@FeignClient(
name = "inventory-service",
fallback = InventoryFallback.class
)
public interface InventoryClient { /* ... */ }
@Component
public class InventoryFallback implements InventoryClient {
@Override
public Item getItem(Long id) {
return new Item("fallback-item", 0);
}
}
Fallback Factory (with exception context)
@FeignClient(
name = "inventory-service",
fallbackFactory = InventoryFallbackFactory.class
)
public interface InventoryClient { /* ... */ }
@Component
public class InventoryFallbackFactory implements FallbackFactory<InventoryClient> {
@Override
public InventoryClient create(Throwable cause) {
return new InventoryClient() {
@Override
public Item getItem(Long id) {
log.warn("Failed to fetch item {} due to {}", id, cause.getMessage());
return new Item("recovered-item", -1);
}
};
}
}
Observability and Metrics
Micrometer metrics are auto-enabled if feign-micrometer is on the classpath and MeterRegistry is available.
Enable globally:
spring:
cloud:
openfeign:
micrometer:
enabled: true
Or per client:
feign:
client:
config:
inventory-service:
micrometer:
enabled: true
Customize observation registry:
@Bean
public MicrometerObservationCapability observationCapability(ObservationRegistry registry) {
return new MicrometerObservationCapability(registry);
}
Compression Support
Enable request/response GZIP compression:
spring:
cloud:
openfeign:
compression:
request:
enabled: true
mime-types: text/xml,application/xml,application/json
min-request-size: 2048
response:
enabled: true
⚠️ Compression is disabled when OkHttpClient is active and Content-Encoding or Accept-Encoding headers are present.
Query and Path Parameter Binding
- Use
@RequestParamfor query parameters. - Use
@PathVariablefor URI path segments. - For complex objects as query maps, use
@SpringQueryMap:
public class SearchCriteria {
private String category;
private Integer limit;
}
@GetMapping("/search")
List<Product> search(@SpringQueryMap SearchCriteria criteria);
HATEOAS and Matrix Variables
If spring-boot-starter-hateoas or spring-boot-starter-data-rest is present, Feign supports marshalling EntityModel, CollectionModel, and PagedModel.
Matrix variables are supported via @MatrixVariable, requiring explicit path variable alignment:
@GetMapping("/objects/{matrixVars}")
Map<String, List<String>> getObjects(@MatrixVariable Map<String, List<String>> vars);
Collection Format Handling
Specify serializasion format for collection parameters:
@FeignClient(name = "catalog")
public interface CatalogClient {
@CollectionFormat(feign.CollectionFormat.CSV)
@GetMapping("/products")
List<Product> findProducts(@RequestParam List<String> ids);
}
Manual Client Construction
For full control, build clients imperatively:
@Autowired
private Decoder decoder;
@Autowired
private Encoder encoder;
@Bean
public CatalogClient catalogClient() {
return Feign.builder()
.decoder(decoder)
.encoder(encoder)
.contract(new SpringMvcContract())
.requestInterceptor(new AuthInterceptor())
.target(CatalogClient.class, "https://catalog-api.example.com");
}
Refreshable Clients
Enable dynamic property refresh for timeouts and URLs:
spring:
cloud:
openfeign:
client:
refresh-enabled: true
Then update properties at runtime via /actuator/refresh.
Caching Support
With @EnableCaching, Feign respects @Cacheable, @CacheEvict, etc., provided spring-cloud-openfeign.cache.enabled=true (default). Disable with:
spring:
cloud:
openfeign:
cache:
enabled: false
Reactive Support
OpenFeign does not natively support reactive types (Mono, Flux). WebClient remains the preferred choice for reactive service calls.
Data Conversion Extensions
Spring Data Page and Sort converters are auto-registered if spring-data-commons and jackson-databind are present. Disable with:
spring:
cloud:
openfeign:
autoconfiguration:
jackson:
enabled: false
URL Resolution Strategies
| Scenario | Example | Behavior |
|---|---|---|
url in annotation |
@FeignClient(url="http://localhost:8080") |
Direct HTTP call; no load balancing |
url in properties only |
spring.cloud.openfeign.client.config.myclient.url=http://host:port |
Resolved from config; refreshable if enabled |
name only |
@FeignClient(name="my-service") |
Uses service discovery + load balancing |
Both name and url |
Annotation + property | url from annotation wins; property ignored |
Parameter Mapping Examples
| Server Endpoint | Feign Method Signature |
|---|---|
GET /users/{id} |
@GetMapping("/users/{id}") User get(@PathVariable Long id) |
GET /search?q=foo |
@GetMapping("/search") List<R> search(@RequestParam String q) |
POST /users with JSON body |
@PostMapping("/users") User create(@RequestBody User user) |
GET /items?ids=1&ids=2 |
@GetMapping("/items") List<I> list(@RequestParam List<Long> ids) |
GET /items/{id}/details |
@GetMapping("/{id}/details") Detail get(@PathVariable Long id) |
Troubleshooting Initialization Errors
If lazy initialization is required (e.g., during startup), inject clients via ObjectProvider:
@Autowired
private ObjectProvider<InventoryClient> inventoryClientProvider;
// Later...
InventoryClient client = inventoryClientProvider.getObject();
Disabling Hystrix (Legacy Note)
Hystrix support is deprecated. Prefer Spring Cloud CircuitBreaker with Resilience4j or Sentinel. If still used, disable via:
feign:
hystrix:
enabled: false
Summary of Key Configuration Properties
| Property | Description | Default |
|---|---|---|
feign.client.config.default.connectTimeout |
Connection timeout (ms) | 10000 |
feign.client.config.default.readTimeout |
Read timeout (ms) | 60000 |
feign.client.config.default.loggerLevel |
Logging verbosity | NONE |
feign.client.refresh-enabled |
Enable dynamic refresh | false |
spring.cloud.openfeign.micrometer.enabled |
Micrometer metrics | true |
spring.cloud.openfeign.cache.enabled |
Cache annotation support | true |
spring.cloud.openfeign.compression.request.enabled |
Request compression | false |
spring.cloud.openfeign.compression.response.enabled |
Response compression | false |