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

Posted

技术标签:

【中文标题】如何在当前调用中根据 RequestParam 注入正确的 bean 实现【英文标题】:How to inject the right bean implementation according to a RequestParam in the current call 【发布时间】:2022-01-21 07:04:10 【问题描述】:

我有这个 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之外,我想做的是一种更优雅/更灵活的方法。

【问题讨论】:

我会注入一个实现 TaxPayerNameService 的 bean 列表,给该接口一个 String getCountry() 方法,然后将它们放入构造函数中的 Map 或单独的 @987654332 @豆。 还有一个小问题:当你抛出IllegalArgumentException时,请报告错误的国家。 “优雅/弹簧方式”是在@RequestScope 中创建FactoryBean,坏消息是您在实现org.springframework.beans.factory.FactoryBean#getObject 时需要手动解析请求uri 【参考方案1】:

使用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 类:

import org.springframework.stereotype.Component;

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

    //All methods


休息控制器类:

@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();
        
    

【讨论】:

当你抛出 TaxpayerNameServiceException 时,你应该传递 e 作为原因。【参考方案2】:

或者这个?

    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));

    


【讨论】:

以上是关于如何在当前调用中根据 RequestParam 注入正确的 bean 实现的主要内容,如果未能解决你的问题,请参考以下文章

Eureka篇三Eureka如何管理服务调用

在拦截器中获取 RequestParam 的值

百度地图api 怎么获取当前视野内标注?

@RequestParam设置默认可以传空值

@RequestParam设置默认可以传空值

如何在Spring中将@RequestParam绑定到对象