Mastering OpenFeign: Simplify Spring Cloud REST Calls with Declarative Clients
This guide walks through replacing verbose RestTemplate calls with Spring Cloud OpenFeign's declarative REST client, covering dependency setup, client interfaces, annotations, logging, timeout, retry, interceptors, fallback handling with Sentinel, and advanced configurations to streamline microservice communication.
In the previous lesson the order service invoked the product service using a programmatic RestTemplate call, which required manual URL construction and request handling. This tutorial introduces Spring Cloud OpenFeign, a declarative REST client that reduces boilerplate by using annotations.
1. Remote Call with OpenFeign
First add the OpenFeign starter to the pom.xml of the services module:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>Enable Feign clients in the order service's main class:
@EnableFeignClients
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class);
}
}Define a Feign client interface for the product service:
@FeignClient(value = "service-product")
public interface ProductFeignClient {
@GetMapping("/product/{id}")
Product getProduct(@PathVariable("id") Long id);
}Note: The name used in @PathVariable must match the placeholder in the URL, otherwise OpenFeign will fail.
Inject the client into the order service implementation:
@Service
public class OrderServiceImpl implements OrderService {
@Resource
private ProductFeignClient productFeignClient;
public Product getProductRemoteLoadBalanceFeign(Long productId) {
return productFeignClient.getProduct(productId);
}
}1.2 Third‑Party Call Example
For services that provide an external API, such as WeChat Pay, you can still use OpenFeign by specifying the full URL:
@FeignClient(name = "pay-client", url = "https://api.mch.weixin.qq.com")
public interface PayFeignClient {
@PostMapping("/v3/pay/transactions/jsapi")
String pay(@RequestHeader("Authorization") String authorization,
@RequestParam("appid") String appid,
@RequestParam("mchid") String mchid);
}A simple test class can invoke the client:
@SpringBootTest
public class FeignClientTest {
@Resource
private PayFeignClient payFeignClient;
@Test
public void payTest() {
String authorization = "WECHATPAY2-SHA256-RSA2048 mchid=\"1900000001\",...";
String appId = "wx1234567890123456";
String mchId = "1900000001";
String prepayId = payFeignClient.pay(authorization, appId, mchId);
System.out.println(prepayId);
}
}2. Basic Configuration
2.1 Logging
Set the Feign client log level to FULL in application.yml and expose a bean:
logging:
level:
com.greenbook.order.feign: debug @Configuration
public class OrderServiceConfig {
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}2.2 Timeout Control
Configure connection and read timeouts to avoid cascading failures:
spring:
cloud:
openfeign:
client:
config:
default:
logger-level: full
connect-timeout: 1000
read-timeout: 3000
service-product:
logger-level: full
connect-timeout: 3000
read-timeout: 50002.3 Retry Mechanism
Inject a Retryer bean to enable automatic retries. The default implementation uses three parameters: period (initial interval), maxPeriod (maximum interval), and maxAttempts (maximum retries).
@Bean
Retryer retryer() {
return new Retryer.Default();
}The default constructor sets period = 100L, maxPeriod = 1s, and maxAttempts = 5. The interval grows by a factor (default 1.5) after each attempt.
3. Advanced Usage
3.1 Interceptor
Implement a request interceptor to add a custom header (e.g., X-Token) to every outgoing request:
@Component
public class XTokenRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
log.info("Entering interceptor, adding X-Token header");
requestTemplate.header("X-Token", UUID.randomUUID().toString());
}
}Register the interceptor either via @Component or explicitly in the OpenFeign configuration:
spring:
cloud:
openfeign:
client:
config:
default:
request-interceptors:
- com.greenbook.order.interceptor.XTokenRequestInterceptor3.2 Fallback (Fullback)
When a remote call fails or times out, a fallback implementation can return default data. First add the Sentinel starter dependency:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>Enable Sentinel for Feign:
feign:
sentinel:
enabled: trueCreate a fallback class that implements the Feign client interface:
@Service
public class ProductFeignClientFallback implements ProductFeignClient {
@Override
public Product getProduct(Long id) {
log.info("Fallback executed");
Product product = new Product();
product.setId(id);
product.setProductName("Default Product");
product.setPrice(BigDecimal.ZERO);
product.setNum(0);
return product;
}
}Reference the fallback in the client declaration:
@FeignClient(value = "service-product", fallback = ProductFeignClientFallback.class)
public interface ProductFeignClient {
@GetMapping("/product/{id}")
Product getProduct(@PathVariable("id") Long id);
}Remember to comment out the custom Retryer bean if you want the fallback to trigger after the first failure, because the retry mechanism would otherwise consume all attempts before the fallback is invoked.
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.
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.
