Another approach would be use bean name as follows. This expects a mode of payment specified in the request.
example : Payment
public class Payment {
private String mode;
public String getMode() {
return this.mode;
}
//.. rest of the class
}
Now define a constants class PaymentModes
public final class PaymentModes {
private PaymentModes() {
}
public static final String CASH = "cash";
public static final String CREDIT = "credit";
}
And provide bean names for the services
@Service(value = PaymentModes.CASH)
public class CashPaymentService extends PaymentService {
@Override
public boolean processPayment(Payment payment) {
//Implementation
}
}
and
@Service(value = PaymentModes.CREDIT)
public class CreditPaymentService extends PaymentService {
@Override
public boolean processPayment(Payment payment) {
// Implementation
}
}
This would register both the beans to the application context with the bean names specified during component scan.
From the documentation
An autowired Map instance’s values consist of all bean instances that
match the expected type, and the Map instance’s keys contain the
corresponding bean names.
So if you autowire as follows
@Autowired
Map<String,PaymentService> serviceMap;
the map would be something as follows :
{cash=rg.so.q61741320.CashPaymentService@1e74829, credit=rg.so.q61741320.CreditPaymentService@16f416f}
where key is the bean name configured and value is the bean instance.
and the following logic can be used to lookup the required service instance from the map.
@Override
@PostMapping
private ResponseEntity<Payment> processPayment(@RequestBody Payment payment) {
String paymentMode = payment.getMode();// get payment mode;
if(serviceMap.containsKey(paymentMode)) {
serviceMap.get(paymentMode).processPayment(payment);
}
return noContent().build();
}
Note : The overridden methods should be public
to allow it to be called from other classes , but given as private
in the code with the question. Appears to be a typo .
Hope this helps