openrewrite / openrewrite/rewrite-spring

Recipe to address `unresolvable circular reference` upgrading to Spring Boot 2.7.18

Open
#668 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

boot-2.7 enhancement recipe spring-security
Dominant language
Java
Stars
403
Forks
149
Avg merge
2h 33m
Merged PRs (30d)
10

Description

What problem are you trying to solve?

Improve the Spring Security recipe that updates the class that extends WebSecurityConfigurerAdapter.

Sample application

The sample application is the spring-boot-blog-app. This is a fork of skarware/spring-boot-blog-app. The OpenRewrite maven plugin is defined in the pom.xml.

Branch before the upgrade:

https://github.com/nickdala/spring-boot-blog-app-java-copilot-upgrade-demo/tree/openrewrite-circular-reference-before

Branch after the upgrade using OpenRewrite

https://github.com/nickdala/spring-boot-blog-app-java-copilot-upgrade-demo/tree/openrewrite-circular-reference-after

What precondition(s) should be checked before applying this recipe?

A PasswordEncoder bean like the following defined in the class that extends WebSecurityConfigurerAdapter.

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
  @Bean
  public BCryptPasswordEncoder bcryptEncoder() {
      return new BCryptPasswordEncoder();
  }
}
Steps to reproduce
  1. Clone the repo spring-boot-blog-app-java-copilot-upgrade-demo
git clone https://github.com/nickdala/spring-boot-blog-app-java-copilot-upgrade-demo
cd spring-boot-blog-app-java-copilot-upgrade-demo
  1. (Optional) Open the project with VS Code and start the Dev Container. Docker needs to be running.
code .
  1. Check out the branch openrewrite-circular-reference-before
git checkout openrewrite-circular-reference-before
  1. Upgrade using maven
./mvnw rewrite:run

or

mvnw.cmd rewrite:run
  1. (Optional) Use Java 21. Below is an example of installing Java 21 using SDKMAN!. This is not optional if you're using the dev container.
sdk install java 21.0.5-tem
  1. Build and run the application
./mvnw clean package

./mvnw spring-boot:run

You will see the following error.

[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  11.282 s
[INFO] Finished at: 2025-01-15T12:39:31-05:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:2.7.18:run (default-cli) on project spring-boot-blog-app: Application finished with exit code: 1 -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'webSecurityConfig': Requested bean is currently in creation: Is there an unresolvable circular reference?
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:355) ~[spring-beans-5.3.31.jar:5.3.31]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:227) ~[spring-beans-5.3.31.jar:5.3.31]

Describe the situation after applying the recipe

Step 1:

Move the method bcryptEncoder() in WebSecurityConfig.java to a separate class to create the BCryptPasswordEncoder instance.

@Configuration
public class PasswordEncoderConfig {
    @Bean
    BCryptPasswordEncoder bcryptEncoder() {
        return new BCryptPasswordEncoder();
    }   
}
Step 2:

Pass the BCryptPasswordEncoder to the configureGlobal() method in WebSecurityConfig.

@Autowired
    public void configureGlobal(AuthenticationManagerBuilder authenticationManagerBuilder, BCryptPasswordEncoder passwordEncoder) throws Exception {

        authenticationManagerBuilder
                .jdbcAuthentication()
                .usersByUsernameQuery(USERS_SQL_QUERY) // not really necessary, as users table follows default Spring Security User schema
                .authoritiesByUsernameQuery(AUTHORITIES_SQL_QUERY)  // a must as using customized authorities table, many to many variation
                .dataSource(dataSource)
                .passwordEncoder(passwordEncoder);
}

The complete code is in the branch openrewrite-circular-reference-after

Are you interested in [contributing this recipe to OpenRewrite]

Yes. I'm interested in collaborating with the community to verify the recipe and to ensure that we're accounting for any edge cases. Once that's done, I can lead the coding effort.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the Spring Security recipe that updates the class extending WebSecurityConfigurerAdapter, then reproduce the upgrade using the sample pom.xml and the openrewrite-circular-reference-before branch with ./mvnw rewrite:run. Compare WebSecurityConfig.java with the openrewrite-circular-reference-after branch and verify that the upgraded application builds and runs without the unresolvable circular-reference error, including the PasswordEncoder case.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring-boot
Domain
backend, tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.