[英]Can I inject Map of all impl of a bean in Spring
我正在编写一个获取输入的服务,我需要根据该输入调用一个服务的某些实现。 此输入是需要调用的实现的名称列表。
public interface Processor {
Map<String, String> execute();
}
@Service("BUCKET_PROCESSOR")
public class BucketProcessor implements Processor {
..... //first impl
}
@Service("QUERY_PROCESSOR")
public class QueryProcessor implements Processor {
..... //second impl
}
@Service("SQL_PROCESSOR")
public class SQLProcessor implements Processor {
..... //third impl
}
然后我有一个服务,我想在其中注入所有这些 impl 的映射,以便我可以遍历输入并调用相应的 impl。
@Service
public class MyAysncClient {
@Autowired
private Map<String, Processor> processorMap;
public void execute(List<String> processors) {
List<Future> tasks = new ArrayList<>();
for (String p : processors) {
final Processor processor = this.processorMap.get(p);
processor.execute()
....
}
}
}
是的,您可以 - spring 默认启用此功能。 即,您可以定义将Map<String, Processor>
注入 spring bean。
这将指示 spring 查找作为Processor
接口实现的所有 bean,这些 bean 将是映射的值,相应的键将是 bean 名称。
所以问题中提供的代码应该可以工作。
检查著名的@Autowired注释的文档。
在“自动装配数组、集合和映射”一节中,它说明了以下内容:
在数组、集合或映射依赖类型的情况下,容器自动装配与声明的值类型匹配的所有 bean。 为此,必须将映射键声明为 String 类型,该类型将解析为相应的 bean 名称。 这样一个容器提供的集合将被排序,考虑目标组件的 Ordered 和 @Order 值,否则按照它们在容器中的注册顺序。 或者,单个匹配的目标 bean 也可能是一般类型的 Collection 或 Map 本身,因此被注入。
请参阅此示例- 相关部分是将地图注入测试的位置。
一个更好更优雅的方法是使用以下代码定义Service locator pattern
@Configuration
public class ProcessorConfig {
@Bean("processorFactory")
public FactoryBean<?> serviceLocatorFactoryBean() {
ServiceLocatorFactoryBean factoryBean = new ServiceLocatorFactoryBean();
factoryBean.setServiceLocatorInterface(ProcessorFactory.class);
return factoryBean;
}
}
public interface ProcessorFactory {
Processor getProcessor(ProcessorTypes processorTypes);
}
然后
public interface Processor {
Map<String, String> execute();
}
@Component(ProcessorTypes.ProcessorConstants.BUCKET_PROCESSOR)
@Slf4j
public class BucketProcessor implements Processor {
@Override
public Map<String, String> execute() {
return Collections.singletonMap("processor","BUCKET_PROCESSOR");
}
}
@Component(ProcessorTypes.ProcessorConstants.QUERY_PROCESSOR)
@Slf4j
public class QueryProcessor implements Processor {
@Override
public Map<String, String> execute() {
return Collections.singletonMap("processor","QUERY_PROCESSOR");
}
}
@Component(ProcessorTypes.ProcessorConstants.SQL_PROCESSOR)
@Slf4j
public class SqlProcessor implements Processor {
@Override
public Map<String, String> execute() {
return Collections.singletonMap("processor","SQL_PROCESSOR");
}
}
现在定义您的服务注入工厂
@Service
@RequiredArgsConstructor
@Slf4j
public class ProcessorService {
private final ProcessorFactory processorFactory;
public void parseIndividual(ProcessorTypes processorTypes) {
processorFactory
.getProcessor(processorTypes)
.execute();
}
public void parseAll(List<ProcessorTypes> processorTypes) {
processorTypes.forEach(this::parseIndividual);
}
}
在客户端,您可以通过以下方式执行
processorService.parseAll(Arrays.asList(ProcessorTypes.SQL, ProcessorTypes.BUCKET, ProcessorTypes.QUERY));
processorService.parseIndividual(ProcessorTypes.BUCKET);
如果您想公开为 REST API,您可以通过以下方式进行
@RestController
@RequestMapping("/processors")
@RequiredArgsConstructor
@Validated
public class ProcessorController {
private final ProcessorService processorService;
@GetMapping("/process")
public ResponseEntity<?> parseContent(@RequestParam("processorType") @Valid ProcessorTypes processorTypes) {
processorService.parseIndividual(ProcessorTypes.BUCKET);
return ResponseEntity.status(HttpStatus.OK).body("ok");
}
@GetMapping("/process-all")
public ResponseEntity<?> parseContent() {
processorService.parseAll(Arrays.asList(ProcessorTypes.SQL, ProcessorTypes.BUCKET, ProcessorTypes.QUERY));
return ResponseEntity.status(HttpStatus.OK).body("ok");
}
}
希望您的问题通过解决方案得到解决
你可以只使用getBeansOfType(Processor.class) :
返回具有匹配 bean 的 Map,包含 bean 名称作为键和相应的 bean 实例作为值
@Bean
public Map<String, Processor> processorMap(ApplicationContext context) {
return context.getBeansOfType(Processor.class);
}
我认为这会对您有所帮助,将 bean 配置添加到配置文件中
@Bean(name = "mapBean")
public Map<String, Processor > mapBean() {
Map<String, Processor > map = new HashMap<>();
//populate the map here
return map;
}
在您的服务中
@Service
public class MyAysncClient {
@Autowired
@Qualifier("mapBean")
private Map<String, Processor> processorMap;
public void execute(List<String> processors) {
List<Future> tasks = new ArrayList<>();
for (String p : processors) {
final Processor processor = this.processorMap.get(p);
processor.execute()
....
}
}
}
顺便说一句,如果您不需要 bean 的名称(根据您的示例),请定义一个列表,spring 将在同一接口上注入所有定义为服务的 bean
@Autowired
private List<Processor> processors; // include all defined beans
之后迭代它们中的每一个并调用execute方法。
是的,您可以,但它需要对您当前的代码进行一些改进才能使其以这种方式工作。 首先,您必须将getProcessorName
方法添加到Processor
接口:
public interface Processor {
Map<String, String> execute();
String getProcessorName();
}
当你实现它时,你应该在getProcessorName
方法的返回中设置它的名称
@Service
public class QueryProcessor implements Processor {
//...
@Override
public String getProcessorName() {
return "QUERY_PROCESSOR";
}
}
然后你必须创建一个 spring 配置或将 bean 创建添加到现有的配置中
@Configuration
public class MyShinyProcessorsConfiguration {
@Bean
@Qualifier("processorsMap")
public Map<String, Processor> processorsMap(List<Processor> processors) {
Map<String, Processor > procMap = new HashMap<>();
processors.forEach(processor -> procMap.put(processor.getProcessorName(), processor);
return procMap;
}
}
...然后您可以简单地将您的处理器映射添加到任何组件
@Service
public class MyAysncClient {
@Autowired
@Qualifier("processorsMap")
private Map<String, Processor> processorsMap;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.