有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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

我目前正在写一个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) 个答案

  1. # 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")定义其他名称
  2. # 2 楼答案

    除了使用@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);
    }