Skip to content
Advertisement

How to inject the right bean implementation according to a RequestParam in the current call

I have this Spring bean (this RestController for the sake of the example) that, depending on the country (let’s say a param that comes in), I want to inject the right implementation of the TaxpayerNameService.

So, I have that TaxpayerNameService interface and two (more in the future) implementations of such interface which needs to be injected in the current call of the controller; I say current call because that same controller will be serving many countries and depending on the iso2 constant that I’m sending somewhere in (right now it comes from documentType.getCountry(), I have to retrieve in runtime the right TaxpayerNameService implementation and call that method getTaxpayerName.

Each country has a different set of services, so each implementation of the interface does the right call to the right service.

@RestController
@RequestMapping("/taxpayers")
public class TaxpayerController {

    @Autowired
    @Qualifier("TaxpayerNameServiceImplHN")
    private TaxpayerNameService taxpayerNameServHN;

    @Autowired
    @Qualifier("TaxpayerNameServiceImplCR")
    private TaxpayerNameService taxpayerNameServCR;

    @GetMapping(path = "/{documentType}-{documentNumber}/name", produces = MediaType.TEXT_PLAIN_VALUE)
    public ResponseEntity<String> getName(
            final @PathVariable("documentType") TaxpayerDocumentType documentType,
            final @PathVariable("documentNumber") String documentNumber) throws NoSuchMethodException {
        try {
            final TaxpayerNameService taxpayerNameService = getTaxpayerNameServiceImpl(documentType.getCountry());
            return ResponseEntity.of(taxpayerNameService.getTaxpayerName(documentType, documentNumber));
        } catch (IOException ex) {
            log.error(String.format("Error querying [%s][%s]", documentType, documentNumber), ex);
            return ResponseEntity.internalServerError().build();
        }
    }

    private TaxpayerNameService getTaxpayerNameServiceImpl(final String country) {
        switch(country) {
            case "CR":
                return taxpayerNameServCR;
            case "HN":
                return taxpayerNameServHN;
            default:
                throw new IllegalArgumentException("Invalid country");
        }
    }
}

What I want to do is a more elegant/spring way to do it, other than this ugly method getTaxpayerNameServiceImpl.

Advertisement

Answer

Use BeanFactory to create beans programatically:

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.stereotype.Component;

@Component
public class TaxpayerNameServiceFactory implements BeanFactoryAware {
    private static final String BEAN_NAME_FORMAT = "TaxpayerNameServiceImpl%s";

    private BeanFactory beanFactory;

    public TaxpayerNameService getTaxpayerNameServiceImpl(String countryName) {
        try {
            return (TaxpayerNameService) beanFactory.getBean(String.format(BEAN_NAME_FORMAT, countryName));
        }
        catch(Exception e) {
            throw new TaxpayerNameServiceException(e.getMessage(), e);
        }
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }
}

TaxpayerNameServiceImplCR class:

import org.springframework.stereotype.Component;

@Component("TaxpayerNameServiceImplCR")
public class TaxpayerNameServiceImplCR implements TaxpayerNameService {

    //All methods

}

Rest controller class:

@RestController
@RequestMapping("/taxpayers")
public class TaxpayerController {

    @Autowired
    TaxpayerNameServiceFactory factory;

    @GetMapping(path = "/{documentType}-{documentNumber}/name", produces = MediaType.TEXT_PLAIN_VALUE)
    public ResponseEntity<String> getName(
            final @PathVariable("documentType") TaxpayerDocumentType documentType,
            final @PathVariable("documentNumber") String documentNumber) throws NoSuchMethodException {
        try {
            final TaxpayerNameService taxpayerNameService = factory.getTaxpayerNameServiceImpl(documentType.getCountry());
            return ResponseEntity.of(taxpayerNameService.getTaxpayerName(documentType, documentNumber));
        } catch (IOException ex) {
            log.error(String.format("Error querying [%s][%s]", documentType, documentNumber), ex);
            return ResponseEntity.internalServerError().build();
        }
    }
}
User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement