【问题标题】:How to reference another property in java.util.Properties?如何在 java.util.Properties 中引用另一个属性?
【发布时间】:2010-10-26 17:09:55
【问题描述】:

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

这可能吗?

【问题讨论】:

标签: java properties


【解决方案1】:

java.util.Properties 类不会为您执行此操作。继承属性,重写 load() 方法并自己进行替换不会太难。

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 只有一个 jar 可以安装和使用,因为你的类路径中有 appache common logging。 here语法概述。
      【解决方案4】:

      Chris Mair 的 XProperties 课程可能是一个很好的起点。

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

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

      在此示例中,“SomeValue”属性的计算结果为“鞋子、船舶和密封蜡”。

      【讨论】:

      • @ianaz 请注意在链接中您必须使用子类 XProperties 而不是 Properties。这可能就是你什么都看不到的原因。
      【解决方案5】:

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

      1. 将:dir.proj1=dir.default /p1 更改为 dir.proj1_extension=/p1
      2. 添加:dir.defaultdir.proj1_extension 以获取应用程序代码中 proj1 的完整位置。

      对其他项目做同样的事情。

      【讨论】:

        【解决方案6】:

        Commons Config 库也可以做到这一点。 http://commons.apache.org/configuration/userguide/overview.html#Using_Configuration

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

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

        【讨论】:

          【解决方案7】:

          由于 eproperties 有点不维护,并且 commons 配置依赖于日志记录(具有讽刺意味的是,这意味着您不能使用它来配置日志记录)我使用这个代码 sn-p 只需要 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&lt;String,String&gt; 转换回 Properties 对象。我根据之前声明的属性和系统属性进行解析,但您显然可以在 StrSubstitutor.lookup 中进行调整。

          【讨论】:

            【解决方案8】:

            下面是 Java 中的 sn-p 代码,用于读取引用其他属性的属性。具体来说,这些是可重用的查询,但也可以是其他东西。

            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

            【讨论】:

            • 不要将StrSubstitutor 用于 SQL! 它不能防止 SQL 注入。我也不介意,也许你错过了,但我已经在你之前 2 年使用 StrSubstitutor 提供了答案。
            • @AdamGent - 这是通过键引用查询的机制 - 而不是值。所有查询都是准备好的语句:)
            【解决方案9】:

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

            因为 Properties 继承自 Hashtable,所以putputAll 方法可以应用于 Properties object

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

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

            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

            # MySQL Key for each developer for their local machine
            dbIP       = 127.0.0.1
            dbName     = myApplicationDB
            dbUser     = scott
            dbPassword = tiger
            
            # MySQL Properties 
            # To replace fixed-keys with corresponding build environment values. like « predev,testing,preprd.
            config.db.driverClassName : com.mysql.jdbc.Driver
            config.db.url             : jdbc:mysql://${dbIP}:3306/${dbName}
            config.db.username        : ${dbUser}
            config.db.password        : ${dbPassword}
            
            # SystemProperties
            userDir      = ${user.dir}
            os.name      = ${os.name}
            java.version = ${java.version}
            java.specification.version = ${java.specification.version}
            
            # If you want reference to be ignored and won't be replaced.
            # $${${name}} must be used for output ${ Yash }.  EX: jdk = ${jre-1.8}
            jdk = $${jre-${java.specification.version}}
            

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

            【讨论】:

              【解决方案10】:

              没有一个我真正喜欢的给定解决方案。 EProperties 没有维护,它在 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=this is a car from ruben, name=ruben, another_name=ruben}

              【讨论】:

                【解决方案11】:

                我喜欢上述解决方案的想法,但我真的想要一些东西来代替属性。下面的课程建立在上面的那些想法之上。它仍然使用 Apache Commons-text StringSubstitutor,并在 Properties 类、Java 系统定义或系统的 Env 中查找键。这个类扩展了 Properties 并覆盖了 getProperty(...) 方法,所以它是一个替代品。您可以使用“lookup()”方法获取原始键的值,但它会从这 3 个位置之一返回一个值。如果要确定属性中是否存在键,请使用 Map 的底层 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);
                  }
                }
                
                

                【讨论】:

                  【解决方案12】:

                  “纯”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("$", "\\$")));
                      }
                  

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

                  
                  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 来反斜杠转义 $

                  【讨论】:

                    猜你喜欢
                    • 2013-02-11
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2019-08-20
                    • 1970-01-01
                    相关资源
                    最近更新 更多