注入多个相同类型的bean,并根据通用类型在它们之间自动select

Inject multiple beans of the same type and automatically select between them based on generic type

我有两个(将来会更多)ImportantService 的实现 – VeryImportantServiceLessImportantService:

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);
    }
}