How to Resolve Common Spring Boot Configuration Pitfalls and Circular Dependency Errors
This article explains why @Configuration classes can cause circular dependency and custom BeanPostProcessor issues in Spring Boot, and provides practical solutions such as enabling circular references, using static @Bean methods, and preferring constructor injection for reliable bean injection.
Environment: SpringBoot 3.3.0
1. Introduction
In Spring Boot, the @Configuration annotation declares a configuration class that can define and register Bean objects, including special handlers such as BeanPostProcessor or BeanFactoryPostProcessor. Incorrect configurations can cause various problems, which are described below.
2. Practical Cases
2.1 Circular Dependency Error
When a @PostConstruct method in a configuration class calls another bean, a circular dependency occurs. Example:
<code>@Configuration
public class AppConfig {
@PostConstruct
public void init() {
dao();
System.out.println("AppConfig init...");
}
@Bean
DAO dao() {
return new DAO();
}
}
</code>The application fails to start and shows an error because the @Bean method is non‑static and requires a fully initialized configuration instance.
Solution 1: Enable circular references (not recommended for new projects).
<code>spring:
main:
allow-circular-references: true
</code>Solution 2: Declare the bean method as static, which removes the need for the configuration class to be fully initialized.
<code>@Bean
public static DAO dao() {
return new DAO();
}
</code>Static methods are the recommended approach.
2.2 Custom Processor Error
Defining a BeanPostProcessor or BeanFactoryPostProcessor with @Bean can prevent @Value, @Autowired, or @Resource injections from working because the default processor’s priority is lower than the custom one.
<code>@Configuration
public class AppConfig {
@Value("${pack.title}")
private String title;
}
</code>After registering a custom BeanPostProcessor, the injected value becomes null.
Solution 1: Implement ApplicationContextInitializer to add the processor manually (complex).
<code>public class PackApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext context) {
context.getBeanFactory().addBeanPostProcessor(new PackBeanPostProcessor());
}
}
</code>Register via META‑INF/spring.factories.
Solution 2: Declare the @Bean method as static, so the container can obtain the processor without instantiating the configuration class first.
<code>@Bean
public static PackBeanPostProcessor packBeanPostProcessor() {
return new PackBeanPostProcessor();
}
</code>For @Configuration classes that need injected beans, constructor injection is recommended.
<code>@Configuration
public class AppConfig {
private final Person person;
public AppConfig(Person person) {
this.person = person;
}
}
</code>Constructor injection works reliably in all scenarios.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.