如何配置多个 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);
}
我目前正在编写一个 "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);
}