Search code examples
springspring-bootjunitautowired

Spring boot autowiring an interface with multiple implementations


In normal Spring, when we want to autowire an interface, we define it's implementation in Spring context file.

  1. What about Spring boot?
  2. how can we achieve this?

currently we only autowire classes that are not interfaces.

Another part of this question is about using a class in a Junit class inside a Spring boot project.

If we want to use a CalendarUtil for example, if we autowire CalendarUtil, it will throw a null pointer exception. What can we do in this case? I just initialized using "new" for now...


Solution

  • Use @Qualifier annotation is used to differentiate beans of the same interface
    Take look at Spring Boot documentation
    Also, to inject all beans of the same interface, just autowire List of interface
    (The same way in Spring / Spring Boot / SpringBootTest)
    Example below:

    @SpringBootApplication
    public class DemoApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
    
    public interface MyService {
    
        void doWork();
    
    }
    
    @Service
    @Qualifier("firstService")
    public static class FirstServiceImpl implements MyService {
    
        @Override
        public void doWork() {
            System.out.println("firstService work");
        }
    
    }
    
    @Service
    @Qualifier("secondService")
    public static class SecondServiceImpl implements MyService {
    
        @Override
        public void doWork() {
            System.out.println("secondService work");
        }
    
    }
    
    @Component
    public static class FirstManager {
    
        private final MyService myService;
    
        @Autowired // inject FirstServiceImpl
        public FirstManager(@Qualifier("firstService") MyService myService) {
            this.myService = myService;
        }
    
        @PostConstruct
        public void startWork() {
            System.out.println("firstManager start work");
            myService.doWork();
        }
    
    }
    
    @Component
    public static class SecondManager {
    
        private final List<MyService> myServices;
    
        @Autowired // inject MyService all implementations
        public SecondManager(List<MyService> myServices) {
            this.myServices = myServices;
        }
    
        @PostConstruct
        public void startWork() {
            System.out.println("secondManager start work");
            myServices.forEach(MyService::doWork);
        }
    
    }
    
    }
    

    For the second part of your question, take look at this useful answers first / second