Autowire List<List<String>> 超出属性文件,密钥长度未知

Autowire List<List<String>> out of properties file with unknown keys length

有没有办法在从属性文件读取的另一个列表中自动装配一个包含字符串的列表?我发现的困难是 属性-values 需要拆分成一个字符串列表(或数组),然后自动连接到。 我的属性文件看起来像这样:

jobFolders1=C:/Temp/originFolder, C:/Temp/trafoIn, C:/Temp/trafoOut, C:/Temp/destinationFolder
jobFolders2=C:/Temp/originFolder2, C:/Temp/trafoIn2, C:/Temp/trafoOut2, C:/Temp/destinationFolder2

现在我希望我的用户能够在有新工作时向该文件添加行。所以我永远不知道按键的名称,也不知道行数。 有什么方法可以将文件条目自动连接到一个列表,该列表本身包含一个包含 4 个字符串的列表(由“,”分隔)? 可能这整个方法不是最好的。如果是这样,请随时告诉我。

您可以使用 Apache Commons Configuration。 getKeys() 方法将为您提供配置中包含的键列表。刷新策略确保您能够动态加载在运行时添加的新属性。为了您的方便,我写了一个示例示例,但尚未对其进行测试。

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;
import org.apache.log4j.Logger;

public class SystemProperties {

    private static final Logger LOG = Logger.getLogger(SystemProperties.class.getSimpleName());;

    private static SystemProperties singleton = new SystemProperties();

    private PropertiesConfiguration conf = null;
    private final FileChangedReloadingStrategy strategy = new FileChangedReloadingStrategy();

    private static final long REFRESH_INTERVAL = 2000;

    private final String configurationFilePath = "/my-properties-files/system.properties";

    private SystemProperties() 
    {
        conf = new PropertiesConfiguration();
        conf.setDelimiterParsingDisabled(true);

        if (conf.getFile() == null) 
        {
            try {
                URL url = getClass().getResource(configurationFilePath);

                File configurationFile = new File(url.getFile());
                InputStream in = new FileInputStream(url.getFile());
                conf.load(in);
                conf.setFile(configurationFile);
            } catch (final Exception ex) {
                LOG.error("SystemProperties: Could not load properties file ", ex);
            }
        }

        if (conf.getFile() == null) 
        {
            LOG.warn("File could not be loaded");
        }

        strategy.setRefreshDelay(REFRESH_INTERVAL);
        conf.setReloadingStrategy(strategy);
        conf.setAutoSave(true);
    }

    public static SystemProperties getInstance() 
    {
        return singleton;
    }

    public String get(String s) 
    {
        return conf.getString(s);
    }

    public List<String> getKeys() 
    {
        @SuppressWarnings("unchecked")
        final Iterator<String> keysIterator = conf.getKeys();
        if (keysIterator != null) {
            final List<String> keysList = new ArrayList<String>();
            while(keysIterator.hasNext()) {
                keysList.add(keysIterator.next());
            }
            return keysList; 
        }
        return Collections.emptyList();
    }

    public void setProperty(String property, String value) throws Exception 
    {
        conf.setProperty(property, value);
    }
}

您可以做的是将所有作业的所有目录存储在一个值中。这些作业将由一个特殊字符分隔,该字符肯定不会在任何目录名称中使用。

为了保持可读性,您可以use a multiline property value。例如,使用 |(管道)作为分隔符:

# Starting with a line break for readability.
# Looks nice, but the first array value will be empty.
# Just leave it out if you don't want this.
jobFolders=|\
    C:/Temp/originFolder, C:/Temp/trafoIn, C:/Temp/trafoOut, C:/Temp/destinationFolder|\
    C:/Temp/originFolder2, C:/Temp/trafoIn2, C:/Temp/trafoOut2, C:/Temp/destinationFolder2

在您的代码中,您可以简单地应用

String[] jobs = jobFolders.split("|");

到该值以获取所有作业配置的数组。


旁注: 分隔符可以是 reserved filename characters 中的任何一个,但请务必选择在您的目标平台上肯定无效的分隔符。阅读维基百科文章了解更多信息。

我在示例中使用了|,这在普通操作系统的文件名中是不允许的,但是可能在Unix文件名中是允许的。

好的,下面是一个相当 "springy" 的解决方案,尽管我认为可以更优雅地解决这个问题(根本不需要编写自定义代码):

编写 PropertyMapper:

@Component("PropertyMapper")
public class PropertyMapper {

@Autowired
ApplicationContext context;
@Autowired
List<List<String>> split;

public List<List<String>> splitValues(final String beanname) {
((Properties) this.context.getBean(beanname)).values().forEach(v -> {
final List<String> paths = Arrays.asList(((String) v).split(","));
paths.forEach(p -> paths.set(paths.indexOf(p), p.trim()));
this.split.add(paths);
});
return this.split;
}

像这样在 context.xml 中加载属性:

<util:properties id="testProps" location="classpath:test.properties"/>

然后通过调用 splitValues 方法使用 Spring EL 'tweaking' 原始值将值连接到字段:

@Value("#{PropertyMapper.splitValues('testProps')}")
private List<List<String>> allPaths;