Backend Development 15 min read

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.

<code>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() {}
}
</code>

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.

<code>package com.sue.cache.service.test;

import org.springframework.stereotype.Service;

@Service
public class TestService1 {
    public void test1() {}
}
</code>

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.

<code>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(); }
}
</code>

3. @Qualifier and @Primary

When multiple candidates exist, you can switch from

byType

to

byName

by using

@Qualifier

to specify the bean name.

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

Alternatively, mark one bean with

@Primary

so that it is chosen automatically.

<code>@Primary
@Service
public class User1 implements IUser {
    @Override
    public void say() {}
}
</code>
@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

<code>@Service
public class UserService {
    @Autowired
    private IUser user;
}
</code>

4.2 Constructor

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

4.3 Method

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

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

4.4 Parameter

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

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.

<code>@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);
    }
}
</code>

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.

<code>public class UserService {
    @Autowired
    private IUser user;
}
</code>

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:
<code>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();
    }
}
</code>

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
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

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.