Backend Development 12 min read

Why the @Async Annotation Triggers BeanCurrentlyInCreationException in Spring Circular Dependencies

This article explains how Spring's @Async annotation interacts with circular dependencies, why it leads to BeanCurrentlyInCreationException, and provides detailed insights into AsyncAnnotationBeanPostProcessor, AOP proxy creation, the three‑level cache mechanism, and practical solutions to avoid the issue.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Why the @Async Annotation Triggers BeanCurrentlyInCreationException in Spring Circular Dependencies

When a colleague encountered a BeanCurrentlyInCreationException after adding @Async to a service method, the author reproduced the problem with two mutually dependent services (AService and BService) where AService.save() is annotated with @Async .

The exception occurs because Spring's circular‑dependency resolution relies on early bean exposure via a three‑level cache, but the @Async processing creates a proxy after the early reference has been exposed, causing the final bean instance to differ from the early one.

@Component public class AService { @Resource private BService bService; @Async public void save() { // ... } } @Component public class BService { @Resource private AService aService; }

The @Async functionality is implemented by AsyncAnnotationBeanPostProcessor , which is registered via @EnableAsync . This post‑processor runs in postProcessAfterInitialization , wrapping beans that have @Async‑annotated methods with a dynamic proxy.

In contrast, general AOP and @Transactional proxies are created by AnnotationAwareAspectJAutoProxyCreator , which also implements BeanPostProcessor and participates in early bean exposure through the SmartInstantiationAwareBeanPostProcessor interface.

During circular‑dependency resolution, Spring first obtains an early reference of AService via AnnotationAwareAspectJAutoProxyCreator.getEarlyBeanReference . Because AsyncAnnotationBeanPostProcessor does not implement SmartInstantiationAwareBeanPostProcessor , it does not affect the early reference. After both beans are instantiated, AsyncAnnotationBeanPostProcessor creates a proxy for AService, making the final bean different from the early reference, which triggers the BeanCurrentlyInCreationException .

Key code that throws the exception:

if (earlySingletonExposure) {
    Object earlySingletonReference = getSingleton(beanName, false);
    if (earlySingletonReference != null) {
        if (exposedObject == bean) {
            exposedObject = earlySingletonReference;
        } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
            // throw BeanCurrentlyInCreationException
        }
    }
}

Solutions include:

Refactor the design to eliminate circular dependencies.

Remove @Async and use a manual thread pool.

Mark one of the injected fields with @Lazy to defer resolution.

Set allowRawInjectionDespiteWrapping to true via a custom BeanFactoryPostProcessor , though this is not recommended.

By understanding the distinct proxy mechanisms of AsyncAnnotationBeanPostProcessor and AnnotationAwareAspectJAutoProxyCreator , developers can avoid the incompatibility between @Async and circular dependencies.

backendjavaSpringasyncCircular DependencyBeanPostProcessor
Code Ape Tech Column
Written by

Code Ape Tech Column

Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn

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.