Understanding the Differences Between @Resource and @Autowired in Spring
This article explains the distinct origins, injection strategies, and internal processing of Spring's @Resource and @Autowired annotations, compares their behavior, and walks through relevant source code to illustrate how each annotation resolves dependencies.
Annotation Differences
@Resource is a JDK-provided annotation, while @Autowired is supplied by Spring. They both support dependency injection but differ in provider and injection mode.
Provider
@Autowired : Spring annotation.
@Resource : JDK annotation.
Injection Mode
@Autowired defaults to type‑based injection; it requires the bean to exist unless required=false . Name‑based injection can be achieved with @Qualifier .
@Resource defaults to name‑based injection. If name is not specified, the field name is used; only when no bean matches the name does it fall back to type‑based injection.
Overall, the two annotations differ in their source and default wiring strategy.
Source Code Analysis
The following examples show how field injection is processed for each annotation.
@Resource processing
The handler is CommonAnnotationBeanPostProcessor . Its postProcessProperties method collects fields annotated with @Resource into an InjectionMetadata object and invokes inject .
<code>public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
// Find all fields annotated with @Resource
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
metadata.inject(bean, beanName, pvs);
return pvs;
}
</code>The core class InjectionMetadata iterates over its InjectedElement list and calls inject on each element. For @Resource , the element is a ResourceElement that determines the bean name (or defaults to the field name) and obtains the target instance via getResourceToInject .
<code>private class ResourceElement extends LookupElement {
public ResourceElement() {
Resource resource = ae.getAnnotation(Resource.class);
String resourceName = resource.name();
Class<?> resourceType = resource.type();
this.isDefaultName = !StringUtils.hasLength(resourceName);
if (this.isDefaultName) {
resourceName = this.member.getName();
}
this.name = (resourceName != null ? resourceName : "");
Lazy lazy = ae.getAnnotation(Lazy.class);
this.lazyLookup = (lazy != null && lazy.value());
}
protected Object getResourceToInject(Object target, String requestingBeanName) {
return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName)
: getResource(this, requestingBeanName));
}
}
</code>The CommonAnnotationBeanPostProcessor ultimately calls getResource , which delegates to autowireResource . If a bean with the specified name exists, it is retrieved; otherwise the container falls back to type‑based resolution.
@Autowired processing
The handler is AutowiredAnnotationBeanPostProcessor . Its postProcessProperties method gathers fields annotated with @Autowired into an InjectionMetadata and invokes inject .
<code>public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
// Find all fields annotated with @Autowired
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
metadata.inject(bean, beanName, pvs);
return pvs;
}
</code>During injection, each AutowiredFieldElement resolves the dependency via the DefaultListableBeanFactory , which performs type‑based lookup and handles qualifiers, primary, and priority annotations.
<code>private class AutowiredFieldElement {
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
Field field = (Field) this.member;
Object value = resolveFieldValue(field, bean, beanName);
if (value != null) {
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}
}
private Object resolveFieldValue(Field field, Object bean, String beanName) {
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
desc.setContainingClass(bean.getClass());
return beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
}
}
</code>The DefaultListableBeanFactory resolves dependencies by type, and when multiple candidates exist, it selects a bean based on @Primary , @Priority , or name matching.
By following these code paths, you can see how @Resource prefers name‑based injection while @Autowired defaults to type‑based injection, and how each annotation ultimately obtains the required bean from the Spring container.
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.