【问题标题】:Injecting a List of Strings From a Properties File Using Guice使用 Guice 从属性文件中注入字符串列表
【发布时间】:2021-09-30 13:39:40
【问题描述】:

我有一个应用程序需要从属性文件中读取一些配置值,然后使用 Guice 的 @Named 注释注入这些值。我目前在我的模块中做这样的事情:

@Override
protected void configure() {
    try {
        Properties properties = getProperties();
        Names.bindProperties(binder(), properties);
        binder().bind(MyApi.class);
     } catch (Exception e) {
        log.error(e.getMessage());
        throw new RuntimeException(e);
    }
}

然后在我的应用程序中,我执行以下操作:

public class MyApi
{
    @Inject
    @Named("ftp.dir")
    public String ftpDirectory;
}

这种方法可以很好地注入原始值,但现在我需要注入一个字符串列表。如果我只是添加注入

@Inject
@Named("mail.to")
public List<String> mailTo;

对于像这样的属性文件:

#Properties
ftp.dir=/opt/my/dir
mail.to=foo@bar.com, abc@123.com

那么 guice 会抛出一个异常说

'没有实现 java.util.List 注释 @google.inject.name.Named(value=mail.to) 已绑定。

我还看到了一些其他类似的问题,例如: Guice : Inject an ArrayList of StringsInjecting list of strings in Guice 但是所有的解决方案都采用为字段添加显式绑定的方法。

有什么方法可以在 guice 中处理集合,而无需手动从属性文件中提取它们并添加显式绑定?

【问题讨论】:

  • 你能把.properties文件包括进来吗?
  • @GeorgeZ。我编辑了帖子以包含属性示例

标签: java dependency-injection collections properties guice


【解决方案1】:

如果将此方法添加到模块中会怎样?

@Named("mail.to") List<String> provideMailtoAsList(@Named("mail.to") String mailto) {
  return List.of(mailto.trim().split("\\s*,\\s*"));
}

这会将名为“mail.to”的已知String 转换为恰好也名为“mail.to”的List&lt;String&gt;

【讨论】:

  • 添加提供者方法是一种选择,事实上这是我正在使用的工作,直到我能想出更好的东西。但问题是是否有办法做到这一点,而不必为每个包含列表的属性设置单独的提供程序。
  • 不,不使用它是不可能的(这对我来说似乎不是一种解决方法)。原因是您可能想要逗号作为分隔符,但如果其他人想要破折号作为分隔符、管道或分号怎么办?此外,人们如何知道空间是否重要?所以不,这是不可能的,因为有太多的可能性。所以只要使用可能的东西,这就是我在上面提供的解决方案。
【解决方案2】:

我设法找到了满足我需求的解决方案。 Guice 3.0 有一个通过 Binder.convertToTypes() 方法添加自定义类型转换器的方法。我只是简单地调用了这个方法并传入了 com.google.inject.spi.TypeConverter 的实现,该实现将获取属性值并将其从字符串转换为列表。我模块中的配置方法最终看起来像这样:

@Override
protected void configure() {
    try {
        //I'm using apache commons-configurations here but you could
        //just as easily use just a java Properties object
        Configuration properties = getProperties();
 
        //I'm using the builtin Matchers.only from guice for this simple 
        //case but you could implement a custom Matcher for more complex
        //behavior
        binder().convertToTypes(Matchers.only(new TypeLiteral<List<String>>(){}),
     new TypeConverter() {
            //again, I'm using commons-configuration to perform the 
            //actual conversion but you could use a different library
            //or even do the conversion yourself if needed
            final DefaultListDelimiterHandler handler = new DefaultListDelimiterHandler(',');
            @Override
            public Collection<String> convert(String value, TypeLiteral<?> toType) {
                
                return handler.split(value, true);
            }
        });

        //now that guice has a converter registered for List<String>
        //we can simply use bindProperties to set up the Named bindings
        Names.bindProperties(binder(), ConfigurationConverter.getProperties( properties));
        binder().bind(MyApi.class);
    } catch (Exception e) {
        log.error(e.getMessage());
        throw new RuntimeException(e);
    }
}

此解决方案的优点是它减少了我的 .properties 文件与实际代码之间的耦合。如果我想使用 List 值添加其他属性,我可以简单地更改属性文件,而无需为每个新属性添加自定义提供程序方法。我的代码和属性文件之间的唯一耦合是在 @Named 注释中。

可能有更好的解决方案;如果你有什么让我知道。如果打算再开放几天,以防有人有更好的建议。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-05-28
    • 2012-04-04
    • 2015-08-28
    • 2016-09-22
    • 1970-01-01
    • 1970-01-01
    • 2014-12-21
    相关资源
    最近更新 更多