为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

呼啦一阵风 2021-12-30 19:46:33
我是 Spring 框架的新手。我开发了一个独立的控制台应用程序。应用程序将获得多个不同格式(CSV、JSP、XML)的文件作为参数。我想根据文件格式注入解析器的某个实现。这是我的服务:@Servicepublic class ParsingService {private final Parser parser;@Autowiredpublic 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 根据文件格式注入必要的实现。
查看完整描述

3 回答

?
慕标琳琳

TA贡献1830条经验 获得超9个赞

假设这Parser是你自己的接口,你可以添加一个方法来告诉它能够解析的格式:


public interface Parser {

   List<Order> parse(String filePath);

   String getFormat();

}

然后在所有实现中覆盖它:


@Component

public class CsvParser implements Parser {


    public static final String FORMAT = "csv";


    public String getFormat(){

        return FORMAT;

    }


    // ...

}

通过使用 @Bean/@Component 注释类或在配置类中创建实例来配置解析器 bean。(如果您使用的是 SpringBoot,我建议您使用@ConditionalOn...注释以避免创建不必要的 bean)


现在您可以将所有Parser实例注入到ParserService.


@Service

public class ParsingService {


    private final Map<String, Parser> parsers;


    @Autowired

    public ParsingService(List<Parser> allParsers) {

        this.parsers = allParsers

                           .stream()

                           .collect(Collectors.toMap(Parser::getFormat, p -> p));

    }


    public List<Order> parse(String filePath) {

        try {

            String format = getFormat(filePath);

            Parser parser = parsers.get(format);

            if(parser == null) {

                // Replace this exception by a more appropriate one

                throw new RuntimeException("No parsers found for format : " + format);

            } else {

                return parser.parse(filePath);

            }

        } catch (Exception e) {

            e.printStackTrace();

        }

        return null;

    }


    private String getFormat(String filePath){

        int i = filePath.lastIndexOf('.');

        if (i > 0) {

            return filePath.substring(i+1).toLowerCase();

        } else {

            // Replace this exception by a more appropriate one

            throw new RuntimeException("Cannot determine the file format!");

        }   

    }

}

这样,您ParserService和Main类都不会依赖于您的自定义Parser实现。一旦你需要一个新的解析器,你可以简单地定义一个实现接口的新类。不再需要更改。


更新


添加Main和AppConfig类


public class Main {


    public static void main(String[] args) {

        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConf.class);

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


        for (String arg : args) {

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

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

        }

    }

}


@Configuration

@ComponentScan(basePackages = "your.root.package")

public class AppConf {

    // Do something here

}

对于并行处理,请尝试Main使用以下代码替换您的 for 循环:


 Arrays.stream(args)

       .parallel()

       .map(service::parse)

       .flatMap(List::stream)

       .forEach(System.out::println);

或者您可以使用ExecutorService:


int poolSize = 3;

ExecutorService executorService =  new ThreadPoolExecutor(poolSize, poolSize, 0L, TimeUnit.MILLISECONDS,

            new LinkedBlockingQueue<Runnable>());


for (String arg : args) {

    executorService.submit(() -> {

        service.parse(arg).forEach(System.out::println);

    });

}


查看完整回答
反对 回复 2021-12-30
?
墨色风雨

TA贡献1853条经验 获得超6个赞

您可能想要注入一组解析器


@Autowired

private List<Parser> parsers;

然后从该列表中选择正确的解析器。此外,这可以通过 Map Spring Annotations - Injecting Map of Objects 来完成


您可以在解析器接口中定义方法,该方法返回扩展集合,如下所示


 public interface Parser {

    List<String> getExtensions();

}

然后你可以利用 Java 8 流来寻找正确的解析器:


parsers.stream().filter(p->p.getExtensions().contains(extension)).findFirst();

这将返回可能包含所需解析器的可选当您添加解析器时,您需要的是添加解析器并定义扩展。无需更改 main 中的代码


查看完整回答
反对 回复 2021-12-30
?
牧羊人nacy

TA贡献1862条经验 获得超7个赞

我的建议是考虑使用 Spring Boot 和@ConditionalOnProperty注释。在下面的代码示例中,只有csvParserImpl当 的属性my.parser值为 时才会调用 bean csv。通过将属性值从 更改csv为json,jsonParserImpl将创建而不是csvParserImpl。如果my.parser未定义或设置为既不包含csv也不包含 的值json,则不会有 的实例Parser。


@Configuration

public class MyAutoconfiguration {

  @Bean

  @ConditionalOnProperty(name="my.parser", havingValue="csv")

  CsvParserImpl csvParserImpl() {

    return new CsvParserImpl();

  }


  @Bean

  @ConditionalOnProperty(name="my.parser", havingValue="json")

  JsonParserImpl jsonParserImpl() {

    return new JsonParserImpl();

  }

}

当我提到“财产”时,它在 Spring Boot 中具有特定的含义。 Spring Boot 中的外部化配置可以从多个来源拉取属性值,包括环境变量、系统变量和命令行变量。


查看完整回答
反对 回复 2021-12-30
  • 3 回答
  • 0 关注
  • 168 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信