注入多个相同类型的bean,并根据通用类型在它们之间自动select
Inject multiple beans of the same type and automatically select between them based on generic type
我有两个(将来会更多)ImportantService
的实现 – VeryImportantService
和 LessImportantService
:
public interface ImportantService<T extends ImportantRequest> {}
@Service
public class VeryImportantService implements ImportantService<VeryImportantRequest> {}
@Service
public class LessImportantService implements ImportantService<LessImportantRequest> {}
然后我有一个控制器,我想在其中注入 ImportantService
:
的所有实现
@RequiredArgsConstructor
@RestController
@RequestMapping("/api/important")
public class ImportantController<T extends ImportantRequest> {
private final ImportantService<T> importantService;
@PostMapping
public ResponseEntity<ImportantResponse> create(@RequestBody @Valid T request) {
// very important code here
}
}
很明显,这样的注入王失败了:
UnsatisfiedDependencyException: Error creating bean with name 'importantController' defined in file ...
...
Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed
我想要的是:
注入ImportantService
的所有实现,然后,基于T
自动select需要的bean。我知道我可以向 ImportantService
添加方法,returns 实现所使用的类型,然后将 ImportantService
作为 List<ImportantService> importantServices
注入,然后像这样过滤:
importantServices.stream()
.filter(importantService -> importantService.getType().equals(request.getClass()))
.findFirst()
.ifPresent(importantService -> importantService.doImportantJob(request));
但是!我有数百个服务需要像这样重构,我真的不想为控制器编写额外的逻辑。
我知道 @Conditional
注释和 Condition
界面,但是据我所知,没有办法让它们做我想做的事。
您想要的是 ImportantService 类型的 bean 列表
所以你必须像这样声明一个变量。
final List<ImportantService> importantServices;
demoController(List<ImportantService> importantServices) {
this.importantServices = importantServices;
}
为什么不实施 proxy pattern?
示例:
@Service
@Primary
@RequiredArgsConstructor
public class ImportantServiceProxy implements ImportantService<T extends ImportantRequest> {
private final List<ImportantService> importantServices;
private ImportantService getImportantService(ImportantRequest request){
return this.importantServices.stream()
.filter(importantService -> importantService.getType().equals(request.getClass()))
.findFirst()
.get();
}
public void doImportantJob(ImportantRequest request){
this.getImportantService(request).doImportantJob(request);
}
}
然后在你的控制器中你可以调用函数而不检查类型。
@RequiredArgsConstructor
@RestController
@RequestMapping("/api/important")
public class ImportantController<T extends ImportantRequest> {
private final ImportantService<T> importantService;
@PostMapping
public ResponseEntity<ImportantResponse> create(@RequestBody @Valid T request) {
importantService.doImportantJob(request);
}
}
我有两个(将来会更多)ImportantService
的实现 – VeryImportantService
和 LessImportantService
:
public interface ImportantService<T extends ImportantRequest> {}
@Service
public class VeryImportantService implements ImportantService<VeryImportantRequest> {}
@Service
public class LessImportantService implements ImportantService<LessImportantRequest> {}
然后我有一个控制器,我想在其中注入 ImportantService
:
@RequiredArgsConstructor
@RestController
@RequestMapping("/api/important")
public class ImportantController<T extends ImportantRequest> {
private final ImportantService<T> importantService;
@PostMapping
public ResponseEntity<ImportantResponse> create(@RequestBody @Valid T request) {
// very important code here
}
}
很明显,这样的注入王失败了:
UnsatisfiedDependencyException: Error creating bean with name 'importantController' defined in file ...
...
Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed
我想要的是:
注入ImportantService
的所有实现,然后,基于T
自动select需要的bean。我知道我可以向 ImportantService
添加方法,returns 实现所使用的类型,然后将 ImportantService
作为 List<ImportantService> importantServices
注入,然后像这样过滤:
importantServices.stream()
.filter(importantService -> importantService.getType().equals(request.getClass()))
.findFirst()
.ifPresent(importantService -> importantService.doImportantJob(request));
但是!我有数百个服务需要像这样重构,我真的不想为控制器编写额外的逻辑。
我知道 @Conditional
注释和 Condition
界面,但是据我所知,没有办法让它们做我想做的事。
您想要的是 ImportantService 类型的 bean 列表 所以你必须像这样声明一个变量。
final List<ImportantService> importantServices;
demoController(List<ImportantService> importantServices) {
this.importantServices = importantServices;
}
为什么不实施 proxy pattern?
示例:
@Service
@Primary
@RequiredArgsConstructor
public class ImportantServiceProxy implements ImportantService<T extends ImportantRequest> {
private final List<ImportantService> importantServices;
private ImportantService getImportantService(ImportantRequest request){
return this.importantServices.stream()
.filter(importantService -> importantService.getType().equals(request.getClass()))
.findFirst()
.get();
}
public void doImportantJob(ImportantRequest request){
this.getImportantService(request).doImportantJob(request);
}
}
然后在你的控制器中你可以调用函数而不检查类型。
@RequiredArgsConstructor
@RestController
@RequestMapping("/api/important")
public class ImportantController<T extends ImportantRequest> {
private final ImportantService<T> importantService;
@PostMapping
public ResponseEntity<ImportantResponse> create(@RequestBody @Valid T request) {
importantService.doImportantJob(request);
}
}