從靜態到動態!Spring Boot 動態創建配置Feign Client
環境:SpringBoot3.4.2
1. 簡介
在微服務架構中,Feign堪稱開發者的得力助手——它是一款聲明式的HTTP客戶端,能讓調用其他服務變得像編寫接口一樣簡單。然而,如果你的Feign客戶端配置(如基礎URL、超時時間或認證信息)需要動態化,那該怎么辦呢?
將這類值硬編碼在application.yml文件中,對于靜態環境而言尚可接受,但在實際場景中,我們往往需要運行時靈活性:
- 多租戶應用中,每個客戶的API端點可能各不相同
- 從數據庫或配置服務器獲取的環境特定配置
- 根據業務邏輯動態路由到不同的微服務
本篇文章我們將通過數據庫動態配置Feign客戶端。
2.實戰案例
2.1 定義Feign基本配置實體
@Entity
@Table(name = "t_feign_config")
public class FeignConfig {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id ;
/**客戶端名稱*/
private String clientName ;
/**該客戶端的baseUrl*/
private String baseUrl ;
/**請求超時配置*/
private Long connectTimeout ;
private Long readTimeout ;
private String apiKey ;
// getters, setters
}該實體對象中我們定義了Feign客戶端所需要的基本信息。準備如下數據:
圖片
2.2 定義Repository接口
在該Repository中,我們僅提供了一個方法,通過clientName查詢對應的配置詳細。
public interface FeignConfigRepository extends JpaRepository<FeignConfig, Long> {
/**根據clientName查詢Feign配置*/
Optional<FeignConfig> findByClientName(String clientName) ;
}2.3 定義Service
@Service
public class FeignConfigService {
private final FeignConfigRepository configRepository ;
public FeignConfigService(FeignConfigRepository configRepository) {
this.configRepository = configRepository;
}
public FeignConfig getClientConfig(String clientName) {
return configRepository.findByClientName(clientName)
.orElseThrow(() -> new RuntimeException("Feign 配置不存在: " + clientName));
}
}2.4 Feign客戶端創建工廠
@Component
public class FeignClientFactory {
private final FeignConfigService configService;
private final ObjectFactory<HttpMessageConverters> messageConverters ;
public FeignClientFactory(FeignConfigService configService,
ObjectFactory<HttpMessageConverters> messageConverters) {
this.configService = configService;
this.messageConverters = messageConverters ;
}
public <T> T getClient(Class<T> clientType, String clientName) {
FeignConfig config = configService.getClientConfig(clientName) ;
Request.Options options = new Request.Options(config.getConnectTimeout(), TimeUnit.MILLISECONDS,
config.getReadTimeout(), TimeUnit.MILLISECONDS, false);
return Feign.builder()
.encoder(new SpringEncoder(this.messageConverters))
.decoder(new SpringDecoder(messageConverters))
.contract(new SpringMvcContract())
.retryer(Retryer.NEVER_RETRY)
.requestInterceptor(template -> template.header("Authorization", "Bearer " + config.getApiKey()))
.options(options)
.target(clientType, config.getBaseUrl()) ;
}
}每次在調用時都從數據庫中獲取最新的配置創建對象。
2.5 測試
@RestController
@RequestMapping("/test")
public class TestController {
private final FeignClientFactory factory;
public TestController(FeignClientFactory factory) {
this.factory = factory;
}
@GetMapping("/user/query")
public UserDTO query() {
return this.factory
.getClient(UserClient.class, "user-client")
.query() ;
}
}調用的接口
@RestController
@RequestMapping("/users")
public class UserController {
@GetMapping("/query")
public ResponseEntity<?> query() {
return ResponseEntity.ok(new User("pack", "123123", new Date(), "xxgg@qq.com")) ;
}
}執行結果
圖片
以上我們就完成了基于數據庫的Feign的動態配置。
默認情況下,spring-cloud-openfeign 已經提供了一定程度的動態配置支持,尤其在以下兩個關鍵場景中表現尤為突出:
- 動態配置請求超時時間
- 動態刷新請求的url
接下來,我們將通過具體代碼示例,演示如何利用 spring-cloud-openfeign 的現有能力實現上述兩種動態配置功能,幫助你快速掌握這一核心特性。
2.6 動態配置請求超時時間
首先,準備三方接口(模擬耗時)。
@GetMapping("/list")
public ResponseEntity<?> list() throws Exception {
List<User> users = List.of(
new User("張三", "111111", new Date(), "zs@gmail.com"),
new User("李四", "222222", new Date(), "ls@gmail.com"),
new User("王五", "333333", new Date(), "ww@gmail.com"),
new User("趙六", "444444", new Date(), "zl@gmail.com")
) ;
// 模擬耗時4s
TimeUnit.SECONDS.sleep(4) ;
return ResponseEntity.ok(users) ;
}其次,定義Feign接口。
@FeignClient(name = "user-client", url = "http://localhost:8080")
public interface UserDefaultClient {
@GetMapping("/users/list")
List<UserDTO> list() ;
}最后,為user-client配置超時時間。
spring:
cloud:
openfeign:
client:
config:
user-client:
read-timeout: 3000設置讀數據超時時間為3s(我們上面接口模擬了4s才會輸出內容)。
測試接口
private final UserDefaultClient defaultClient ;
public TestController(UserDefaultClient defaultClient) {
this.defaultClient = defaultClient ;
}
@GetMapping("/user/list2")
public List<UserDTO> list2() {
return this.defaultClient.list() ;
}
圖片

拋出了超時異常。
接下來,我們引入actuator。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>配置actuator。
management:
endpoints:
web:
base-path: /ac
exposure:
include:
- refresh我們再次啟動服務;啟動服務后,我們直接修改配置如下:
spring:
cloud:
openfeign:
client:
config:
user-client:
read-timeout: 5000接著調用 /ac/refresh 接口。
圖片
發現了配置的變化,再次訪問接口。
圖片
動態刷新配置成功。
2.7 動態刷新請求URL
首先,修改Feign定義接口如下:
@FeignClient(name = "user-client")
public interface UserDefaultClient {
@GetMapping("/users/list")
List<UserDTO> list() ;
}注意:這里我們并沒有定義url屬性,我們會在配置文件中定義。
其次,配置文件中定義user-client客戶端請求的url。
spring:
cloud:
openfeign:
client:
config:
user-client:
url: http://localhost:8080最后,我們還是訪問上面的接口。
圖片
成功;修改請求的url如下:
spring:
cloud:
openfeign:
client:
config:
user-client:
url: http://localhost:8081將端口改錯;接下來,調用/ac/refresh接口。
圖片
再次訪問接口。
圖片

連接錯誤了,請求的端口變為8081。





























