如何配置多个Spring Bean并选择使用哪个?可能吗?

时间:2019-06-04 14:27:07

标签: java spring spring-boot autowired

我目前正在编写TDEE计算器,该计算器的“每日总能源消耗”。换句话说,您一天燃烧的卡路里大约是多少。

有三个不同的公式。我想让最终用户选择他们要选择计算的公式。

现在我正在关注Spring Structure

在创建程序包和它们各自的类时,我意识到服务类将具有相同的方法getTdee(),但是实现方式却不同。最好是,我想使用具有三种不同实现的接口,但是我知道您不能自动装配多个bean。有什么解决办法吗?还是我注定要重复三个程序包,每个程序包包含一个控制器,服务,请求有效负载?

控制器:

@RestController
@RequestMapping("/tdee")
public class Tdee{
    private TdeeService tdeeService

    @Inject
    public Tdee(TdeeService tdeeService){
        this.tdeeService = tdeeService;
    }

    @PostMapping
    public getTdee(){
        return tdeeService.getTdee();
    }
}

服务:

@Named
public class TdeeService{
   public int getTdee(){
       //logic here
   }
}    

最好将TdeeService切换到一个接口,并实现所有三个公式:

@Named
KatchTdeeServiceImpl implements TdeeService{

    @Override
    public int getTdee(){
        //logic here
    }
}

@Named
HarrisTdeeServiceImpl implements TdeeService{

    @Override
    public int getTdee(){
        //logic here
    }
}


@Named
MiffinTdeeServiceImpl implements TdeeService{

    @Override
    public int getTdee(){
        //logic here
    }
}

总结我的问题:

理想情况下,我想创建一个具有一个服务接口,三个服务实现,一个控制器和一个有效载荷类的包,而不是创建三个具有控制器,服务和有效载荷的包。谢谢!

2 个答案:

答案 0 :(得分:1)

那么您将根据用户传递的某些参数来确定要使用哪些实现?

实际上,如果有多个实现相同接口的bean,也可以注入它们。一种方法是使用@Qualifier指定要注入的bean(按bean名称):

@RestController
@RequestMapping("/tdee")
public class Tdee{

    @Autowired 
    @Qualifier("katchTdeeServiceImpl")
    private TdeeService katchTdeeServce;

    @Autowired 
    @Qualifier("harrisTdeeServiceImpl")
    private TdeeService harrisTdeeService;


    @PostMapping
    public getTdee(FooRequest request){

        if(request.isKatch()){
            katchTdeeServce.getTdee();
        }else if(requst.isHarris()){
            harrisTdeeService.getTdee();
        }

    }
}

注意:

  • 您必须使用@Autowired进行注入。 @Inject似乎不起作用 与@Qualifier
  • @Qualifier是spring软件包中的一个,而不是javax.inject
  • 中的那个
  • 默认bean名称是类名的小写驼峰式。您可以使用@Named("foo")来定义其他名称。

答案 1 :(得分:1)

除了使用@Qualifier来唯一标识正确的实现所说的以外,您还可以自动装配一系列bean,例如:

@Autowired
private List<TdeeService> tdeeServices;

这使您可以提供一种更动态的方法。例如,假设您使用以下方法扩展TdeeService界面:

boolean isSupported(String calculationType);

您可以这样实现:

@Override
public boolean isSupported(String calculationType) {
    return "Harris".equals(calculationType);
}

最后,我假设您会在某个地方有一个@RequestParam,用于标识您要使用哪种计算类型。如果是这种情况,您可以遍历tdeeServices,调用isSupported()方法以找到正确的实现,然后使用该方法实际进行计算。

例如:

@GetMapping
public int getTdee(@RequestParam String calculationType) {
    return tdeeServices
        .stream()
        // Filter out the TdeeService that are not supported
        .filter(service -> service.isSupported(calculationType))
        // Obtain the amount of calories
        .map(TdeeService::getTdee)
        // Get any of the results
        // Ideally, you'll only have one implementation that returns true for a specific calculationType
        // If multiple implementation returned 'true', any will be picked
        .findAny()
        // If the calculationType is not supported, an exception will be thrown
        .orElseThrow(UnsupportedCalculationTypeException::new);
}