In my previous post I briefly introduced the concept of Spring’s @Autowired
annotation. This post goes more in-depth about this annotation, and its importance when developing with the Spring framework.
Spring uses the @Autowired
annotation when it determines where it needs to inject a given @Bean
. You can see this in the example below:
This example uses @Autowired
on a private member variable referenced via class. In this example, the @Component
is on the WeatherService
class. The DependencyInjectionWithSpring
class references the WeatherService
class directly. I’ve annotated this reference with the @Autowired
annotation. Spring then knows that it needs to find a bean (component) that matches. In this case, it finds the WeatherService
class and injects it into the DependencyInjectionWithSpring
class.
You can also reference with @Autowired
via interface:
Here, I change the WeatherService
class to be named WeatherServiceImpl, and have it implement the WeatherService
interface, which I’ve also defined. DependencyInjectionWithSpring
now references this interface (WeatherService
), and the @Autowired
annotation stays. Spring then knows that it needs to find a bean/component that implements the WeatherService
interface. It finds the WeatherServiceImpl
class and injects it into the DependencyInjectionWithSpring
as a dependency (weatherService
).
Field X in Y required a single bean, but 2 were found
What happens if we have multiple beans (components) that implement the same interface? Like this:
Both SunnyWeatherServiceImpl
and CloudyWeatherServiceImpl
implement the WeatherService
interface. The DependencyInjectionWithSpring
class depends on an implementation of WeatherService
. Both of these classes would work, since they implement that interface. Spring is aware and manages both of these classes, since they are annotated with the @Component
annotation. How does Spring know which one to choose? Let’s run the application and see what happens:
The application is unable to start. Spring can’t determine which bean to set for the weatherService
.
How do I fix this?
To resolve this issue and tell Spring which bean to use in this situation, we have a few options.
The first option is to follow the first suggestion given in the exception:
Consider marking one of the beans as @Primary…
The @Primary
annotation can be used to indicate to Spring which bean to use. In this case, I add the @Primary
annotation to the SunnyWeatherServiceImpl:
Now when I run the application, it starts up with no errors and runs as expected, injecting the SunnyWeatherServiceImpl (the one with the @Primary
annotation) into the DependencyInjectionWithSpring
class.
There’s another option that Spring gives us, as mentioned in the error message from above:
or using @Qualifier to identify the bean that should be consumed
@Qualifier
is a Spring annotation that allows us to tell Spring which bean we want to inject. See below:
Pay particular attention to the DependencyInjectionWithSpring
class. The weatherService
dependency in this class is now annotated with @Qualifier(value="cloudyWeatherServiceImpl")
. This tells Spring to use the cloudyWeatherServiceImpl
bean when injecting this dependency.
Now when I run the application, again it starts up with no errors and runs as expected. This time, I told Spring to use the cloudyWeatherServiceImpl
class and it prints out The weather is cloudy with a 90% chance of rain
as expected.
There’s yet another way to indicate to Spring which bean to inject. This is by naming the variable of the dependency the name of the class we want to inject. Like so:
Here, the WeatherService
interface gets the name cloudyWeatherServiceImpl
. This tells Spring that I want to inject the bean with the name cloudyWeatherServiceImpl
. When I run this code, the application again starts up with no problems, and I again get the expected output.
Summary
Spring’s @Autowired
annotation is used to indicate where to inject a Spring-managed bean. The @Primary
annotation allows us to indicate which bean to use when multiple beans that match are present. The @Qualifier
annotation allows us to specify a specific bean to inject on a given dependency. Finally, we learned that we can name the dependency with the bean name to also specify which bean to inject.