Backend Development 6 min read

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.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
How to Resolve Common Spring Boot Configuration Pitfalls and Circular Dependency Errors

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.

JavaconfigurationSpring BootBeanCircular DependencyBeanPostProcessorstatic-bean
Spring Full-Stack Practical Cases
Written by

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.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.