Understanding the @Autowired Annotation in Spring: Usage, Implementation, and Underlying Mechanics

This article explains the @Autowired annotation in Spring, covering its various usage forms such as constructor, setter, method, field, and collection injection, and delves into the underlying implementation details within Spring's AutowiredAnnotationBeanPostProcessor, including metadata handling, reflection-based injection, and common pitfalls.

Top Architect
Top Architect
Top Architect
Understanding the @Autowired Annotation in Spring: Usage, Implementation, and Underlying Mechanics

Introduction

When developing with Spring, configuration can be done via XML or Java config. As Spring Boot becomes more popular, Java config and its annotations, especially @Autowired, are widely used for bean injection.

@Autowired Usage Examples

The annotation can be applied to constructors, setter methods, arbitrary methods, fields, and even collections such as arrays, Set, and Map. The following examples illustrate each case.

public class MovieRecommender {
    private final CustomerPreferenceDao customerPreferenceDao;
    @Autowired
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
        this.customerPreferenceDao = customerPreferenceDao;
    }
}
public class SimpleMovieLister {
    private MovieFinder movieFinder;
    @Autowired
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}
public class MovieRecommender {
    private MovieCatalog movieCatalog;
    private CustomerPreferenceDao customerPreferenceDao;
    @Autowired
    public void prepare(MovieCatalog movieCatalog, CustomerPreferenceDao customerPreferenceDao) {
        this.movieCatalog = movieCatalog;
        this.customerPreferenceDao = customerPreferenceDao;
    }
}
public class MovieRecommender {
    @Autowired
    private MovieCatalog movieCatalog;
    private final CustomerPreferenceDao customerPreferenceDao;
    @Autowired
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
        this.customerPreferenceDao = customerPreferenceDao;
    }
}
public class MovieRecommender {
    @Autowired
    private MovieCatalog[] movieCatalogs;
}
public class MovieRecommender {
    private Set<MovieCatalog> movieCatalogs;
    @Autowired
    public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) {
        this.movieCatalogs = movieCatalogs;
    }
}
public class MovieRecommender {
    private Map<String, MovieCatalog> movieCatalogs;
    @Autowired
    public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) {
        this.movieCatalogs = movieCatalogs;
    }
}

What Does @Autowired Actually Do?

@Autowired

belongs to Spring’s container‑configuration annotations. It tells the container to automatically wire a bean from the application context into the annotated field, constructor, or method, effectively performing dependency injection.

How Annotations Work in Java

Annotations are metadata retained either only in source code (e.g., @Override) or at runtime (e.g., Spring’s @Component). Runtime retention enables frameworks to read the annotation via reflection and apply custom logic.

Example: @Override

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {}

Annotations themselves contain no behavior; the JVM or a framework interprets them.

Creating a Custom Annotation

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SimpleAnnotation {
    String value();
}
public class UseAnnotation {
    @SimpleAnnotation("testStringValue")
    public void testMethod() {
        // do something here
    }
}

To make a custom annotation functional, you must write code that reads it via reflection and performs the desired action.

Implementation of @Autowired in Spring

The annotation is defined in org.springframework.beans.factory.annotation.Autowired with runtime retention and can target constructors, methods, parameters, fields, and other annotations.

Spring processes the annotation through the class AutowiredAnnotationBeanPostProcessor. The core method buildAutowiringMetadata scans a bean’s fields and methods, collects those annotated with @Autowired, and creates an InjectionMetadata object.

private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
    LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<>();
    Class<?> targetClass = clazz;
    do {
        LinkedList<InjectionMetadata.InjectedElement> currElements = new LinkedList<>();
        // Scan fields
        ReflectionUtils.doWithLocalFields(targetClass, field -> {
            AnnotationAttributes ann = findAutowiredAnnotation(field);
            if (ann != null) {
                if (Modifier.isStatic(field.getModifiers())) {
                    logger.warn("Autowired annotation is not supported on static fields: " + field);
                    return;
                }
                boolean required = determineRequiredStatus(ann);
                currElements.add(new AutowiredFieldElement(field, required));
            }
        });
        // Scan methods
        ReflectionUtils.doWithLocalMethods(targetClass, method -> {
            Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
            if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
                return;
            }
            AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);
            if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
                if (Modifier.isStatic(method.getModifiers())) {
                    logger.warn("Autowired annotation is not supported on static methods: " + method);
                    return;
                }
                if (method.getParameterCount() == 0) {
                    logger.warn("Autowired annotation should only be used on methods with parameters: " + method);
                }
                boolean required = determineRequiredStatus(ann);
                PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
                currElements.add(new AutowiredMethodElement(method, required, pd));
            }
        });
        elements.addAll(0, currElements);
        targetClass = targetClass.getSuperclass();
    } while (targetClass != null && targetClass != Object.class);
    return new InjectionMetadata(clazz, elements);
}

The resulting InjectionMetadata holds the target class and a collection of InjectedElement objects representing fields or methods that need injection.

During bean post‑processing, Spring calls postProcessPropertyValues, obtains the metadata, and invokes metadata.inject(bean, beanName, pvs). The inject method iterates over each InjectedElement and performs the actual injection:

protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs) throws Throwable {
    if (this.isField) {
        Field field = (Field) this.member;
        ReflectionUtils.makeAccessible(field);
        field.set(target, getResourceToInject(target, requestingBeanName));
    } else {
        Method method = (Method) this.member;
        ReflectionUtils.makeAccessible(method);
        method.invoke(target, getResourceToInject(target, requestingBeanName));
    }
}

For fields, Spring simply sets the field value; for methods, it invokes the method with the resolved bean as an argument. The bean to inject is obtained via getResourceToInject, which looks up the appropriate bean name in the application context.

Common Questions

What is the lifecycle of an annotation?

An annotation’s lifecycle is determined by its @Retention policy. RetentionPolicy.SOURCE annotations disappear after compilation, while RetentionPolicy.RUNTIME annotations remain in the class file and can be read via reflection at runtime.

How is the relationship between an injected bean and the bean that uses it maintained?

The injected bean becomes a regular field (or method parameter) of the consuming class. Spring resolves the dependency from the container and assigns the reference, establishing a normal object‑reference relationship.

Why can’t @Autowired be used on static fields?

Static fields belong to the class, not to an instance, which contradicts Spring’s instance‑based dependency injection model. Using static injection would create global state, hinder testing, and break the container’s lifecycle management. Spring therefore warns and ignores @Autowired on static members.

Conclusion

In summary, @Autowired enables Spring to automatically wire beans into constructors, methods, fields, and collections. The framework implements this through reflection‑based metadata scanning and injection performed by AutowiredAnnotationBeanPostProcessor. Understanding the underlying mechanism helps developers use the annotation correctly and avoid common pitfalls such as static injection.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaBackend Developmentspringannotationsdependency-injectionAutowired
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

0 followers
Reader feedback

How this landed with the community

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.