Spring annotations I never had the chance to use part 1: @primary
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.
@Component public class MetalSinger implements Singer{ @Override public String sing(String lyrics) { return "I am singing with DIO voice: "+lyrics; } }
public class OperaSinger implements Singer { @Override public String sing(String lyrics) { return "I am singing in Bocelli voice: "+lyrics; } }
They both implement the Singer interface.
public interface Singer { String sing(String lyrics); }
And lets define a SingerService and inject the Singer bean inside.
@Component public class SingerService { private static final Logger logger = LoggerFactory.getLogger(SingerService.class); @Autowired private Singer singer; public String sing(){ return singer.sing("song lyrics"); } }
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:
@Component public class OperaSinger implements Singer { @Override public String sing(String lyrics) { return "I am singing in Bocelli voice: "+lyrics; } }
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.
@Primary @Component public class OperaSinger implements Singer{ @Override public String sing(String lyrics) { return "I am singing in Bocelli voice: "+lyrics; } }
If we do the SingerService call we will get:
"I am singing in Bocelli voice: song lyrics"
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.
Reference: | Spring annotations I never had the chance to use part 1: @primary from our JCG partner Sezin Karli at the caught Somewhere In Time = true; blog. |
Thanks for sharing Sezin Karli, interesting observation and helpful post.