猿问

如何根据应用程序参数注入依赖项?

我是 Spring 框架的新手。我开发了一个独立的控制台应用程序。应用程序将获得多个不同格式(CSV、JSP、XML)的文件作为参数。我想根据文件格式注入解析器的某个实现。

这是我的服务:


@Service

public class ParsingService {


private final Parser parser;


@Autowired

public ParsingService(Parser parser) {

    this.parser = parser;

}


public List<Order> parse(String filePath) {

    try {

        return parser.parse(filePath);

    } catch (IOException e) {

        e.printStackTrace();

    }

    return null;

}

}

我的主要课程:


public class Main {

public static void main(String[] args) throws IOException {

    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConf.class);

    for (String arg : args) {

        ParsingService service = context.getBean(ParsingService.class);

        List<Order> listOfParsedObjects = service.parse(arg);

        listOfParsedObjects.forEach(System.out::println);

    }

}

}

我将向命令行传递几个文件路径,我需要 Spring 根据文件格式注入必要的实现。


呼啦一阵风
浏览 169回答 3
3回答

慕标琳琳

假设这Parser是你自己的接口,你可以添加一个方法来告诉它能够解析的格式:public interface Parser {&nbsp; &nbsp;List<Order> parse(String filePath);&nbsp; &nbsp;String getFormat();}然后在所有实现中覆盖它:@Componentpublic class CsvParser implements Parser {&nbsp; &nbsp; public static final String FORMAT = "csv";&nbsp; &nbsp; public String getFormat(){&nbsp; &nbsp; &nbsp; &nbsp; return FORMAT;&nbsp; &nbsp; }&nbsp; &nbsp; // ...}通过使用 @Bean/@Component 注释类或在配置类中创建实例来配置解析器 bean。(如果您使用的是 SpringBoot,我建议您使用@ConditionalOn...注释以避免创建不必要的 bean)现在您可以将所有Parser实例注入到ParserService.@Servicepublic class ParsingService {&nbsp; &nbsp; private final Map<String, Parser> parsers;&nbsp; &nbsp; @Autowired&nbsp; &nbsp; public ParsingService(List<Parser> allParsers) {&nbsp; &nbsp; &nbsp; &nbsp; this.parsers = allParsers&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;.stream()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;.collect(Collectors.toMap(Parser::getFormat, p -> p));&nbsp; &nbsp; }&nbsp; &nbsp; public List<Order> parse(String filePath) {&nbsp; &nbsp; &nbsp; &nbsp; try {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; String format = getFormat(filePath);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Parser parser = parsers.get(format);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if(parser == null) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Replace this exception by a more appropriate one&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; throw new RuntimeException("No parsers found for format : " + format);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return parser.parse(filePath);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; } catch (Exception e) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; e.printStackTrace();&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; return null;&nbsp; &nbsp; }&nbsp; &nbsp; private String getFormat(String filePath){&nbsp; &nbsp; &nbsp; &nbsp; int i = filePath.lastIndexOf('.');&nbsp; &nbsp; &nbsp; &nbsp; if (i > 0) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return filePath.substring(i+1).toLowerCase();&nbsp; &nbsp; &nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Replace this exception by a more appropriate one&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; throw new RuntimeException("Cannot determine the file format!");&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp;&nbsp; &nbsp; }}这样,您ParserService和Main类都不会依赖于您的自定义Parser实现。一旦你需要一个新的解析器,你可以简单地定义一个实现接口的新类。不再需要更改。更新添加Main和AppConfig类public class Main {&nbsp; &nbsp; public static void main(String[] args) {&nbsp; &nbsp; &nbsp; &nbsp; AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConf.class);&nbsp; &nbsp; &nbsp; &nbsp; ParsingService service = context.getBean(ParsingService.class);&nbsp; &nbsp; &nbsp; &nbsp; for (String arg : args) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; List<Order> listOfParsedObjects = service.parse(arg);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; listOfParsedObjects.forEach(System.out::println);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}@Configuration@ComponentScan(basePackages = "your.root.package")public class AppConf {&nbsp; &nbsp; // Do something here}对于并行处理,请尝试Main使用以下代码替换您的 for 循环:&nbsp;Arrays.stream(args)&nbsp; &nbsp; &nbsp; &nbsp;.parallel()&nbsp; &nbsp; &nbsp; &nbsp;.map(service::parse)&nbsp; &nbsp; &nbsp; &nbsp;.flatMap(List::stream)&nbsp; &nbsp; &nbsp; &nbsp;.forEach(System.out::println);或者您可以使用ExecutorService:int poolSize = 3;ExecutorService executorService =&nbsp; new ThreadPoolExecutor(poolSize, poolSize, 0L, TimeUnit.MILLISECONDS,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; new LinkedBlockingQueue<Runnable>());for (String arg : args) {&nbsp; &nbsp; executorService.submit(() -> {&nbsp; &nbsp; &nbsp; &nbsp; service.parse(arg).forEach(System.out::println);&nbsp; &nbsp; });}

墨色风雨

您可能想要注入一组解析器@Autowiredprivate List<Parser> parsers;然后从该列表中选择正确的解析器。此外,这可以通过 Map Spring Annotations - Injecting Map of Objects 来完成您可以在解析器接口中定义方法,该方法返回扩展集合,如下所示&nbsp;public interface Parser {&nbsp; &nbsp; List<String> getExtensions();}然后你可以利用 Java 8 流来寻找正确的解析器:parsers.stream().filter(p->p.getExtensions().contains(extension)).findFirst();这将返回可能包含所需解析器的可选当您添加解析器时,您需要的是添加解析器并定义扩展。无需更改 main 中的代码

牧羊人nacy

我的建议是考虑使用 Spring Boot 和@ConditionalOnProperty注释。在下面的代码示例中,只有csvParserImpl当 的属性my.parser值为 时才会调用 bean csv。通过将属性值从 更改csv为json,jsonParserImpl将创建而不是csvParserImpl。如果my.parser未定义或设置为既不包含csv也不包含 的值json,则不会有 的实例Parser。@Configurationpublic class MyAutoconfiguration {&nbsp; @Bean&nbsp; @ConditionalOnProperty(name="my.parser", havingValue="csv")&nbsp; CsvParserImpl csvParserImpl() {&nbsp; &nbsp; return new CsvParserImpl();&nbsp; }&nbsp; @Bean&nbsp; @ConditionalOnProperty(name="my.parser", havingValue="json")&nbsp; JsonParserImpl jsonParserImpl() {&nbsp; &nbsp; return new JsonParserImpl();&nbsp; }}当我提到“财产”时,它在 Spring Boot 中具有特定的含义。 Spring Boot 中的外部化配置可以从多个来源拉取属性值,包括环境变量、系统变量和命令行变量。
随时随地看视频慕课网APP

相关分类

Python
我要回答