Today I remembered an old friend of mine (@primary) with whom we met from tutorials to tutorials. You know that in Spring @Autowired annotation works by type, that is if Spring finds an eligible bean that matches in terms of type it will inject it in. Let's see it on an example.
Assume I have two Singer classes; OperaSinger and MetalSinger.
They both implement the Singer interface.
And lets define a SingerService and inject the Singer bean inside.
What do you think; which Singer will be injected inside?
Here's the result:
I am singing with DIO voice: song lyrics.
This is because OperaSinger is not defined as Component or Service so Spring does not have a clue about it. If we add @Component annotion to it:
Than I'll get this exception:
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [main.service.Singer] is defined: expected single matching bean but found 2: metalSinger,operaSinger
The reason is quite plain to see. If I have more than one bean with same type, and if I use @Autowired annotion which binds type I'll have this exception. Spring does not have a clue which Singer it should use.
Let's favor a music genre and tell Spring to use OperaSinger as Primary.
If we do the SingerService call we will get:
That is because we choose OperaSinger as Primary which means "if you get confused with types you better use this one". Another approach would be the use of qualifier names which directly maps names to beans.
Assume I have two Singer classes; OperaSinger and MetalSinger.
1 | @Component |
2 | public class MetalSinger implements Singer{ |
3 |
4 | @Override |
5 | public String sing(String lyrics) { |
6 | return "I am singing with DIO voice: " +lyrics; |
7 | } |
8 | } |
1 | public class OperaSinger implements Singer { |
2 | @Override |
3 | public String sing(String lyrics) { |
4 | return "I am singing in Bocelli voice: " +lyrics; |
5 | } |
6 | } |
1 | public interface Singer { |
2 | String sing(String lyrics); |
3 | } |
01 | @Component |
02 | public class SingerService { |
03 | private static final Logger logger = LoggerFactory.getLogger(SingerService. class ); |
04 |
05 | @Autowired |
06 | private Singer singer; |
07 |
08 | public String sing(){ |
09 | return singer.sing( "song lyrics" ); |
10 | } |
11 | } |
I am singing with DIO voice: song lyrics.
This is because OperaSinger is not defined as Component or Service so Spring does not have a clue about it. If we add @Component annotion to it:
1 | @Component |
2 | public class OperaSinger implements Singer { |
3 | @Override |
4 | public String sing(String lyrics) { |
5 | return "I am singing in Bocelli voice: " +lyrics; |
6 | } |
7 | } |
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [main.service.Singer] is defined: expected single matching bean but found 2: metalSinger,operaSinger
The reason is quite plain to see. If I have more than one bean with same type, and if I use @Autowired annotion which binds type I'll have this exception. Spring does not have a clue which Singer it should use.
Let's favor a music genre and tell Spring to use OperaSinger as Primary.
1 | @Primary |
2 | @Component |
3 | public class OperaSinger implements Singer{ |
4 |
5 | @Override |
6 | public String sing(String lyrics) { |
7 | return "I am singing in Bocelli voice: " +lyrics; |
8 | } |
9 | } |
1 | "I am singing in Bocelli voice: song lyrics" |