我可以在春季注入所有bean的impl的Map吗

时间:2020-08-30 15:23:08

标签: java spring spring-boot

我正在编写一个获取输入的服务,基于该输入我需要调用某个服务的某些隐式实现。此输入是需要调用的impls名称的列表。

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()

            ....
        }

    }
}

5 个答案:

答案 0 :(得分:2)

是的,您可以-spring默认情况下启用此功能。即,您可以定义将Map<String, Processor>注入到spring bean中。

这将指示spring查找作为Processor接口实现的所有bean,这些将是map的值,相应的键将是bean名称。

因此问题中显示的代码应该可以工作。

检查著名的@Autowired注释的文档。

在“自动装配数组,集合和映射”部分中,其内容如下:

对于数组,Collection或Map依赖项类型,容器将自动装配与声明的值类型匹配的所有bean。为此,映射键必须声明为String类型,它将解析为相应的bean名称。将根据目标组件的Ordered和@Order值对容器提供的此类集合进行排序,否则将遵循其在容器中的注册顺序。另外,单个匹配的目标bean也可以是一般类型的Collection或Map本身,就这样注入。

请参见This example-其中的相关部分是将地图注入到测试中的地方。

答案 1 :(得分:1)

更好且优雅的方法是 使用以下代码定义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");
    }
}

希望您的问题得到解决方案的解决

答案 2 :(得分:1)

您可以只使用getBeansOfType(Processor.class)

返回具有匹配bean的Map,其中包含bean名称作为键,并包含对应的bean实例作为值

isKeyExists(keyStore)

答案 3 :(得分:0)

我认为这对您有帮助,将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的名称(根据您的示例),那么定义一个list,spring将在同一接口上注入所有定义为服务的bean

@Autowired 
private List<Processor> processors; // include all defined beans

之后,对它们中的每一个进行迭代并调用execute方法。

答案 4 :(得分:0)

是的,可以,但是需要对当前代码进行一些改进才能使其以这种方式工作。 首先,您必须向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;  
    
}
相关问题