DI – Two beans implementing same inteface

How to inject the right one?

To see what happens lets annotate with @Component our TweetService class.

@Component
public class TweetService implements MessageService {


    public void sendMessage(String subject, String message) {
        System.out.println("Tweet: " + subject + " - " + message);
    }
}

Trying to run the application you’ll get an error:

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.programmerscuriosity.springforblogdemo.di.example.four.MessageService] is defined: expected single matching bean but found 2: smsService,tweetService

Spring, when trying to find a suitable bean for injecting to the messageService variable, finds two beans – the instances of both TweetService and SmsService.

Let’s try to fix it. One of few ways to do it is, to specify a name in @Resource annotation.

@RestController
public class MessageController {

    @Resource(name = "tweetService")
    private MessageService messageService;

    @RequestMapping("/message")
    public String sendMessage() {
        messageService.sendMessage("Very important:", "Please read this carefully.");
        return "Your message was sent";
    }
}

Spring will then be able to inject the exact bean without any error.

Another alternative will be to give a name to the bean to match the resource name.

@RestController
public class MessageController {

    @Resource
    private MessageService messageService;

    @RequestMapping("/message")
    public String sendMessage() {
        messageService.sendMessage("Very important:", "Please read this carefully.");
        return "Your message was sent";
    }
}
@Component("messageService")
public class SmsService implements MessageService {

    @Override
    public void sendMessage(String subject, String message) {
        System.out.println("SMS: " + subject + " - " + message);
    }
}

Yet another option to tell Spring which bean to inject is to use @Primary annotation.

@Primary
@Component
public class TweetService implements MessageService {


    public void sendMessage(String subject, String message) {
        System.out.println("Tweet: " + subject + " - " + message);
    }
}
@Component
public class SmsService implements MessageService {

    @Override
    public void sendMessage(String subject, String message) {
        System.out.println("SMS: " + subject + " - " + message);
    }
}

The application should still run fine.

A more powerful (it not only works on setters, but on any method, including the constructors) but still very similar to @Resource is @Autowired annotation.

@RestController
public class MessageController {


    private MessageService messageService;

    @Autowired
    public MessageController(MessageService messageService) {
        this.messageService = messageService;
    }

    @RequestMapping("/message")
    public String sendMessage() {
        messageService.sendMessage("Very important:", "Please read this carefully.");
        return "Your message was sent";
    }
}

However, @Autowired does not take the name element. Instead, we need to use @Qualifier along with @Autowired.

@RestController
public class MessageController {


    private MessageService messageService;

    @Autowired
    public MessageController(@Qualifier("sms") MessageService messageService) {
        this.messageService = messageService;
    }

    @RequestMapping("/message")
    public String sendMessage() {
        messageService.sendMessage("Very important:", "Please read this carefully.");
        return "Your message was sent";
    }
}
@Component
@Qualifier("sms")
public class SmsService implements MessageService {

    @Override
    public void sendMessage(String subject, String message) {
        System.out.println("SMS: " + subject + " - " + message);
    }
}

By default, the qualifier of a bean will be same as its name.

@RestController
public class MessageController {


    private MessageService messageService;

    @Autowired
    public MessageController(@Qualifier("tweetService") MessageService messageService) {
        this.messageService = messageService;
    }

    @RequestMapping("/message")
    public String sendMessage() {
        messageService.sendMessage("Very important:", "Please read this carefully.");
        return "Your message was sent";
    }
}
@Component
public class TweetService implements MessageService {


    public void sendMessage(String subject, String message) {
        System.out.println("Tweet: " + subject + " - " + message);
    }
}

 

Spring also has @Inject , which is synonymous to @Autowired. It’s part of the Java CDI JSR-300 standard, and so Spring thought to support it. What is the difference between @Inject and @Autowired?

The short answer: There is no different and can be used interchangeably.

In more detail @Inject is part of the Java CDI (Contexts and Dependency Injection) standard introduced in Java EE 6 (JSR-300). Spring has chosen to support using @Inject synonymously with their own @Autowired annotation.@Autowired is Spring’s own annotation. In a Spring application, the two annotations works the same way as Spring has decided to support some JSR-300 annotations in addition to their own.

 

Leave a Reply