Unveiling Spring’s @Autowired vs @Resource: Deep Dive into Injection Mechanics
This article explains the fundamental differences between Spring's @Autowired and @Resource annotations, walks through their underlying source code, and demonstrates how Spring resolves dependencies by type or by name, complete with annotated code examples and processor internals.
Environment: Spring 5.3.23
1. Introduction
Both @Autowired and @Resource are used for dependency injection in Spring, but they follow different resolution strategies.
@Autowired
@Autowired is a Spring-provided annotation that injects dependencies primarily by type (byType). If multiple beans match the required type, Spring falls back to matching by bean name.
@Resource
@Resource comes from Jakarta EE and injects dependencies by name (byName) by default. If a name is not specified, it falls back to type‑based injection, and if neither name nor type is provided it attempts a reflective name‑based injection.
2. Source Code Analysis
Example code used for the demonstration:
<code>static interface DAO {}
@Priority(1)
static class PersonDAO implements DAO {}
@Priority(2)
static class StudentDAO implements DAO {}
static class Service {
@Resource
// @Autowired
private DAO dao;
@Override
public String toString() {
return "Service [dao=" + dao + "]";
}
}
</code>2.1 @Resource Processing
The core processor for @Resource is CommonAnnotationBeanPostProcessor , which participates in the bean post‑processing phase to populate fields.
<code>public abstract class AbstractAutowireCapableBeanFactory {
protected void populateBean(...) {
// @Resource handling is delegated to CommonAnnotationBeanPostProcessor
for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
}
}
}
</code>The processor invokes CommonAnnotationBeanPostProcessor#postProcessProperties to handle the injection.
<code>public class CommonAnnotationBeanPostProcessor {
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
try {
metadata.inject(bean, beanName, pvs);
} catch (Throwable ex) {
// handle exception
}
return pvs;
}
}
</code>The InjectionMetadata class holds information about all fields that need injection.
<code>public class InjectionMetadata {
private final Collection<InjectedElement> injectedElements;
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
for (InjectedElement element : injectedElements) {
element.inject(target, beanName, pvs);
}
}
}
</code>For @Resource, the concrete InjectedElement implementation is ResourceElement , which obtains the actual bean to inject via getResourceToInject .
<code>private class ResourceElement extends LookupElement {
protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
// Lazy handling omitted for brevity
return getResource(this, requestingBeanName);
}
}
</code>The CommonAnnotationBeanPostProcessor#getResource method ultimately delegates to the bean factory to resolve the dependency, first trying name‑based lookup and then falling back to type‑based resolution, considering @Primary and @Priority annotations when multiple candidates exist.
<code>public class CommonAnnotationBeanPostProcessor {
private boolean fallbackToDefaultTypeMatch = true;
protected Object getResource(...) {
return autowireResource(this.resourceFactory, element, requestingBeanName);
}
protected Object autowireResource(BeanFactory factory, LookupElement element, ...) {
String name = element.name;
if (factory instanceof AutowireCapableBeanFactory) {
AutowireCapableBeanFactory beanFactory = (AutowireCapableBeanFactory) factory;
DependencyDescriptor descriptor = element.getDependencyDescriptor();
if (fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) {
// resolve by type, then @Primary / @Priority
Object resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);
if (resource == null) {
throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");
}
return resource;
}
}
return null;
}
}
</code>2.2 @Autowired Processing
The core processor for @Autowired is AutowiredAnnotationBeanPostProcessor , which also uses InjectionMetadata to perform field injection.
<code>public class AutowiredAnnotationBeanPostProcessor {
// similar structure to CommonAnnotationBeanPostProcessor
}
</code>During injection, AutowiredFieldElement#resolveFieldValue calls the bean factory’s resolveDependency method.
<code>private class AutowiredFieldElement {
private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {
Object value;
try {
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
} catch (Throwable ex) {
// handle exception
}
return value;
}
}
</code>The DefaultListableBeanFactory#resolveDependency method ultimately determines the appropriate bean, handling multiple candidates by checking @Primary, @Priority, and finally falling back to name matching.
<code>public class DefaultListableBeanFactory {
public Object resolveDependency(...) {
return doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
public Object doResolveDependency(...) {
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
if (matchingBeans.size() > 1) {
autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
}
// further logic omitted for brevity
}
protected String determineAutowireCandidate(...) {
// checks @Primary, then @Priority, then name matching
}
}
</code>Through this detailed source‑code walkthrough, the article clarifies how Spring resolves @Resource and @Autowired annotations, highlighting the differences in default injection strategies and the fallback mechanisms that come into play when multiple beans match.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
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.