如何在当前调用中根据 RequestParam 注入正确的 bean 实现

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

我有这个 Spring bean(这个 RestController 是为了示例),根据国家/地区(假设传入的参数),我想 注入TaxpayerNameService.

的执行

所以,我有那个 TaxpayerNameService 接口和两个(将来会更多)这种接口的实现,需要在控制器的 当前调用 中注入;我说 current call 因为同一个控制器将服务于许多国家,并且取决于我在某处发送的 iso2 常量(现在它来自 documentType.getCountry(),我有在运行时 检索 正确的 TaxpayerNameService 实现并调用该方法 getTaxpayerName.

每个国家/地区都有不同的服务集,因此接口的每个实现都会正确调用正确的服务。

@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");
        }
    }
}

除了这种丑陋的方法 getTaxpayerNameServiceImpl.

,我想做的是更 elegant/spring 的方法

使用BeanFactory以编程方式创建bean:

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

}

休息控制器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();
        }
    }
}

或者这个?

    public enum Country {
    
    HR("HR", TaxpayerNameServiceImplHR.class),
    CR("CR", TaxpayerNameServiceImplCR.class);
    
    private String code;
    private Class<? extends TaxpayerNameService> serviceClass;
    
    Country(String code, Class<? extends TaxpayerNameService> svcClass) {
        this.code = code;
        this.serviceClass = svcClass;
    }

    public String getCode() {
        return code;
    }

    public Class<? extends TaxpayerNameService> getServiceClass() {
        return serviceClass;
    }

    public static Optional<Country> findCountry(String code) {
        return java.util.stream.Stream.of(Country.values())
            .filter(country -> code.equals(country.getCode()))
            .findFirst();
    }
}

public class TaxPayerController {

    @Autowired
    private ApplicationContext context;

    public ResponseEntity<String> getName(final @PathVariable("documentType") TaxpayerDocumentType documentType,
            final @PathVariable("documentNumber") String documentNumber) throws NoSuchMethodException {

        Optional<Country> optionalCountry = Country.findCountry(documentType.getCountry());

        if (optionalCountry.isEmpty()) {
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST);
        }

        final TaxpayerNameService taxpayerNameService = context.getBean(optionalCountry.get().getServiceClass());
        return ResponseEntity.ok(taxpayerNameService.getTaxpayerName(documentType, documentNumber));

    }

}