June 2, 2020

M01 Q16 What is the behaviour of the annotation @Autowired?

What is the @Autowired annotation?

@Autowired is an annotation that is processed by AutowiredAnnotationBeanPostProcessor, which can be put onto class constructor, field, setter method or config method. Using this annotation enables automatic Spring Dependency Resolution that is primarily based on types.

@Autowired has a property required which can be used to tell Spring if dependency is required or optional. By default, the dependency is required. If @Autowired with required dependency is used on top of constructor or method that contains multiple arguments, then all arguments are considered required dependency unless the argument is of type Optional, is marked as @Nullable, or is marked as @Autowired(required = false).

If @Autowired is used on top of Collection or Map then Spring will inject all beans matching the type into Collection and key-value pairs as BeanName-Bean into Map. Order of elements depends on the usage of @Order, @Priority annotations and implementation of Ordered interface.

@Autowired uses the following steps when resolving dependency:

  1. Match exactly by type, if only one found, finish
  2. If multiple beans of the same type found, check if any contains @Primary annotation, if yes, inject @Primary bean and finish
  3. If no exactly one match exists, check if @Qualifier exists for the field, if yes use @Qualifier to find matching bean
  4. If still no exactly one bean found, narrow the search by using a bean name
  5. If still no exactly one bean found, throw an exception (NoSuchBeanDefinitionException, NoUniqueBeanDefinitionException, …)

@Autowired with field injection

  • Autowired fields can have any visibility level
  • The injection is happening after Bean is created but before any init method (@PostConstruct, InitializingBean, @Bean(initMethod)) is called
  • By default field is required, however, you can use Optional, @Nullable or @Autowired(required = false) to indicate that field is not required.

After running this example, some of the beans are empty. This is because those are marked as Optional

RecordsService01 recordsHash = Optional.empty
RecordsService01 recordsUtil = null
RecordsService01 recordsValidator = null

@Autowired with constructor

The constructor can have any access modifier (public, protected, private, package-private).

If there is only one constructor in a class, there is no need to use @Autowired on top of it, Spring will use this default constructor anyway and will inject dependencies into it.

If a class defines multiple constructors, then you are obligated to use @Autowired to tell Spring which constructor should be used to create Spring Bean. If you will have a class with multiple constructors without any of constructor marked as @Autowired then Spring will throw NoSuchMethodException.

By default all arguments in constructor are required, however, you can use Optional, @Nullable or @Autowired(required = false) to indicate that parameter is not required.

In this example, there is only one constructor. And there is no need to use @Autowired. Spring already knows which dependencies need to be injected. All dependencies will be resolved.

In the next example, one of the constructors is annotated with @Autowired. And the bean will be created correctly.

Also, some of the dependencies marked as Optional and they will be empty or null. But constructor will be still called correctly.

@Autowired with the method injection

@Autowired method can have any visibility level and also can contain multiple parameters.

If the method contains multiple parameters, then by default it is assumed that in @Autowired method all parameters are required. If Spring will be unable to resolve all dependencies for this method, NoSuchBeanDefinitionException or NoUniqueBeanDefinitionException will be thrown.

When using @Autowired(required = false) with the method, it will be invoked only if Spring can resolve all parameters.

If you want Spring to invoke method only with arguments partially resolved, you need to use @Autowired method with parameter marked as Optional, @Nullable or @Autowired(required = false) to indicate that this parameter is not required.

@Autowired with Collections

RecordsReader is an interface that is implemented by 4 classes. And we can inject all of these Beans into a List.

After running this example, we can see the next output:

RecordsService05 setRecordsReaders:

Because Spring injects all beans with a type of RecordReader to list. And the order is not random. It is dependent on the implementation of the components by annotations @Order, @Priority and Ordered interface.