Autowired annotation in Spring

Have you ever asked you about @Autowired annotation in Spring ? Frequently used to facilitate Dependency Injection, an whole mechanism is hidden under this process.

@Autowired features in Spring

@Autowired is an annotation that performs Dependency Injection. Every time when one Spring-managed bean meets this annotation, it injects directly corresponding another Spring-managed bean. This annotation can be applied at different levels:
- class fields: Spring will look for appropriated beans by scanning defined packages (for example in case of annotated controllers) or by looking for beans directly inside configuration files.
- methods: every method annotated with @Autowired is submitted to Dependency Injection. But beware, all objects presented in method's signature must be Spring-managed beans. If you have a method like setTest(Article article, NoSpringArticle noSpringArt) with only one parameter (Article article) managed by Spring, an exception org.springframework.beans.factory.BeanCreationException will be thrown. It's caused by the fact that Spring doesn't know one or more specified parameters and isn't able to resolve them. A full exception trace can look like:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'testController': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void org.krams.tutorial.controller.TestController.ix(com.mysite.controller.IndexController,com.mysite.nospring.data.Article); nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.mysite.nospring.data.Article] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {} 

- constructors: works at the same manner as in previous point dedicated to the methods.

Objects injection follows some rules. One bean can be injected according to its:
- name: bean resolving is made thanks to bean name.
- type: the resolving process is based on bean's class type.

In some cases, @Autowired should by helped by @Qualifier annotation. The last one facilitates the recognition of object to inject in the case when, for example, several beans are of the same type. Imagine following configuration:

<bean name="comment1" class="com.waitingforcode.Comment">
	<property name="text" value="Content of the 1st comment" />
</bean>

<bean name="comment2" class="com.waitingforcode.Comment">
	<property name="text" value="Content of the 2nd comment" />
</bean>

With a simple @Autowired, Spring won't know which bean you want to inject. It's why, you should consider to use @Qualifier(value="beanName") annotation. In our case, to distinct comment1 from comment2 bean, we can write following code:

@Qualifier(value="comment1")
@Autowired
private Comment firstComment;

@Qualifier(value="comment2")
@Autowired
private Comment secondComment;

Implementing @Autowired in Spring

As we saw in previous part, they are different ways to implement @Autowired in Spring. In this part of article, we'll activate annotation autowiring. After, we'll write a simple class and configure some beans. After that, we'll use them in two another classes: a controller annotated by @Controller and a class not managed by Spring. Let's start by activate the annotation autowiring :

  <context:annotation-config />

You must put this entry in your application context configuration. It will enable dependency injection in @Autowired annotation.

Now, we can write and configure our beans:

// beans first
public class Comment {

	private String content;
	
	public void setContent(String content) {
		this.content = content;
	}
	
	public String getContent() {
		return this.content;
	}
	
}

// sample controller
@Controller
public class TestController {
	
	@Qualifier(value="comment1")
	@Autowired
	private Comment firstComment;
	
	@Qualifier(value="comment2")
	@Autowired
	private Comment secondComment;
	
	@RequestMapping(value = "/test", method = RequestMethod.GET)
	public String test() {
		System.out.println("1st comment text: "+firstComment.getText());
		System.out.println("2nd comment text: "+secondComment.getText());
    	return "test";
	}

}

// no-Spring managed class
public class TestNoSpring {

	@Autowired
	private Comment comment;
	
	
	public void testComment(String content) {
		if (comment == null) {
			System.out.println("Comment's instance wasn't autowired because this class is not Spring-managed bean");
		} else {
			comment.setContent(content);
			System.out.println("Comment's content: "+comment.getContent());
		}
	}
	
}

And XML configuration (already seen in previous part):

<bean name="comment1" class="com.specimen.exchanger.Comment">
	<property name="content" value="Content of the 1st comment" />
</bean>

<bean name="comment2" class="com.specimen.exchanger.Comment">
	<property name="content" value="Content of the 2nd comment" />
</bean>

Now, let's try to run TestController by browsing http://localhost:8080/test . As expected, the comment fields from TestController were correctly autowired and the comment field from TestNoSpring no:

1st comment text: Content of the 1st comment
2nd comment text: Content of the 2nd comment
Comment's instance wasn't autowired because this class is not Spring-managed bean

What is wrong ? TestNoSpring class is not handled by Spring. It's why Spring can't inject Comment's instance dependency. We'll explain this concept in the next part.

How does @Autowired annotation work ?

Before approaching code details, we need to remind the basics. Spring manages beans which are Java objects available for whole application. They are living inside the Spring container, called application context. It means that we don't need to handler theirs lifecycle (initialization, destruction). This task is accomplished by the application context. In additionally, this context has an entry point which can be, in case of web application, a dispatcher servlet. It's there where the context is bootstraped and all beans injected.

The dependencies autowiring is possible thanks to org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor. This class handles both, @Autowired and @Value, Spring annotations. It can also manage JSR-303's @Inject annotation, if it's available. The annotation to handle are defined inside AutowiredAnnotationBeanPostProcessor constructor. After, several methods permits an @Autowired annotation treatment.

The first one, private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) resolves all class properties waiting for autowiring. It does it by analyzing all fields and methods and initializing an instance of org.springframework.beans.factory.annotation.InjectionMetadata class. This class holds a list of elements to inject. The injection is done through Java's API Reflection (Field set(Object obj, Object value) method or Method invoke(Object obj, Object... args) method). This process is invoked directly in AutowiredAnnotationBeanPostProcessor's method public void processInjection(Object bean) throws BeansException. It retrieves all injectable beans as InjectionMetadata instances and invoke theirs inject() methods.

Another important function in AutowiredAnnotationBeanPostProcessor class is private AnnotationAttributes findAutowiredAnnotation(AccessibleObject ao). It looks for @Autowired annotation by analyzing all annotations belonging to one field or one method. If @Autowired annotation is not found, it returns null and field or method is considered as not injectable.

In the above article we've seen autowiring process in Spring. Whole article shows that this dependency injection is a beautiful way to unload XML configuration files and facilitate injection (can be done on fields as well on methods). In additionally, it enhances the code readability. To inject a bean with annotation, we don't need anymore to prefix all setter methods with set word. As you can see, @Autowired influences not only configuration side, but Java's side too.

If you liked it, you should read:

The comments are moderated. I publish them when I answer, so don't worry if you don't see yours immediately :)

📚 Newsletter Get new posts, recommended reading and other exclusive information every week. SPAM free - no 3rd party ads, only the information about waitingforcode!