Home > Back-end >  Conditionally choosing implementation in spring boot
Conditionally choosing implementation in spring boot

Time:09-16

I have two implementations for a single interface and I segregated them using @Qualifier("IMP1") and @Qualifier("IMP2") and this is the case for most of the interfaces in my application.

And the requirement is autowire "IMP2" of an interface if available or autowire "IMP1".

As of now, I'm handling like

    @Autowired(required = false)
    @Qualifier("IMP1")
    HelloService imp1Service;

    @Autowired(required = false)
    @Qualifier("IMP2")
    HelloService imp2Service;


    @GetMapping("/hello")
    public String hello() {
        if(l1Service != null){
            return imp2Service.getString();
        } else {
            return imp1Service.getString();
        }
    }

But this is not an elegant way as I've to check for all interfaces in the same way. how to do this

CodePudding user response:

Consider using @ConditionalOnMissingBean on IMP1Service. In this case, IMP1Service will only be loaded in spring context when IMP2Service is missing. This will make sure that you have only implementation of HelloService interface in spring context i.e. either IMP1Service or IMP2Service

@Service
@ConditionalOnMissingBean(IMP2Service.class)
class IMP1Service implements HelloService {
    // TODO
}

Since you have only one implementation of HelloService, you don't need to use @Qualifier annotation anymore

    @Autowired(required = false)
    HelloService impService;

    @GetMapping("/hello")
    public String hello() {
        return impService.getString();
    }

You can read more about Conditional Annotations here:- https://codingnconcepts.com/spring-boot/conditional-annotations-in-spring-boot/

CodePudding user response:

If you need only one bean to be available in your application, which means, given, the other been is not used anywhere else, @ashish's answer would suffice.

BUT, if you need both the services available for the application, for example one service is needed in your aforementioned class and the other is needed in another class, you can use a factory implementation to load the service that is needed only.

Add another method in the interface as follows:

    public interface IHelloService {
        
        boolean isNeededToLoad(Object ...params);

        //write other interface methods
        
    }

Implement the Services as needed with the implementation of the method:

    @Service
    public class FirstHelloService {
        
        @Override            
        public boolean isNeededToLoad(Object ...params) {
            // write conditions for returning this service
        }

        //write the implementation of other methods
        
    }


    @Service
    public class SecondHelloService {
        
        @Override            
        public boolean isNeededToLoad(Object ...params) {
            // write conditions for returning this service
        }

        //write the implementation of other methods
        
    }

Write a service selector class:

    @Component
    @RequiredArgsConstructor
    public class HelloServiceSelector {
        
        private final List<HelloService> serviceList;

        public HelloService locateHelloService(Object ...params) {
            return serviceList.stream()
                        .filter(service -> service.isNeededToLoad(params))
                        .findFirst()
                        .orElseThrow(() -> new RuntimeException("No Suitable Service Found"));

        }
        
    }

This service selector class will return the specific service you need only.

Now autowire the service selector class and call the service as follows:

    @Autowired
    HelloServiceSelector serviceSelector;


    @GetMapping("/hello")
    public String hello() {
        String res1 = serviceSelector.locateHelloService("IMP1").getString();        //this will return FirstHelloService;
        String res2 = serviceSelector.locateHelloService("IMP2").getString();        //this will return SecondHelloService;
        return res1   " "   res2;
    }

You can implement multiple services in this way and can use only the required one when needed.

  • Related