I have a Service class defined like this,
@RequiredArgsConstructor
class SomeService<T extends AbstractResponse> {
private final ValidationService<T> validationService;
....
}
And I have two kinds of AbstractResponse
, ResponseA
and ResponseB
and have a validation service defined for both of them.
@Service("aValidationService");
class AValidationService<ResponseA> implements ValidationService<ResponseA> {
....
}
and
@Service("ValidationService");
class BValidationService<ResponseB> implements ValidationService<ResponseB> {
....
}
Right now spring is throwing an error because it's not able to deduce the implementation of ValidationService
to use in SomeService
as there are two implementations of it. How do I make spring deduce the correct implementation based on the type of AbstractResponse?
CodePudding user response:
Hope that I understood your requirements.
You can not automatically inject, when you have (2) of the same kind. In this case ValidationService.
You could inject @ValidationServiceA
, or @ValidationServiceB
, or a List<ValidationServiceI>
and then return the one you want based on a <T> type
you care about:
The solution below highlights that.
The method getGenericParameter()
is used to return the <T>
parameter. This is to avoid the use of Reflection.
The method methodWhichDeterminesWhichServiceToUseBasedOnResponseType
to used to determine which ValidationService to use based on the input that you require.
You can find the complete solution below, including a verification Test.
import org.springframework.stereotype.Service;
@Service
public class ValidationServiceA implements ValidationServiceI<ResponseA>{
@Override public Class<ResponseA> getGenericParameter() {
return ResponseA.class;
}
public void print(){
System.out.println("Service A");
}
}
@Service
public class ValidationServiceB implements ValidationServiceI<ResponseB>{
@Override public Class<ResponseB> getGenericParameter() {
return ResponseB.class;
}
public void print(){
System.out.println("Service B");
}
}
public interface ValidationServiceI<T>{
Class<T> getGenericParameter();
void print();
}
@Service
public class ServiceWhichCallsOthers {
@Autowired
private List<ValidationServiceI> validationServices;
public <T> ValidationServiceI<T> methodWhichDeterminesWhichServiceToUseBasedOnResponseType(T responseType){
Optional<ValidationServiceI> validationServiceSupportingResponse = validationServices.stream().filter(validationServiceI -> validationServiceI.getGenericParameter().equals(responseType)).findFirst();
return validationServiceSupportingResponse.get();
}
public void callValidationServiceA(){
methodWhichDeterminesWhichServiceToUseBasedOnResponseType(ResponseA.class).print();
}
public void callValidationServiceB(){
methodWhichDeterminesWhichServiceToUseBasedOnResponseType(ResponseB.class).print();
}
}
@SpringBootTest
public class ServiceWhichCallsOthersIT {
@Autowired
private ServiceWhichCallsOthers serviceWhichCallsOthers;
@Test
public void validateBasedOnResponseType(){
Assertions.assertEquals(ValidationServiceA.class, serviceWhichCallsOthers.methodWhichDeterminesWhichServiceToUseBasedOnResponseType(ResponseA.class).getClass());
Assertions.assertEquals(ValidationServiceB.class, serviceWhichCallsOthers.methodWhichDeterminesWhichServiceToUseBasedOnResponseType(ResponseB.class).getClass());
serviceWhichCallsOthers.callValidationServiceA();
serviceWhichCallsOthers.callValidationServiceB();
}
}