spring-bootspring-securityspring-cloud-gatewayspring-cloud-feignopenfeign

Feign Client with Spring Cloud GatewayFilter cause cycle dependency


During certain checks, I need to make a request to my Microservice in the Gateway Filter. When I define the Feign class in the GatewayFilter(my SecurityFilter.java) class, it gives the following error. How can I resolve this situation.

Error:

Description:

The dependencies of some of the beans in the application context form a cycle:

┌─────┐
|  securityFilter defined in file [/target/classes/com/example/SecurityFilter.class]
↑     ↓
|  cachedCompositeRouteLocator defined in class path resource [org/springframework/cloud/gateway/config/GatewayAutoConfiguration.class]
↑     ↓
|  routeDefinitionRouteLocator defined in class path resource [org/springframework/cloud/gateway/config/GatewayAutoConfiguration.class]
└─────┘


Action:

Despite circular references being allowed, the dependency cycle between beans could not be broken. Update your application to remove the dependency cycle.

GatewayFilter class

@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>

Main class

@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 class

@FeignClient(name = "user", path="userService/", url="http://localhost:8091/")
public interface UserApi {
    @GetMapping("/getUserByUserName/{userName}")
    ResponseEntity<Object> getUserByUserName(@PathVariable(name = "userName") String userName);
}

Security class

@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 file

server.port=8090
spring.main.allow-circular-references=true

Solution

  • private final UserApi userApi;
    
    @Autowired
    public SecurityFilter(@Lazy UserApi userApi) {
       this.userApi = userApi;
    }
    

    This is how I was able to fix the problem by adding @Lazy.