如何引用java.util.Properties中的另一个属性?

时间:2009-05-16 11:50:31

标签: java properties

Java属性文件可以引用其他属性文件吗?

## define a default directory for Input files  
dir.default=/home/data/in/

dir.proj1=${dir.default}p1
dir.proj2=${dir.default}p2
dir.proj3=${dir.default}p3

这可能吗?

12 个答案:

答案 0 :(得分:51)

这就是你想要的,它有点陈旧,但可能适合你的需要。

Enabling constant substitution in Property Values

  

您可以在属性值的任何位置替换常量,甚至在值中包含多个常量,如下例所示:

CONST_1 = shoes and ships
CONST_2 = sealing wax
SomeValue = {CONST_1} and {CONST_2} 
  

在此示例中,“SomeValue”属性的评估结果为“鞋子和船只以及封蜡。”

答案 1 :(得分:12)

Eproperties is the open source project提供变量替换以及一些其他功能 - 尽管替换可能是最有用的。它是java.util.Properties的子类,可以被任何其他可以将配置信息作为属性的类使用。

答案 2 :(得分:7)

Commons Config lib也可以这样做。 http://commons.apache.org/configuration/userguide/overview.html#Using_Configuration

但是,正如已经指出的那样,看一下EProperties库; http://code.google.com/p/eproperties/

它支持许多简洁的功能(如替换,嵌套,列表),包括包含,扩展Java属性,并且比Commons Config(它还允许您使用include语法包含属性)更轻一些。

答案 3 :(得分:6)

标准属性文件只是键值对。在文本格式中,Properties只是将键与值分开,并执行一些简单的操作,例如允许转义字符。您可以使用详细的XML语法定义实体。

如果您需要自己的替换语法,则可以像处理任何其他字符串一样操纵返回的值。或者,您可以编写自己的Properties版本,或在生成文件时执行替换。

答案 4 :(得分:5)

java.util.Properties类不会为您执行此操作。子类化属性,覆盖load()方法并自己进行替换也不会太困难。

答案 5 :(得分:3)

由于eproperties有点没有维护,并且commons配置依赖于日志记录(具有讽刺意味着你不能用它来配置日志记录)我使用这个代码片段只需要commons-lang(3)来加载插值属性:

@SuppressWarnings("serial")
public static Map<String,String> loadPropertiesMap(InputStream s) throws IOException {
    final Map<String, String> ordered = new LinkedHashMap<String, String>();
    //Hack to use properties class to parse but our map for preserved order
    Properties bp = new Properties() {
        @Override
        public synchronized Object put(Object key, Object value) {
            ordered.put((String)key, (String)value);
            return super.put(key, value);
        }
    };
    bp.load(s);
    final Map<String,String> resolved = new LinkedHashMap<String, String>(ordered.size());
    StrSubstitutor sub = new StrSubstitutor(new StrLookup<String>() {
        @Override
        public String lookup(String key) {
            String value = resolved.get(key);
            if (value == null)
                return System.getProperty(key);
            return value;
        }
    });
    for (String k : ordered.keySet()) {
        String value = sub.replace(ordered.get(k));
        resolved.put(k, value);
    }
    return resolved;
}

<强>输入

blah=${user.dir}
one=1
two=2
five=5
fifteen=${one}${five}
twoonefive=${two}${fifteen}
six=6

<强>输出

blah=/current/working/dir
one=1
two=2
five=5
fifteen=15
twoonefive=215
six=6

显然,如果需要,您可以将Map<String,String>转换回Properties对象。我基于先前声明的属性和系统属性来解决,但您显然可以在StrSubstitutor.lookup

中进行调整

答案 6 :(得分:1)

在这种特殊情况下(以及others中),您最好通过定义不同的属性来解决重复问题:

  1. dir.proj1=dir.default /p1更改为dir.proj1_extension=/p1
  2. dir.default添加到dir.proj1_extension以获取应用代码中proj1的完整位置。
  3. 对其他项目也这样做。

答案 7 :(得分:1)

配置文件包含key=valuekey:value格式的语句。 它们是键值可以引用另一个键值的可能方式。开头“$ {”和结束“}”之间的字符串被解释为键。替换变量的值可以定义为系统属性或配置文件本身。

由于 Properties 继承自 Hashtable ,因此putputAll方法可应用于{ {1}}。

Properties object

详细阐述了 @Adam Gent 的帖子。 commons-text-1.1.jar

Map<String, String> map = new LinkedHashMap<String, String>();
map.put("key", "vlaue");
Properties props = new Properties();
props.putAll( map );
  

配置文件«如果您想要忽略参考并且不会被替换,那么您可以使用以下格式。

import org.apache.commons.text.StrLookup;
import org.apache.commons.text.StrSubstitutor;

public class Properties_With_ReferedKeys {
    public static void main(String[] args) {

        ClassLoader classLoader = Properties_With_ReferedKeys.class.getClassLoader();

        String propertiesFilename = "keys_ReferedKeys.properties";
        Properties props = getMappedProperties(classLoader, propertiesFilename);

        System.out.println( props.getProperty("jdk") );

    }


    public static Properties getMappedProperties( ClassLoader classLoader, String configFilename ) {
        Properties fileProperties = new Properties();

        try {
            InputStream resourceAsStream = classLoader.getResourceAsStream( configFilename );

            Map<String, String> loadPropertiesMap = loadPropertiesMap( resourceAsStream );
            Set<String> keySet = loadPropertiesMap.keySet();
            System.out.println("Provided 'Key':'Value' pairs are...");
            for (String key : keySet) {
                System.out.println( key + " : " + loadPropertiesMap.get(key) );
            }

            fileProperties.putAll( loadPropertiesMap );
        } catch ( IOException e ) {
            e.printStackTrace();
        }

        return fileProperties;
    }
    public static Map<String,String> loadPropertiesMap( InputStream inputStream ) throws IOException {
        final Map<String, String> unResolvedProps = new LinkedHashMap<String, String>();

        /*Reads a property list (key and element pairs) from the input byte stream. 
         * The input stream is in a simple line-oriented format.
         */
        @SuppressWarnings("serial")
        Properties props = new Properties() {
            @Override
            public synchronized Object put(Object key, Object value) {
                unResolvedProps.put( (String)key, (String)value );
                return super.put( key, value );
            }
        };
        props.load( inputStream );

        final Map<String,String> resolvedProps = new LinkedHashMap<String, String>( unResolvedProps.size() );

        // Substitutes variables within a string by values.
        StrSubstitutor sub = new StrSubstitutor( new StrLookup<String>() {
            @Override
            public String lookup( String key ) {

                /*The value of the key is first searched in the configuration file,
                 * and if not found there, it is then searched in the system properties.*/
                String value = resolvedProps.get( key );

                if (value == null)
                    return System.getProperty( key );

                return value;
            }
        } );

        for ( String key : unResolvedProps.keySet() ) {

            /*Replaces all the occurrences of variables with their matching values from the resolver using the given 
             * source string as a template. By using the default ${} the corresponding value replaces the ${variableName} sequence.*/
            String value = sub.replace( unResolvedProps.get( key ) );
            resolvedProps.put( key, value );
        }
        return resolvedProps;
    }
}

档案: $${${name}} must be used for output ${ Yash }. EX: jdk = ${jre-1.8}

keys_ReferedKeys.properties

Java属性(key = value)格式示例log4j.properties

答案 8 :(得分:0)

下面是Java中的代码片段,用于读取引用其他属性的属性。具体来说,这些是可重复使用的查询,但也可以是其他内容。

LinkedHashMap<String, String> sqlsRaw = loadPropertiesFromFile();
LinkedHashMap<String, String> sqls = new LinkedHashMap<>();
StrSubstitutor substitutor = new StrSubstitutor(sqls);

for (Map.Entry<String, String> entry : sqlsRaw.entrySet()) {
    String sql = entry.getValue();
    try {
        sql = substitutor.replace(sql);
    } catch (Exception e) {
        throw new RuntimeException("Found an sql with a non replaced reference to another. Please validate that the required key was defined before this sql: " + entry.getValue(), e);
    }
    sqls.put(entry.getKey(), sql);
}

示例属性:

key1=value1
key21=value2 ${key1}

运行此功能后,key21的值为value2 value1

*使用apache的StrSubstitutor

答案 9 :(得分:0)

我都不喜欢给定的解决方案。 EProperty未维护,并且在Maven Central中不可用。 Commons Config太大了。 commons-lang中的StrSubstitutor已弃用。

我的解决方案仅依赖于通用文本:

public static Properties interpolateProperties(Properties rawProperties) {
    Properties newProperties = new Properties();
    interpolateProperties(rawProperties, newProperties);
    return newProperties;
}

public static void interpolateProperties(Properties rawProperties, Properties dstProperties) {
    StringSubstitutor sub = new StringSubstitutor((Map)rawProperties);
    for (Map.Entry<Object, Object> e : rawProperties.entrySet()) {
        dstProperties.put(e.getKey(), sub.replace(e.getValue()));
    }
}

即:

Properties props = new Properties();
props.put("another_name", "lqbweb");
props.put("car", "this is a car from ${name}");
props.put("name", "${another_name}");
System.out.println(interpolateProperties(props));

打印出:

  

{car =这是一辆来自鲁本的汽车,name = ruben,another_name = ruben}

答案 10 :(得分:0)

我喜欢上面解决方案的想法,但是我确实想要一些东西来代替Properties。下面的课程基于上面的想法。它仍然使用Apache Commons文本StringSubstitutor,并在Properties类,Java System定义的或System的Env中查找键。此类扩展了Properties并覆盖了getProperty(...)方法,因此是替代品。您可以使用'lookup()'方法获取原始键的值,但是它将从这3个位置之一返回一个值。如果要确定属性中的键是否存在,请使用地图的基础get()。

Apache Commons依赖项:

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-text</artifactId>
            <version>1.3</version>
        </dependency>

类源。

import java.util.Properties;
import org.apache.commons.text.StringSubstitutor;
import org.apache.commons.text.lookup.StringLookup;

/**
 * This extends Properties to provide macros substitution and includes getting properties from
 *     the Java System properties or the System's Environment.  This could be used to consolidate
 *     getting a system variable regardless if it is defined in the Java system or in the System's
 *     environment, without any other actual properties.
 *
 * The macro substitution is recursive so that given the following properties:
    <code>
      myProg=Program1
      outDir=./target
      inputs'./data/${defman }Templates
      outputs=${outDir}/${myProg}
      log=${outDir}/${myProg}/build_report.txt
      homeLog=${HOMEDRIVE}/${log}
    </code>
 * And assuming the environment variable HOMEDRIVE=C:
 * the getProperties("homeLog") would result in:  C:/./target/Program1/build_report.txt
 * 
 * Although based on the article below, this version substitutes during the getProperty() functions
 * instead of the loading functions explained in the article.
 * 
 * Based on this article: 
 *     https://stackoverflow.com/questions/872272/how-to-reference-another-property-in-java-util-properties
 *
 * @author Tim Gallagher
 * @license - You are free to use, alter etc. for any reason
 */
public class MacroProperties extends Properties implements StringLookup {

  // Substitutes variables within a string by values.
  public final StringSubstitutor macroSubstiitutor;

  public MacroProperties() {
    this.macroSubstiitutor = new StringSubstitutor(this);
  }

  public MacroProperties(Properties prprts) {
    super(prprts);
    this.macroSubstiitutor = new StringSubstitutor(this);
  }

  /**
   * The value of the key is first searched in the properties, and if not found there, it is then
   * searched in the system properties, and if still not found, then it is search in the 
   * system Env.
   *
   * @param key non-null string.
   * @return may be null.
   */
  @Override
  public String lookup(String key) {
    // get the Property first - this must look up in the parent class
    // or we'll get into an endless loop
    String value = super.getProperty(key);
    if (value == null) {      
      // if not found, get the Java system property which may have been defined on the 
      // Java command line with '-D...'
      value = System.getProperty(key);

      if (value == null) {
        // if not found, get the System's environment variable.
        value = System.getenv(key);
      }
    }

    return value;
  }

  @Override
  public String getProperty(String key, String defaultValue) {
    /*
       * Replaces all the occurrences of variables with their matching values from the resolver 
       * using the given source string as a template. By using the default ${} the corresponding
       * value replaces the ${variableName} sequence.
     */
    String value = lookup(key);
    if (value != null) {
      value = macroSubstiitutor.replace(value);
    } else {
      value = defaultValue;
    }
    return value;
  }

  @Override
  public String getProperty(String key) {
    return getProperty(key, null);
  }
}

答案 11 :(得分:0)

“纯”Java 实现:

    static final Pattern PATTERN = Pattern.compile("\\$\\{([^}]+)}");

    private static void macro(final Properties properties)
    {
        properties.replaceAll((k, v) -> PATTERN.matcher((String) v).replaceAll(mr -> properties.getProperty(mr.group(1), mr.group(0)).replace("$", "\\$")));
    }

可以合并到一个简单的 Properties 子类中,如下所示:


import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.Properties;
import java.util.regex.Pattern;

public class MacroProperties extends Properties
{
    static final Pattern PATTERN = Pattern.compile("\\$\\{([^}]+)}", 0);

    @Override
    public synchronized void load(final Reader reader) throws IOException
    {
        super.load(reader);
        macro(this);
    }

    @Override
    public synchronized void load(final InputStream inStream) throws IOException
    {
        super.load(inStream);
        macro(this);
    }

    private static void macro(final Properties properties)
    {
        properties.replaceAll((k, v) -> PATTERN.matcher((String) v).replaceAll(mr -> properties.getProperty(mr.group(1), mr.group(0)).replace("$", "\\$")));
    }
}


它是如何工作的?

PATTERN 是一个匹配简单 ${foo} 模式的正则表达式,并将大括号之间的文本作为一个组捕获。

Properties.replaceAll 应用一个函数来用函数的结果替换每个值。

Matcher.replaceAll 应用一个函数来替换 PATTERN 的每个匹配项。

我们对该函数的实现在属性中查找匹配组 1 或默认为匹配(即实际上不进行替换)。

Matcher.replaceAll 还解释寻找组引用的替换字符串,因此我们还需要使用 String.replace 来反斜杠转义 $