Backend Development 8 min read

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.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Understanding the Differences Between @Resource and @Autowired in Spring

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.

JavaSpringAnnotationsdependency injectionAutowiredResource
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.