Skip to content

Fix: Spring Boot "The dependencies of some of the beans in the application context form a cycle"

FixDevs ·

Quick Answer

How to fix Spring Boot circular dependency errors — BeanCurrentlyInCreationException, refactoring to break cycles, @Lazy injection, setter injection, and @PostConstruct patterns.

The Error

Spring Boot fails to start with:

***************************
APPLICATION FAILED TO START
***************************

Description:

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

userService defined in file [.../UserService.class]

emailService defined in file [.../EmailService.class]

userService (field com.example.UserService com.example.EmailService.userService)

Or in older Spring versions:

org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'userService':
Requested bean is currently in creation:
Is there an unresolvable circular reference?

Or after upgrading to Spring Boot 2.6+:

Spring Boot 2.6 changed the default setting for spring.main.allow-circular-references to false.
If you need to allow for circular references, set this property to true.

Why This Happens

A circular dependency exists when Bean A requires Bean B to be created, and Bean B requires Bean A to be created — forming a loop that Spring cannot resolve through constructor injection.

Spring Boot 2.6+ disabled circular dependencies by default because they often indicate a design problem:

  • Service layer cyclesUserService depends on EmailService, and EmailService depends on UserService.
  • Controller depending on itself — a controller autowires a service that autowires the same controller.
  • Shared utility bean — two services both depend on each other through a shared operation that should be extracted into its own component.
  • Constructor injection reveals the problem — with constructor injection, circular dependencies always fail at startup. With field injection, they may work via proxy magic but still represent a design flaw.

Why this matters: Circular dependencies signal that responsibilities aren’t cleanly separated. A dependency cycle means two classes know too much about each other. Fixing the design produces more maintainable, testable code.

Fix 1: Refactor to Break the Cycle (Best Fix)

The correct fix is to restructure your code so the cycle doesn’t exist. The most common approach: extract the shared logic into a third component.

Before — circular dependency:

@Service
public class UserService {
    private final EmailService emailService;  // UserService → EmailService

    public UserService(EmailService emailService) {
        this.emailService = emailService;
    }

    public User createUser(String email) {
        User user = userRepository.save(new User(email));
        emailService.sendWelcomeEmail(user);  // UserService calls EmailService
        return user;
    }
}

@Service
public class EmailService {
    private final UserService userService;  // EmailService → UserService (CYCLE!)

    public EmailService(UserService userService) {
        this.userService = userService;
    }

    public void sendWelcomeEmail(User user) {
        String name = userService.getDisplayName(user);  // EmailService calls UserService
        // send email with name
    }
}

After — extract the shared logic:

@Service
public class UserService {
    private final EmailService emailService;

    public UserService(EmailService emailService) {
        this.emailService = emailService;
    }

    public User createUser(String email) {
        User user = userRepository.save(new User(email));
        emailService.sendWelcomeEmail(user.getEmail(), user.getDisplayName());
        return user;
    }

    public String getDisplayName(User user) {
        return user.getFirstName() + " " + user.getLastName();
    }
}

@Service
public class EmailService {
    // EmailService no longer depends on UserService
    private final JavaMailSender mailSender;

    public EmailService(JavaMailSender mailSender) {
        this.mailSender = mailSender;
    }

    // Accept the data it needs as parameters — no need to call UserService
    public void sendWelcomeEmail(String email, String displayName) {
        // send email
    }
}

Another common pattern — extract to an event or mediator:

// Instead of direct service-to-service calls, use Spring events
@Service
public class UserService {
    private final ApplicationEventPublisher eventPublisher;

    public UserService(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }

    public User createUser(String email) {
        User user = userRepository.save(new User(email));
        eventPublisher.publishEvent(new UserCreatedEvent(user));  // Decouple via event
        return user;
    }
}

@Service
public class EmailService {
    // Listens for UserCreated events — no dependency on UserService
    @EventListener
    public void handleUserCreated(UserCreatedEvent event) {
        User user = event.getUser();
        sendWelcomeEmail(user.getEmail(), user.getDisplayName());
    }
}

Fix 2: Use @Lazy to Defer Injection

@Lazy tells Spring to inject a proxy instead of the real bean at construction time. The real bean is created only when a method is first called. This breaks the construction-time cycle without redesigning the classes:

@Service
public class UserService {
    private final EmailService emailService;

    // @Lazy on one of the two injections breaks the cycle
    public UserService(@Lazy EmailService emailService) {
        this.emailService = emailService;
    }
}

@Service
public class EmailService {
    private final UserService userService;

    public EmailService(UserService userService) {
        this.userService = userService;
    }
}

Or use @Lazy on the field (with field injection):

@Service
public class UserService {

    @Lazy
    @Autowired
    private EmailService emailService;
}

Note: @Lazy is a workaround, not a design fix. Spring injects a CGLIB proxy at construction time and calls the real bean when the method is invoked. This adds a small runtime overhead and can cause confusing behavior in tests. Prefer Fix 1 (refactoring) when possible.

Fix 3: Switch from Constructor Injection to Setter Injection

Spring can resolve circular dependencies through setter injection (but not constructor injection) because with setter injection, the beans are first created (empty) and then wired together:

@Service
public class UserService {
    private EmailService emailService;

    // Setter injection — Spring creates UserService first, then injects EmailService
    @Autowired
    public void setEmailService(EmailService emailService) {
        this.emailService = emailService;
    }
}

@Service
public class EmailService {
    private UserService userService;

    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }
}

Warning: Setter injection makes dependencies optional by default — Spring won’t fail at startup if a bean is missing (it will fail only when the method is called). Use @Autowired(required = true) if the dependency is mandatory. Like @Lazy, setter injection is a workaround. Prefer refactoring.

Fix 4: Use @PostConstruct for Initialization Logic

Sometimes the cycle occurs because a bean calls another bean during its own construction. Move the initialization logic to @PostConstruct, which runs after all beans are wired:

@Service
public class CacheService {
    private final UserService userService;
    private List<User> cachedUsers;

    public CacheService(UserService userService) {
        this.userService = userService;
        // Don't call userService here — it may not be ready yet
    }

    @PostConstruct
    public void init() {
        // Called after all beans are wired — safe to call userService
        this.cachedUsers = userService.findAll();
    }
}

Fix 5: Allow Circular References (Last Resort)

Spring Boot 2.6+ blocks circular references by default. You can re-enable them as a temporary workaround while you fix the underlying design:

# application.yml — allow circular references (temporary fix only)
spring:
  main:
    allow-circular-references: true

Or in application.properties:

spring.main.allow-circular-references=true

Warning: Setting allow-circular-references=true re-enables the old behavior that Spring Boot deliberately removed. It masks the design problem and makes your application harder to reason about. Use it only as a short-term fix to keep the application running while you implement a proper structural fix. Track it as technical debt.

Fix 6: Diagnose the Full Dependency Cycle

When the cycle involves multiple beans, the error message shows the full chain. Read it carefully:

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

orderService → paymentService → notificationService → userService → orderService

This tells you the cycle is: orderService → paymentService → notificationService → userService → orderService.

Use Spring’s dependency graph to investigate:

// Add to your application — temporarily log all bean dependencies
@Configuration
public class BeanInspector implements ApplicationContextAware {

    @Override
    public void setApplicationContext(ApplicationContext ctx) throws BeansException {
        ConfigurableApplicationContext context = (ConfigurableApplicationContext) ctx;
        BeanFactory factory = context.getBeanFactory();

        if (factory instanceof DefaultListableBeanFactory) {
            DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) factory;
            for (String name : beanFactory.getBeanDefinitionNames()) {
                BeanDefinition def = beanFactory.getBeanDefinition(name);
                String[] deps = def.getDependsOn();
                if (deps != null) {
                    System.out.println(name + " depends on: " + Arrays.toString(deps));
                }
            }
        }
    }
}

Enable debug logging to see Spring’s bean creation order:

logging:
  level:
    org.springframework.beans.factory: DEBUG

Still Not Working?

Check if the cycle is in test configuration. Spring test contexts can introduce cycles that don’t exist in production if test configuration beans add extra dependencies:

@TestConfiguration
public class TestConfig {
    // Beans defined here can create cycles with production beans
}

Check if @Component, @Service, or @Repository is accidentally applied to a class that should not be a Spring bean. An unexpected component scan can pull in a class that creates a cycle.

Check for @Configuration class cycles. Circular dependencies between @Configuration classes (where one @Bean method calls another @Configuration class’s @Bean method) also cause this error:

@Configuration
public class ConfigA {
    @Bean
    public BeanA beanA(BeanB beanB) { return new BeanA(beanB); }  // Requires BeanB
}

@Configuration
public class ConfigB {
    @Bean
    public BeanB beanB(BeanA beanA) { return new BeanB(beanA); }  // Requires BeanA → CYCLE
}

Fix: inject by method reference instead of by parameter, or merge related configuration into a single @Configuration class.

For related Spring Boot issues, see Fix: Spring Boot DataSource Failed and Fix: Spring Security Returning 403 Forbidden.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles