GatewayFilter注入Feign Client引发循环依赖问题求解决方案
在网关过滤器的校验环节中,我需要调用微服务接口。当我将Feign Client(UserApi)注入GatewayFilter实现类SecurityFilter后,出现了如下循环依赖错误,即使已开启允许循环引用仍无法解决,请问该如何处理?
错误信息
描述:
应用上下文部分Bean的依赖形成了循环:
┌─────┐
| securityFilter(定义在文件[/target/classes/com/example/SecurityFilter.class])
↑ ↓
| cachedCompositeRouteLocator(定义在类路径资源[org/springframework/cloud/gateway/config/GatewayAutoConfiguration.class])
↑ ↓
| routeDefinitionRouteLocator(定义在类路径资源[org/springframework/cloud/gateway/config/GatewayAutoConfiguration.class])
└─────┘解决方案建议:
尽管已允许循环引用,但仍无法打破Bean之间的依赖循环。请更新应用以移除该依赖循环。
GatewayFilter实现类
@Component public class SecurityFilter implements GatewayFilterFactory<SecurityFilter.Config> { private final UserApi userApi; public SecurityFilter(UserApi userApi) { this.userApi = userApi; } @Override public GatewayFilter apply(Config config) { return ((exchange, chain) -> { return chain.filter(exchange.mutate().request(exchange.getRequest()).build()); }); } @Override public Class<Config> getConfigClass() { return Config.class; } @Override public Config newConfig() { Config c = new Config(); return c; } public static class Config { } }
pom.xml
<properties> <java.version>17</java.version> <spring-cloud.version>2021.0.3</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-resource-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>4.2.1</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
主启动类
@SpringBootApplication @ComponentScan("com.example") @EnableFeignClients(basePackages = {"com.example.feign"}) public class GatewayApplicationMain { public static void main(String[] args) { SpringApplication.run(GatewayApplicationMain.class, args); } }
Feign API接口
@FeignClient(name = "user", path="userService/", url="http://localhost:8091/") public interface UserApi { @GetMapping("/getUserByUserName/{userName}") ResponseEntity<Object> getUserByUserName(@PathVariable(name = "userName") String userName); }
安全配置类
@Configuration public class SecurityConfig { @Bean public SecurityWebFilterChain configure(ServerHttpSecurity http) { http.csrf().disable() .authorizeExchange() .anyExchange() .authenticated() .and() .oauth2Login() .and().oauth2ResourceServer().jwt(); return http.build(); } }
application.properties配置文件
server.port=8090 spring.main.allow-circular-references=true
这个循环依赖的核心原因是:GatewayFilterFactory类型的Bean会被网关自动加载到路由定位器的依赖链中,而构造注入UserApi(Feign客户端)的方式,导致启动时Bean初始化顺序冲突,即使开启循环引用也无法解决。可以通过以下几种方式处理:
1. 延迟注入Feign客户端
给UserApi的注入添加@Lazy注解,延迟Feign客户端的初始化时机,避免在网关Bean初始化阶段就触发依赖加载:
@Component public class SecurityFilter implements GatewayFilterFactory<SecurityFilter.Config> { private final UserApi userApi; public SecurityFilter(@Lazy UserApi userApi) { this.userApi = userApi; } // 其余代码不变 }
2. 改用Setter注入
将构造注入改为Setter注入,让Spring先初始化SecurityFilter,再注入UserApi,打破循环依赖链:
@Component public class SecurityFilter implements GatewayFilterFactory<SecurityFilter.Config> { private UserApi userApi; @Autowired public void setUserApi(UserApi userApi) { this.userApi = userApi; } // 其余代码不变 }
3. 过滤器执行时动态获取Feign客户端
不在Bean初始化阶段注入UserApi,而是在过滤器实际执行时,从Spring上下文动态获取:
@Component public class SecurityFilter implements GatewayFilterFactory<SecurityFilter.Config> { private final ApplicationContext context; public SecurityFilter(ApplicationContext context) { this.context = context; } @Override public GatewayFilter apply(Config config) { return ((exchange, chain) -> { // 调用接口时再获取UserApi实例 UserApi userApi = context.getBean(UserApi.class); // 执行你的校验逻辑 return chain.filter(exchange); }); } // 其余代码不变 }
4. 使用WebClient替代Feign
Gateway是响应式架构,推荐用WebClient直接调用微服务,避免Feign客户端的Bean初始化冲突:
@Component public class SecurityFilter implements GatewayFilterFactory<SecurityFilter.Config> { private final WebClient webClient; public SecurityFilter(WebClient.Builder webClientBuilder) { this.webClient = webClientBuilder.baseUrl("http://localhost:8091/userService/").build(); } @Override public GatewayFilter apply(Config config) { return ((exchange, chain) -> { // 用WebClient调用接口 return webClient.get() .uri("/getUserByUserName/{userName}", "test") .retrieve() .bodyToMono(Object.class) .flatMap(result -> { // 处理返回结果 return chain.filter(exchange); }); }); } // 其余代码不变 }
内容的提问来源于stack exchange,提问作者kernel

