【问题标题】:How to safely read the property file in Java?如何安全地读取 Java 中的属性文件?
【发布时间】:2015-03-04 22:55:39
【问题描述】:

我有一个像这样的属性文件 -

emailFrom=hello@abc.com
emailTo=world@abc.com

# can be separated by comma
whichServer=UserServer,GuestServer

maxTestInSec=120
numberOfUsers=1000

现在我正在用 Java 读取这样的属性文件,如果一切设置正确,它就可以工作 -

private static final Properties prop = new Properties();

private static String emailFrom;
private static String emailTo;
private static List<String> whichServer;
private static String maxTestInSec;
private static String numberOfUsers;

public static void main(String[] args) {
    readConfig(args);
}   

private void readConfig(String[] args) throws FileNotFoundException, IOException {
    if (!TestUtils.isEmpty(args) && args.length != 0) {
        prop.load(new FileInputStream(args[0]));
    } else {
        prop.load(TestTask.class.getClassLoader().getResourceAsStream("config.properties"));
    }

    emailFrom = prop.getProperty("emailFrom").trim();
    emailTo = prop.getProperty("emailTo").trim();
    whichServer = Arrays.asList(prop.getProperty("whichServer").trim().split(","));
    maxTestInSec = prop.getProperty("maxTestInSec").trim();
    numberOfUsers = prop.getProperty("numberOfUsers").trim();
}

问题陈述:-

我需要确保如果缺少任何属性值,那么我想为此使用默认值,如果该属性被注释掉,那么我也想使用默认值,但我会记录警告消息指出该属性丢失或为空,因此使用默认值。我正在尝试涵盖读取文件的所有极端情况-

  • 现在假设,如果我没有在上述文件中为我的任何属性指定值,那么我想为我没有提供的属性使用默认值并记录为警告,指出没有值已为此属性提供,因此使用默认值。例如:假设我没有为emailFrom 字段提供任何值,那么我想使用默认值作为hello@abc.com 和其他类似的东西。所有属性的默认值为:

emailFrom=hello@abc.com

emailTo=world@abc.com

whichServer=UserServer

maxTestInSec=30

numberOfUsers=500

  • 此外,如果任何属性被注释掉,则上述代码将通过 NPE 异常。在那种情况下,我如何也可以使用默认值?

我应该开始为此使用命令行解析器吗?处理这些东西最好、最干净的方法是什么?

我不想有很多 if 块来添加检查然后设置默认值。

【问题讨论】:

    标签: java file properties-file


    【解决方案1】:

    从 Java 8 开始,最简单的做法是使用 getOrDefault(),它允许您在获取站点指定默认值。例如:

    String email = properties.getOrDefault("emailFrom", "hello@abc.com");
    

    这简洁明了,但确实意味着您需要在访问该属性的任何地方指定默认值。

    如果这对您不起作用(即您将多次从属性对象中读取值),您可以使用对默认值的内置支持 - 请注意采用 defaultconstructor Properties 对象。这使您可以构造一个包含默认值的 Properties 对象,然后当您加载用户的属性文件时,如果用户未指定值,它将回退到默认值。

    private static final Properties DEFAULTS = new Properties();
    static {
      DEFAULTS.setProperty("emailFrom", "hello@abc.com");
    }
    
    public Properties getProperties() {
      Properties props = new Properties(DEFAULTS);
      props.load(...);
      return props;
    }
    

    请注意,这与Map 的构造函数的工作方式不同——默认值保留为单独的映射,只有.getProperty() 也查询默认值; Map 中定义的方法,如 .get() 没有。 Properties 扩展 Hashtable 是一个糟糕的决定,这是众多原因之一,但是我的生活...


    这些选项有效,但它们都容易出错,因为 a) Properties 是可变的并且 b) 只有一些公共方法回退到 default 实例。我宁愿从不直接公开Properties 对象,而是使用类型安全的方法创建一个包装类,这些方法公开我的应用程序将关心的值。这需要更多的打字,但使用起来更安全。它看起来像这样:

    public class ApplicationSettings {
      private final Properties properties = new Properties();
      public ApplicationSettings() {
        properties.load(...);
      }
    
      public String emailFrom() {
        // simple methods are concise, and encode the default right inline
        return properties.getOrDefault("emailFrom", "hello@abc.com");
      }
    
      public int getMaxTestSeconds() {
        // You can do more complex validation if you want, too
        String value = properties.get("maxTestInSec");
        if (value == null) {
          return 30;
        }
        int maxTestSeconds = Integer.parseInt(value);
        if (maxTestSeconds <= 0) {
          // could instead log a warning and return the default if you want
          throw new IllegalStateException(
              "maxTestInSec must be positive - was " + maxTestSeconds);
        }
        return maxTestSeconds;
      }
    }
    

    如果需要,您还可以公开 setter,在将值添加到 Properties 对象之前类似地验证这些值,尽管默认情况下将所有内容设为只读通常是一个好习惯。

    【讨论】:

      【解决方案2】:

      您可以尝试将属性兑现到静态地图中,并在实际使用之前在该地图上进行处理。

      private Map<String, String> rawProps = new HashMap<String, String>;
      public static Map<String, String> actualProps = new HashMap<String, String>;
      
      static {
          checkMapForNullAndReport();
      }
      
      private static void checkMapForNullAndReport() {
          // Null logic and Reporting logic
          // Empty rawProps and populate the actualProps
      }
      

      我相信这样的东西对你有用。

      【讨论】:

        【解决方案3】:

        如果一个属性被注释掉,返回值为空,所以只需做一个空检查。

        if (prop.getProperty("name")==null)
        

        如果某个值未填充,则在trim操作后检查其是否等于空白。

        if (prop.getProperty("name").trim().equals(""))
        

        【讨论】:

        • 感谢您的建议。使用这种方法,如果我的配置文件中有 20 个属性,那么这意味着我需要有 40 个 if 块吗?还有其他干净的方法吗?
        • 您可以使用如下枚举获取所有属性名称。枚举 en = prop.propertyNames(); while (en.hasMoreElements()) { System.out.println(en.nextElement()); } 并确保所有属性都存在。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-01-11
        • 2020-02-18
        • 2011-09-09
        • 2012-01-21
        • 1970-01-01
        • 2012-01-07
        • 1970-01-01
        相关资源
        最近更新 更多