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.