Mastering Spring’s @Autowired: Advanced Usage, Qualifiers, and Common Pitfalls

This article explores the default behavior of Spring's @Autowired annotation, how to resolve bean name conflicts, use @Qualifier and @Primary, apply @Autowired on constructors, methods, parameters, and collections, and troubleshoot common issues such as missing annotations, filter injection, component scanning, and circular dependencies.

macrozheng
macrozheng
macrozheng
Mastering Spring’s @Autowired: Advanced Usage, Qualifiers, and Common Pitfalls

Preface

While reviewing other developers' code I discovered several interesting usages of the @Autowired annotation in Spring, which prompted a deeper investigation and a comprehensive guide.

1. Default Autowiring

In Spring, @Autowired performs automatic injection based on type ( byType) by default. The required attribute is true, meaning injection is mandatory unless set to false.

package com.sue.cache.service;

import org.springframework.stereotype.Service;

@Service
public class TestService1 {
    public void test1() {}
}

package com.sue.cache.service;

import org.springframework.stereotype.Service;

@Service
public class TestService2 {
    @Autowired
    private TestService1 testService1;

    public void test2() {}
}

2. Multiple Beans of the Same Type

When more than one bean of the same type exists, Spring cannot decide which one to inject, leading to ConflictingBeanDefinitionException. This occurs because Spring derives bean names from class names (e.g., testService1) and bean names must be unique.

package com.sue.cache.service.test;

import org.springframework.stereotype.Service;

@Service
public class TestService1 {
    public void test1() {}
}

Attempting to start the application results in a conflict error.

Spring does not allow two beans with the same name; the first letter of the class name is used as the default bean name.

One way to create two beans of the same type is to define them manually in a configuration class and remove the @Service annotation from the class.

public class TestService1 {
    public void test1() {}
}

@Configuration
public class TestConfig {
    @Bean("test1")
    public TestService1 test1() { return new TestService1(); }

    @Bean("test2")
    public TestService1 test2() { return new TestService1(); }
}

3. @Qualifier and @Primary

When multiple candidates exist, you can switch from byType to byName by using @Qualifier to specify the bean name.

@Service
public class UserService {
    @Autowired
    @Qualifier("user1")
    private IUser user;
}

Alternatively, mark one bean with @Primary so that it is chosen automatically.

@Primary
@Service
public class User1 implements IUser {
    @Override
    public void say() {}
}
@Qualifier works together with @Autowired to select a bean by its name.

4. Scope of @Autowired

The annotation can be placed on five target types: fields, constructors, methods, parameters, and other annotations.

4.1 Field

@Service
public class UserService {
    @Autowired
    private IUser user;
}

4.2 Constructor

@Service
public class UserService {
    private IUser user;
    @Autowired
    public UserService(IUser user) {
        this.user = user;
        System.out.println("user:" + user);
    }
}
Adding @Autowired on a constructor still uses Spring’s autowiring mechanism, not pure constructor injection.

4.3 Method

@Service
public class UserService {
    @Autowired
    public void test(IUser user) {
        user.say();
    }
}

Spring invokes such methods once during startup, which can be used for initialization.

4.4 Parameter

@Service
public class UserService {
    @Autowired
    public UserService(@Autowired IUser user) {
        this.user = user;
        System.out.println("user:" + user);
    }
}

4.5 Annotation

Other annotation targets are rarely used and omitted here.

5. Advanced @Autowired

@Autowired can also inject collections of beans of the same type.

@Service
public class UserService {
    @Autowired
    private List<IUser> userList;
    @Autowired
    private Set<IUser> userSet;
    @Autowired
    private Map<String, IUser> userMap;

    public void test() {
        System.out.println("userList:" + userList);
        System.out.println("userSet:" + userSet);
        System.out.println("userMap:" + userMap);
    }
}

Calling an endpoint that triggers test() prints all injected beans, demonstrating that Spring aggregates them automatically.

6. Does @Autowired Always Succeed?

6.1 Missing Stereotype Annotation

If a class lacks @Component, @Service, @Controller, etc., Spring will not manage it, and @Autowired will have no effect.

public class UserService {
    @Autowired
    private IUser user;
}

6.2 Injection in Filters or Listeners

Filters and listeners are initialized before Spring’s MVC dispatcher, so beans are not yet available for injection, causing startup failures.

To inject a bean in a filter, obtain the application context manually:
public class UserFilter implements Filter {
    private IUser user;
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(filterConfig.getServletContext());
        this.user = (IUser) ctx.getBean("user1");
        user.say();
    }
}

6.3 Component Scan Misses the Bean

If @ComponentScan does not cover the package containing a bean, Spring cannot discover it, and @Autowired will fail. Using @SpringBootApplication includes a default component scan.

6.4 Circular Dependencies

Spring can resolve circular dependencies for singleton beans via setter injection, but prototype beans or proxy‑based beans may still cause failures.

7. @Autowired vs. @Resource

Both perform dependency injection, but they differ in semantics and usage:

@Autowired defaults to byType injection; @Resource defaults to byName.

@Autowired has a single required attribute; @Resource offers name and type among other attributes.

To achieve name‑based injection with @Autowired you need @Qualifier; @Resource can use the name attribute directly.

@Autowired can be applied to constructors, methods, parameters, fields, and other annotations; @Resource works on classes, fields, and methods.

@Autowired is Spring‑specific, while @Resource is defined by JSR‑250 and supported by many frameworks.

The injection order for @Autowired differs from that of @Resource, as illustrated by the accompanying diagrams.
Autowired injection order
Autowired injection order
Resource injection order
Resource injection order
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BackendjavaspringSpring Bootdependency-injectionAutowired
macrozheng
Written by

macrozheng

Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.

0 followers
Reader feedback

How this landed with the community

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.