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.
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?
@Autowiredbelongs 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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
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.
