Spring Boot Jar启动偶现循环依赖异常求助(IDEA运行正常)
Hey there! I’ve run into similar circular dependency quirks between Spring config beans before—especially when switching between IDE runtimes and executable JARs. Let’s break down what’s happening, why it’s inconsistent, and how to fix it.
The Root Problem: Circular Dependency
Your error message says it loud and clear:
BeanCurrentlyInCreationException: Error creating bean with name 'getBCryptPasswordEncoder': Requested bean is currently in creation: Is there an unresolvable circular reference?
Here’s the loop in your code:
SecurityConfigneeds aPasswordEncoderbean injected via its field.- But the
PasswordEncoderbean is defined insideSecurityConfigitself (thegetBCryptPasswordEncoder()@Bean method).
Spring gets stuck trying to create SecurityConfig because it needs the PasswordEncoder first—but the PasswordEncoder can’t exist until SecurityConfig is initialized.
Why IDE vs JAR Behavior Differs
IDEA’s development runtime has looser bean initialization checks or a different classloading order that sometimes avoids triggering this circular dependency detection. When you package to a JAR, Spring’s context initialization runs in a stricter mode, so the loop gets caught every time (or intermittently, depending on bean creation order luck).
Troubleshooting Directions
- Validate Circular Dependencies: Map out all bean dependencies in your
SecurityConfigand related classes—this loop is the main culprit, but there might be others hiding. - Check Bean Initialization Order: Use Spring’s debug logs (
logging.level.org.springframework.beans=DEBUG) to see how beans are created in IDE vs JAR mode. You’ll likely see a different order that triggers the loop in the JAR. - Verify Packaging Configuration: Ensure your Maven/Gradle build is creating a proper executable fat JAR with all dependencies included (using Spring Boot’s repackage goal/plugin). Missing or misconfigured dependencies can cause odd initialization issues.
- Spring Version Alignment: Double-check that your IDE’s project dependencies match what’s in your build file. Minor version differences can change how Spring handles circular dependencies.
Fixes to Break the Loop
Here are three clean ways to resolve this:
1. Move PasswordEncoder to a Separate Config Class
The simplest fix is to extract the PasswordEncoder bean definition into its own configuration class, so it doesn’t depend on SecurityConfig to exist:
@Configuration public class EncoderConfig { @Bean public PasswordEncoder getBCryptPasswordEncoder() { return new BCryptPasswordEncoder(); } }
Now SecurityConfig can safely inject the PasswordEncoder without waiting for itself to initialize.
2. Use Constructor Injection + @Lazy
Switch from field injection to constructor injection (Spring’s recommended practice) and add @Lazy to delay the PasswordEncoder initialization until it’s actually used:
@Configuration @ComponentScan("ru.javamentor") @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { private final UserDetailsService userDetailsService; private final PasswordEncoder passwordEncoder; @Autowired public SecurityConfig(@Qualifier("userDetailServiceImpl") UserDetailsService userDetailsService, @Lazy PasswordEncoder passwordEncoder) { this.userDetailsService = userDetailsService; this.passwordEncoder = passwordEncoder; } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder); } @Bean public PasswordEncoder getBCryptPasswordEncoder() { return new BCryptPasswordEncoder(); } }
The @Lazy annotation tells Spring to hold off on creating the PasswordEncoder bean until SecurityConfig is fully initialized and the configure() method runs.
3. Instantiate PasswordEncoder Directly (Emergency Fix)
If you need a quick workaround, you can skip dependency injection and create the encoder directly in the configure() method—though this isn’t ideal for maintainability:
@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder()); }
Use this only as a temporary fix, since it bypasses Spring’s bean management.
Extra Tips
- Stick to Constructor Injection: It makes dependency issues easier to catch early and avoids hidden circular dependencies.
- Use Spring Boot Actuator: Add the Actuator dependency and hit the
/actuator/beansendpoint to visualize all bean dependencies and spot other loops. - Validate Your Build: Ensure your Maven plugin config for Spring Boot is correct to generate a valid executable JAR:
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>your-spring-boot-version</version> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
内容的提问来源于stack exchange,提问作者mrdamrin

