【问题标题】:Managing application configuration properties in Java在 Java 中管理应用程序配置属性
【发布时间】:2013-11-23 01:00:01
【问题描述】:

我正在开发一个需要一些配置文件的 Java 应用程序,但我总是忘记属性名称,而且我发现为每个配置属性创建一个 get 方法并不方便。但是我发现它们对于它们为我提供的返回类型很有用。

我想知道如何在设计良好的 Java 应用程序中管理配置,但我想知道如果我使用这样的枚举来做这件事是否是一个好习惯。我想如果他们将本地化文件放在哪里会变得多么复杂。但这个想法仍然吸引着我。

public enum Confs {

    HLENGTH("hlength", Integer.class),
    ;

    private String propertyName;
    private Class type;


    private Confs(String propertyName, Class type) {
        this.propertyName = propertyName;
        this.type = type;
    }

    public Object getVal(){
// This hash map is loaded in a singleton class which uses java Properties feature to read the configuration file
        return MyLoadedConfigurationsSingleTon.getMap().get(this.propertyName);
    }

}

不管怎样,难道没有解决这个问题的标准方案吗?

【问题讨论】:

    标签: java configuration


    【解决方案1】:

    按键

    这真的取决于您的要求。这里有很多选择。对于简单的应用程序,我通常使用将任意配置键映射到值的东西。然后,键的名称可以指定为字符串常量,例如,某处:

    public static final String KEY_COLOR = "color";
    public static final String KEY_SIZE = "size";
    

    当然,这提出了一个where 来指定这些常量的问题。您可以在某个全局共享位置指定它们(例如,在您的配置类中,或在某些特殊的常量值类中,甚至像您建议的那样作为enum)。这样做的好处是可以让您在一个地方查看所有键名,从而轻松避免冲突。然而,这有一个明显的主要缺点,即破坏了模块化并强制所有类依赖于这个键集合——添加/删除/移动对象现在需要您修改全局键集合。

    您可以做的另一件事是将配置键名称定义为实际使用它们的类/包中的字符串常量。这不会破坏模块化。但是,您现在冒着键名与其他不相关的类发生冲突的风险。添加新密钥意味着您必须检查使用您的配置的每个对象,并确保新密钥名称尚未在使用中。

    然而,传统的 Java 解决方案是让键名还包括使用它们的类的包(可能还有类名)。例如:

    package com.me.whatever;
    public class Something {
        static final String KEY_COLOR = "com.me.whatever.Something.color";
        static final String KEY_SIZE = "com.me.whatever.Something.size";
    }
    
    package com.me.util;
    public class Other {
        static final String KEY_SIZE = "com.me.util.Other.size";
        static final String KEY_GROUP = "com.me.util.Other.group";
    }
    

    有时只指定包名更有意义,如果这更能代表您的情况。

    上述想法也适用于其他配置方案。例如。使用Preferences API,密钥的路径可以从包名中派生出来。该 API 甚至提供了诸如 systemNodeForPackage() 之类的东西来为您解决这个问题。

    所以,只要做任何能导致最清晰、最可维护的代码的事情。在简单性、模块化和灵活性之间找到平衡。对于简单的一次性应用程序,只要您所做的事情清楚,打破“OOP”概念并将它们全部放在一个地方并没有错。否则,将键值存储在它们主要使用的位置,并利用包名和类名来确保唯一性并避免键命名空间污染。

    请注意,如果键名在多个包之间拆分,enum 不一定是存储键名的最合适或最方便的数据类型。你当然可以想出一些聪明的系统来完成这项工作,但通常字符串常量既足够又易于使用。但是,如果您存储的不仅仅是键(例如值类型),enum 可能是一个合适的解决方案。


    值类型

    顺便说一下,对于值类型,您也有很多选择。您可以通过客户端类完成转换,而不是在配置端强制执行它——但是,当然,后者在许多情况下非常方便。

    使用enum 的问题在于,您实际上是在一个地方定义了所有配置键(见上文),并且在添加或删除类时很难以模块化方式扩展可用的配置键集.

    您可以创建一个通用的配置密钥类,可以根据需要进行实例化,而不是使用固定的enum,例如:

    public class Conf {
    
        public static class Key {
    
            final String key; 
            final Class<?> type;
    
            public Key (String key, Class<?> type) {
                this.key = key;
                this.type = type;
            }
    
        }
    
        public Object getValue (Key key) {
            ...
        }
    
    }
    

    通过使用泛型类型可以轻松改进上述内容。

    然后在您的客户端类中:

    package com.me.whatever;
    public class Something {
        static final Conf.Key KEY_COLOR = new Conf.Key("com.me.whatever.Something.color", Color.class);
        static final Conf.Key KEY_SIZE = new Conf.Key("com.me.whatever.Something.size", Integer.class);
    }
    

    再次谈到键名:您甚至可以使包名前缀添加成为Conf.Key 的函数,类似于Preferences.systemNodeForPackage() 的工作方式,方法是将声明类的类型作为参数并提取包名,所以上面的声明变成了,例如:

    static final Conf.Key KEY_COLOR = new Conf.Key(Something.class, "color", Color.class);
    

    结论

    我掩饰了很多,因为正如我所说,有无限的选择。我不能在这里真正涵盖每个选项的每个案例,但希望您能明白这一点。更多的是以理智的方式接近它,而不是以一种特定的“正确”方式接近它。

    【讨论】:

    • 值得一提的是,拥有像上面这样的配置键类有一些很好的潜在副作用。例如,每个类可以注册它的Conf.Key 对象列表(或者Conf.Key 可以在实例化时注册自己),然后该列表可以用于例如在运行时动态生成用户文档。默认值可以封装在关键类中,这些信息用于生成默认配置文件。也可以包括验证规则。等等等等。
    猜你喜欢
    • 1970-01-01
    • 2011-09-07
    • 2016-08-18
    • 2012-12-10
    • 2012-07-05
    • 1970-01-01
    • 2011-03-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多