Backend Development 11 min read

Common Pitfalls of @Autowired in Spring: Circular Dependencies and Bean Name Conflicts

This article explains the core concepts of Spring's @Autowired dependency injection, illustrates common mistakes such as unresolved circular dependencies and bean name collisions, and provides detailed solutions including constructor injection, qualifier usage, and bean priority annotations.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Common Pitfalls of @Autowired in Spring: Circular Dependencies and Bean Name Conflicts

Spring's Dependency Injection (DI) is a fundamental concept of the Spring framework that manages and decouples component dependencies. The @Autowired annotation simplifies injection but can cause problems when misused.

The annotation can be applied to fields, constructors, or methods, each representing a different injection scenario:

Field injection : @Autowired on a field lets Spring automatically find and inject a matching bean.

Constructor injection : @Autowired on a constructor is considered the best practice because it guarantees that all required dependencies are provided at object creation.

Method injection : @Autowired on a method lets Spring call the method to inject the required bean.

Improper use of @Autowired often leads to two major issues:

1. Unresolved Circular Dependencies

When two beans depend on each other via constructor injection, Spring cannot resolve the cycle because the bean instance is not fully created when the constructor is invoked. The following example demonstrates a circular dependency between UserManageService and UserService :

@Slf4j
@Service
public class UserManageService {
    public UserService userService;
    @Autowired
    public UserManageService(UserService userService) {
        this.userService = userService;
    }
}
@Service
@Slf4j
public class UserService {
    private UserManageService userManageService;
    @Autowired
    public UserService(UserManageService userManageService) {
        this.userManageService = userManageService;
    }
}

Running this code produces a bean creation error indicating a cycle:

The dependencies of some of the beans in the application context form a cycle:
┌─────┐
|  userManageService defined in file UserManageService.class]
↑     ↓
|  userService defined in file UserService.class]
└─────┘

Spring resolves most circular dependencies by exposing a partially constructed bean as a "early reference" using a three‑level cache (singletonObjects, earlySingletonObjects, singletonFactories). However, this mechanism works only for property (field) injection; constructor injection cannot be satisfied because the bean instance does not exist yet.

2. Bean Name Conflicts

If the container contains multiple candidates for a required type, @Autowired fails with the message "required a single bean, but 2 were found". This situation can be handled by:

Using @Qualifier to specify the exact bean name.

Marking a bean with @Primary or @Priority to give it higher precedence.

Ensuring only one bean of the required type is defined.

The actual injection work is performed by AutowiredAnnotationBeanPostProcessor . During the populateBean phase, it iterates over all BeanPostProcessor instances and calls postProcessProperties , which in turn finds injection metadata and invokes the inject method on each InjectedElement (field or method).

protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    for (InjectedElement element : elementsToIterate) {
        element.inject(bean, beanName, pvs);
    }
}

For field injection, the element resolves the dependency via beanFactory.resolveDependency , applies any @Qualifier or @Primary logic, and finally sets the field value via reflection.

protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    Field field = (Field) this.member;
    Object value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
    if (value != null) {
        ReflectionUtils.makeAccessible(field);
        field.set(bean, value);
    }
}

Understanding these mechanisms helps avoid the two common pitfalls described above.

Conclusion

The @Autowired annotation is one of the most frequently used features in Spring development, but incorrect usage can lead to circular dependency errors and bean ambiguity. By preferring constructor injection, applying @Qualifier , @Primary , or @Priority where appropriate, and being aware of Spring's three‑level cache for early bean exposure, developers can write more robust and maintainable code.

BackendJavaSpringdependency injectionAutowiredBeanCircular Dependency
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

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.