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

How to configure multiple Spring beans and choose which one to use? Is it possible?

我目前正在编写一个 "Total Daily Energy Expenditure" 的 TDEE 计算器。换句话说,就是你一天消耗的卡路里的近似值。

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

现在我正在关注Spring Structure

当我创建我的包和它们各自的 classes 时,我意识到服务 class 将具有相同的方法 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
    }
}

总结一下我的问题:

理想情况下,我想创建一个包,其中包含一个服务接口、三个服务实现、一个控制器和一个有效载荷 class,而不是创建三个包,每个包都有一个控制器、服务和一个有效载荷。谢谢!

所以你会根据用户传递的一些参数来决定使用哪些实现?

实际上如果你有多个实现相同接口的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 名称是 class 名称的小驼峰命名。您可以使用 @Named("foo") 定义其他名称。

除了已经说过的使用 @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);
}