Why I Dropped @FeignClient After Years of Use: Spring Boot’s Built‑In HTTP Service Client
The author refactors an old Spring Cloud project by removing Feign, replacing @FeignClient with Spring Boot 4.x’s native @HttpExchange and @ImportHttpServices, and demonstrates that Spring’s built‑in HTTP Service Client provides the same declarative convenience with less boilerplate, while still supporting custom configuration, error handling, and reactive use cases.
While updating an older microservice project that contained dozens of Feign clients, the author recognized the familiar Feign‑style interface definitions and the typical @EnableFeignClients bootstrapping.
Spring has long offered a declarative HTTP client, and recent releases of Spring Boot 4.x and Spring Framework 7 now provide a fully featured HTTP Service Client that can replace OpenFeign in many scenarios.
Feign’s main advantage is that it lets business code call a simple Java interface without dealing with low‑level HTTP details. Spring’s new annotations ( @HttpExchange, @GetExchange, @PostExchange, etc.) achieve the same effect:
@HttpExchange("/api/stocks")
public interface InventoryClient {
@GetExchange("/{sku}")
StockResponse getStock(@PathVariable String sku);
@PostExchange("/reserve")
ReserveResponse reserve(@RequestBody ReserveRequest request);
}To use these interfaces, the Feign starter dependency and the @EnableFeignClients annotation are removed. Only the standard spring-boot-starter-web (or spring-boot-starter-parent) is required.
Spring Boot now auto‑configures HTTP Service Clients via @ImportHttpServices. A service‑client group can be defined and linked to configuration properties:
@SpringBootApplication
@ImportHttpServices(group = "inventory", types = InventoryClient.class)
public class OrderApplication { … } spring:
http:
serviceclient:
inventory:
base-url: http://localhost:8090
connect-timeout: 1s
read-timeout: 2sMultiple client interfaces that target the same remote service can share a group, avoiding repeated configuration:
@ImportHttpServices(
group = "inventory",
types = { InventoryClient.class, WarehouseClient.class, SkuClient.class })Static headers (e.g., X‑App‑Name) can be set directly in the YAML, while dynamic tokens are added via a RestClientHttpServiceGroupConfigurer bean that intercepts requests for a specific group:
@Bean
RestClientHttpServiceGroupConfigurer inventoryAuthConfigurer(TokenProvider tokenProvider) {
return groups -> groups.filterByName("inventory")
.forEachClient((group, builder) -> builder.requestInterceptor(
(request, body, execution) -> {
request.getHeaders().setBearerAuth(tokenProvider.getToken());
request.getHeaders().set("X-App-Name", "order-service");
return execution.execute(request, body);
}));
}Error handling mirrors Feign’s ErrorDecoder by registering a defaultStatusHandler that throws a custom exception for 4xx/5xx responses:
@Bean
RestClientHttpServiceGroupConfigurer inventoryErrorHandler() {
return groups -> groups.filterByName("inventory")
.forEachClient((group, builder) -> builder.defaultStatusHandler(
HttpStatusCode::isError,
(request, response) -> {
throw new InventoryServiceException(response.getStatusCode().value());
}));
}Earlier manual approaches required creating a RestClient, an adapter, and a HttpServiceProxyFactory. The new @ImportHttpServices annotation eliminates that boilerplate, automatically scanning for @HttpExchange interfaces and registering them as Spring beans.
Reactive applications can also use the same declarative interfaces; Spring’s HTTP Service Client supports both the synchronous RestClient and the reactive WebClient adapters.
The author cautions that existing stable projects with many Feign clients need not rewrite everything, as OpenFeign remains maintained (feature‑complete). However, for new projects that only need a few REST calls, Spring’s built‑in HTTP Service Client is sufficient and avoids the extra dependency on Spring Cloud OpenFeign.
Spring Framework 7 deprecates RestTemplate in favor of RestClient, and the HTTP Service Client sits on top of these modern clients, providing a unified declarative interface layer.
In summary, the migration steps are:
Remove spring-cloud-starter-openfeign and @EnableFeignClients.
Replace @FeignClient with @HttpExchange and method‑level @GetExchange, @PostExchange, etc.
Add @ImportHttpServices to the application class (or use basePackages for scanning).
Configure base URLs and timeouts under spring.http.serviceclient.
Optionally configure static headers, dynamic token interceptors, and custom error handling.
These changes retain the same business‑layer code ( inventoryClient.getStock(sku)) while simplifying configuration and reducing the overall dependency footprint.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
LuTiao Programming
LuTiao Programming is a friendly community offering free programming lessons. We inspire learners to explore new ideas and technologies and quickly acquire job-ready skills.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
