I’ve been playing around in Spring/Spring Boot and notice something strange which I can’t really dig on my own, so any help would be appreciated.
Let’s say we have the following
public interface ICar {}
@Component public class CarOne implements ICar {}
@Component public class CarTwo implements ICar {}
public class Driver { @Autowired private ICar car; }
I am running a Spring Boot program, so everything is set up correctly, this is for simplicity.
At this point, if we run our program we would get an exception, because of the ambiguity. Now according to https://www.baeldung.com/spring-qualifier-annotation if we add @Qualifier
on top of a @Component
class, it should change the bean name of this class. So for example, if we change the CarOne
class to:
@Component @Qualifier("car") public class CarOne implements ICar {}
The bean name should be car
.
Based on Spring documentation, @Autowired
uses the field name as a default qualifier if not provided any, thus we should be able to successfully run our program, since the @Autowired
field in class Driver
is looking for a bean named car
and we already have one. Not this is happening tho. I again receive the same exception, regarding ambiguity.
Now if I add @Qualifier("car")
, along with @Autowired
, everything will run normally.
I am confused, because if we change for example the @Autowired
field to carOne
or carTwo
(without adding any @Qualifier
on top of the @Component
classes), again the program will run correctly. I guess this is related somehow with the type/name difference, but can’t figure it out. Can somebody with a bit more knowledge explain why this could be happening?
P.S: I am referring to the baeldung article, since I couldn’t find anything better in the official Spring doc, regarding the usage of @Qualifier
on top of classes.
Advertisement
Answer
Using @Qualifier
on a component doesn’t change the name. It adds, as the name implies, a qualifier to the metadata of the bean. This metadata (the qualifier) can be used to select which instance of a class to use.
So if you annotated one of them with @Qualifier("car")
you need to add that to the injection point as well (if that is the one you want).
You could also use @Qualifer
as a meta annotation, meaning you could create an annotation with this annotation and use that as the metadata.
Something like
@Qualifier public @interface SimpleCar {}
Now you could annotate your CarOne
(for example) with @SimpleCar
and put that on the @Autowired
field as well. Which is probably clearer as using names/strings to qualify things.
You could also use @Qualifier("carOne")
on the @Autowired
field to specify which to use (as the name of the bean is used as a fallback). However the drawback is you need to know the name (hence preferred is to use specific qualifier annotations).
Now for @Autowired
it works by default on type. So it will try to find a single Car
instance in this case. You have more than that, and so it will, as a fallback, try to match the name of the field to a name of a bean (car
here) as that isn’t the case it will fail. If you change the CarOne
to @Component("car")
it will also work without further change as you now have a bean matching the type and name of the field. However again relying on this is generally a bad idea (one change in naming strategy, class name and things break).