Unlock the Full Power of Spring’s @Autowired: Advanced Techniques and Common Pitfalls
This article explores Spring’s @Autowired annotation in depth, covering default wiring, handling multiple beans, using @Qualifier and @Primary, various injection targets, advanced collection injection, common pitfalls, and differences from @Resource, providing practical code examples and solutions for real‑world projects.
Introduction
While reviewing other developers' code I noticed several unconventional uses of
@Autowired. Curious, I investigated and discovered many powerful features that go beyond the typical field injection.
1. Default Autowiring
In Spring,
@Autowiredperforms automatic injection, usually by type (
byType). The
requiredattribute defaults to
true, enabling injection; set it to
falseto disable.
<code>package com.sue.cache.service;
import org.springframework.stereotype.Service;
@Service
public class TestService1 {
public void test1() {}
}
</code> <code>package com.sue.cache.service;
import org.springframework.stereotype.Service;
@Service
public class TestService2 {
@Autowired
private TestService1 testService1;
public void test2() {}
}
</code>Because Spring defaults to
byType, the above works.
2. Multiple Beans of the Same Type
If more than one bean of the same type exists,
byTypecannot decide which one to inject, leading to conflicts.
Creating a duplicate class
TestService1in a different package causes a
ConflictingBeanDefinitionExceptionat startup.
Spring derives bean names from class names (lower‑casing the first letter). Duplicate names cause bean definition conflicts.
To deliberately create two beans of the same type, you can define them in a @Configuration class:
<code>public class TestConfig {
@Bean("test1")
public TestService1 test1() { return new TestService1(); }
@Bean("test2")
public TestService1 test2() { return new TestService1(); }
}
</code>Starting the application still fails because the bean name
testService1is duplicated.
Another scenario is having two implementations of an interface:
<code>public interface IUser { void say(); }
</code> <code>@Service
public class User1 implements IUser { @Override public void say() {} }
</code> <code>@Service
public class User2 implements IUser { @Override public void say() {} }
</code> <code>@Service
public class UserService {
@Autowired
private IUser user;
}
</code>Spring cannot decide which implementation to inject.
3. @Qualifier and @Primary
When
byTypeis insufficient, you can switch to name‑based injection (
byName) using
@Qualifier:
<code>@Service
public class UserService {
@Autowired
@Qualifier("user1")
private IUser user;
}
</code>Alternatively, mark one implementation with
@Primaryso that it is chosen automatically:
<code>@Primary
@Service
public class User1 implements IUser { @Override public void say() {} }
</code>Removing
@Qualifierfrom the consumer then works because Spring picks the primary bean.
When multiple candidates exist, the bean annotated with @Primary is selected.
4. Injection Targets
@Autowiredcan be placed on five different elements.
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>Annotating the constructor still uses Spring’s autowiring mechanism; it does not become a 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.
<code>@Service
public class UserService {
private IUser user;
@Autowired
public void setUser(IUser user) { this.user = user; }
}
</code>4.4 Parameter
<code>@Service
public class UserService {
@Autowired
public UserService(@Autowired IUser user) { this.user = user; }
}
</code> <code>@Service
public class UserService {
public void test(@Autowired IUser user) { user.say(); }
}
</code>4.5 Annotation (rarely used)
Other annotations can also be meta‑annotated with
@Autowired, but this is uncommon.
5. Advanced Collection Injection
@Autowiredcan inject all beans of a given type into collections:
<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 the corresponding endpoint prints two elements in each collection, confirming that Spring aggregates all matching beans.
6. When @Autowired Fails
6.1 Missing Stereotype Annotation
If a class lacks @Component, @Service, @Controller, @Repository, etc., Spring cannot create the bean, and injection will be null.
6.2 Autowiring in Filters or Listeners
Filters and listeners are initialized before Spring’s DispatcherServlet, so beans are not yet available. Attempting to @Autowired inside them causes startup failures.
Solution: obtain the ApplicationContext manually:
<code>public class UserFilter implements Filter {
private IUser user;
@Override
public void init(FilterConfig filterConfig) {
ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(filterConfig.getServletContext());
this.user = (IUser) ctx.getBean("user1");
user.say();
}
// other methods omitted
}
</code>6.3 Component Scan Missed
If @ComponentScan (or the implicit scan from @SpringBootApplication) does not cover a package, annotated classes are ignored and cannot be autowired.
6.4 Circular Dependencies
Spring can resolve most circular dependencies for singleton beans, but prototype beans or beans created via proxies may still cause failures.
7. Difference Between @Autowired and @Resource
@Autowired defaults to by‑type injection; @Resource defaults to by‑name.
@Autowired has a single
requiredattribute; @Resource offers
nameand
typeamong others.
To achieve by‑name with @Autowired you need @Qualifier; @Resource uses
namedirectly.
@Autowired can be used on constructors, methods, parameters, fields, and annotations; @Resource works on classes, fields, and methods.
@Autowired is Spring‑specific; @Resource is a JSR‑250 standard.
Injection order also differs: @Autowired follows a by‑type resolution flow, while @Resource follows a name‑first, then type‑first strategy.
Conclusion
Understanding the nuances of
@Autowired—its default behavior, how to handle multiple candidates, the role of @Qualifier and @Primary, various injection points, collection injection, common failure scenarios, and its distinction from @Resource—enables more robust and maintainable Spring applications.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.