Skip to content

Circular reference error with RestTemplateBuilder and getDataSource Bean #35217

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
joshua-phillips opened this issue May 1, 2023 · 5 comments
Labels
status: duplicate A duplicate of another issue type: bug A general bug

Comments

@joshua-phillips
Copy link

joshua-phillips commented May 1, 2023

Attempted to upgrade from v2.6.13 to 2.7.11 recently due to some security issues requiring Spring Framework upgrade. Trying to run the application was resulting in a circular reference failure.

Description:

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

   webMvcMetricsFilter defined in class path resource [org/springframework/boot/actuate/autoconfigure/metrics/web/servlet/WebMvcMetricsAutoConfiguration.class]
???????
|  simpleMeterRegistry defined in class path resource [org/springframework/boot/actuate/autoconfigure/metrics/export/simple/SimpleMetricsExportAutoConfiguration.class]
?     ?
|  dataSourcePoolMetadataMeterBinder defined in class path resource [org/springframework/boot/actuate/autoconfigure/metrics/jdbc/DataSourcePoolMetricsAutoConfiguration$DataSourcePoolMetadataMetricsConfiguration.class]
?     ?
|  getDataSource defined in com.example.demo.DemoApplication
?     ?
|  getRestTemplate defined in com.example.demo.DemoApplication
?     ?
|  restTemplateBuilder defined in class path resource [org/springframework/boot/autoconfigure/web/client/RestTemplateAutoConfiguration.class]
?     ?
|  restTemplateBuilderConfigurer defined in class path resource [org/springframework/boot/autoconfigure/web/client/RestTemplateAutoConfiguration.class]
?     ?
|  metricsRestTemplateCustomizer defined in class path resource [org/springframework/boot/actuate/autoconfigure/metrics/web/client/RestTemplateMetricsConfiguration.class]
???????


Action:

Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.

Created a new application from Spring Initilizr using Maven, Java, v2.7.11, JAR, JDK 17 to try and determine if it was anything custom in my existing application, and was able to reproduce the issue with very minimal changes and dependencies.

The error started occurring after adding the spring-boot-starter-actuator and spring-boot-starter-data-jpa dependencies and injecting the RestTemplateBuilder class so that it is part of the getDataSource Bean dependencies. The same error happens if injecting the RestTemplateBuilder directly to the getDataSource Bean rather than indirectly through the getRestTemplate Bean.

    @Bean
    public RestTemplate getRestTemplate(RestTemplateBuilder builder) {
        return builder.build();
    }

    @Bean
    public DataSource getDataSource(RestTemplate restTemplate) {
        DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
        dataSourceBuilder.driverClassName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
        dataSourceBuilder.url("<DB URL>");
        dataSourceBuilder.username("<DB USER NAME>");
        dataSourceBuilder.password("DB PASSWORD");
        return dataSourceBuilder.build();
    }

The only work-around I found is to use the spring.main.allow-circular-references=true setting in the application.properties file.

Example application: https://github.com/joshua-phillips/spring-boot-2-7-x-circular-reference-example

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label May 1, 2023
@wilkinsona
Copy link
Member

Why are you injecting RestTemplate into a @Bean method that creates a DataSource?

@wilkinsona wilkinsona added the status: waiting-for-feedback We need additional information before we can continue label May 1, 2023
@joshua-phillips
Copy link
Author

joshua-phillips commented May 1, 2023

@wilkinsona we retrieve the data source values as well as other secret data from Hashicorp Vault using their REST API. Just as a quick test, I checked if it is just the fact that RestTemplateBuilder is being instanced prior to the getDataSource Bean method regardless of whether it is directly injected into getDataSource and it is the same circular reference failure.

So I can't even do something like this where the getVaultData Bean method gets the RestTemplate instance to retrieve the data and then the VaultData instance is injected to getDataSource

    @Bean
    public RestTemplate getRestTemplate(RestTemplateBuilder builder) {
        return builder.build();
    }

    @Bean
    public VaultData getVaultData(RestTemplate restTemplate) {
        // perform HC Vault REST API calls here to get secret data
        return new VaultData();
    }

    @Bean
    public DataSource getDataSource(VaultData vaultData) {
        DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
        dataSourceBuilder.driverClassName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
        dataSourceBuilder.url(vaultData.getUrl());
        dataSourceBuilder.username(vaultData.getUsername());
        dataSourceBuilder.password(vaultData.getPassword());
        return dataSourceBuilder.build();
    }

For my test VaultData is just a hard-coded class

public class VaultData {
    public String getUrl() {
        return "<DB URL>";
    }

    public String getUsername() {
        return "<DB USER NAME>";
    }

    public String getPassword() {
        return "<DB PASSWORD>";
    }
}

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels May 1, 2023
@wilkinsona
Copy link
Member

Thanks for the additional information.

This has been fixed in 3.0 by the changes made for #30636. Unfortunately, we don't feel that we can make those changes in a maintenance release so they're only available in 3.0 and later.

In the meantime, you can avoid the problem by creating your own RestTemplate to make the Vault calls. That can either be through directly creating a new RestTemplate instance or by using a new RestTemplateBuilder to create the RestTemplate.

@wilkinsona wilkinsona closed this as not planned Won't fix, can't repro, duplicate, stale May 2, 2023
@wilkinsona wilkinsona added type: bug A general bug status: duplicate A duplicate of another issue and removed status: waiting-for-triage An issue we've not yet triaged status: feedback-provided Feedback has been provided labels May 2, 2023
@joshua-phillips
Copy link
Author

@wilkinsona thanks for the info and options! That will hopefully allow us to remove the spring.main.allow-circular-references=true setting :)

@nightswimmings
Copy link

nightswimmings commented Jul 20, 2023

The problem of not using RestTemplateBuilder and a custom RestTemplate instead is that you lose Observability autoconfiguration

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: duplicate A duplicate of another issue type: bug A general bug
Projects
None yet
Development

No branches or pull requests

4 participants