Backend Development 9 min read

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.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Unveiling Spring’s @Autowired vs @Resource: Deep Dive into Injection Mechanics

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.

JavaSpringdependency injectionAutowiredResourceSpring Framework
Spring Full-Stack Practical Cases
Written by

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.

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.