class required a single bean, but 2 were found:
Asked Answered
S

4

24

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

Subjoin answered 21/9, 2017 at 12:9 Comment(1)
Add a qualifier to spell out which bean you intend to inject.Unthrone
C
18

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();
    }
}
Claudieclaudina answered 21/9, 2017 at 12:13 Comment(2)
How can i call service of my choice based on some condition?Abort
@AakashPatel, you can inject all of them using Qualifier annotation with a name of the bean as value. Then use any of them based on your conditionClaudieclaudina
D
21

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";
  }
}
Distraught answered 21/9, 2017 at 12:53 Comment(2)
is it possible to make the qualifier value configurable?Macedonian
it is possible, follow below link #7813245Distraught
C
18

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();
    }
}
Claudieclaudina answered 21/9, 2017 at 12:13 Comment(2)
How can i call service of my choice based on some condition?Abort
@AakashPatel, you can inject all of them using Qualifier annotation with a name of the bean as value. Then use any of them based on your conditionClaudieclaudina
E
3

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.

Ellette answered 11/1, 2023 at 10:56 Comment(0)
D
2

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;
Dulcet answered 14/5, 2020 at 8:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.