Class Required a Single Bean, but 2 Were Found:
I Have the Below Interface: Public Interface Mailsender { Void Sender(String To, String Subject, String Body); } with 2 Imlementation of It: Public Class...
I have the below interface:
public interface MailSender {
void sender(String to, String subject,String body);
}
With 2 imlementation of it:
public class SmtpkMailSender implements MailSender {
static Log log=LogFactory.getLog(MailSender.class);
public void sender(String to, String subject,String body){
log.info("SMTP To: "+to);
log.info("SMTP Subjecy: "+subject);
log.info("SMTP body: "+body);
}
}
and the second one is:
@Primary
public class MockMailSender implements MailSender {
static Log log=LogFactory.getLog(MailSender.class);
public void sender(String to, String subject,String body){
log.info("To: "+to);
log.info("Subject: "+subject);
log.info("body: "+body);
}
}
I used the dependency injection into the controller class which is as following:
@RestController
public class MailController {
@Autowired
private MailSender smtpkMailSender;
@RequestMapping("/send")
public String send(){
smtpkMailSender.sender("Person", "Important", "Take Care");
return "mail is sent";
}
}
At the end i have a configuration class which contains my Beans:
@Configuration
public class MailConfig {
@Bean
public SmtpkMailSender getSmtpkMailSender(){
return new SmtpkMailSender();
}
@Bean
public MockMailSender getMockMailSender(){
return new MockMailSender();
}
}
Unfortunatly, when i run my application it complains with:
Description:
Field smtpkMailSender in com.example.demo.MailController required a single bean, but 2 were found:
- getSmtpkMailSender: defined by method 'getSmtpkMailSender' in class path resource [com/example/mail/MailConfig.class]
- getMockMailSender: defined by method 'getMockMailSender' in class path resource [com/example/mail/MailConfig.class]
Action:
Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed
As you see, although i have specified the MockMailSender as Primary the spring still complains, and cannot identify it
4 Answers
You can use @Qualifier annotation for specify which specific type of your implementation, you want to autowire.
@RestController
public class MailController {
@Autowired
@Qualifier("smtpkMailSender")
private MailSender smtpkMailSender;
@RequestMapping("/send")
public String send(){
smtpkMailSender.sender("Person", "Important", "Take Care");
return "mail is sent";
}
}
Since you're using java config you should mark config method with @Primary annotation, and not a class:
@Configuration
public class MailConfig {
@Bean
public SmtpkMailSender getSmtpkMailSender(){
return new SmtpkMailSender();
}
@Bean
@Primary
public MockMailSender getMockMailSender(){
return new MockMailSender();
}
}
You may simply name your bean using
@Bean(name="mockMailSender")
public MockMailSender getMockMailSender() { }
and then decorate the autowired field with
@Autowired
@Qualifier("mockMailSender")
private MailSender smtpkMailSender;
Another solution for this error is to exclude the Bean that is not used (the duplicated bean), from the autoconfiguration stage within "application.yml" file, as explained in this thread: How to exclude AutoConfiguration classes in Spring Boot JUnit tests?
spring:
autoconfigure:
exclude:
- org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration
- org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration
This is applicable if the error appears after you added a new Maven/Gradle dependency, which duplicates an existing bean that you are using. For example, when both beans come from external JARs, so the source code cannot be updated with @Primary or @Qualifier annotations to solve the error.