弹簧。如何根据应用程序参数注入依赖关系?

时间:2018-11-05 13:04:32

标签: java spring

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

my service and parsers

这些是我的服务

@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根据文件格式注入必要的实现。

3 个答案:

答案 0 :(得分:0)

您可能想注入解析器集合

@Autowired
private List<Parser> parsers;

然后从该列表中选择正确的解析器。 另外,通过地图可以做到 Spring Annotations - Injecting Map of Objects

您可以在解析器界面中定义方法,该方法返回扩展的集合,像这样

 public interface Parser {
    List<String> getExtensions();
}

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

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

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

答案 1 :(得分:0)

我的建议是考虑使用Spring Boot和@ConditionalOnProperty注释。在下面的代码示例中,如果csvParserImpl的属性的值为my.parser,将永远只有一个名为csv的bean。通过将属性值从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();
  }
}

当我指的是“财产”时,在弹簧靴中有特定含义。春季启动中的Externalized Configuration可以从多个来源提取属性值,包括环境变量,系统变量和命令行变量。

答案 2 :(得分:0)

假设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注释类或通过在config类中创建实例来配置解析器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!");
        }   
    }
}

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

更新

添加MainAppConfig

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