简体   繁体   English

在Java中管理应用程序配置属性

[英]Managing application configuration properties in Java

I'm developing a Java application that needs some configuration files, but I always forget the property names and I find it not comfortable creating a get method for every configuration property. 我正在开发需要一些配置文件的Java应用程序,但是我总是忘记了属性名称,并且发现为每个配置属性创建get方法都不方便。 However I find them useful for the return type they provide for me. 但是,我发现它们对于它们为我提供的返回类型很有用。

I wonder how configurations are managed in well-design Java-apps but I'd like to know if it is a good practice if I do it using enums like this. 我不知道如何在设计良好的Java应用程序中管理配置,但是我想知道如果我使用这样的枚举来进行配置是否是一个好习惯。 I imagine how complicated it would become if they where localization files. 我想象如果将本地化文件放在哪里会变得多么复杂。 But this idea is still seducing me. 但是这个想法仍然吸引着我。

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);
    }

}

Anyway, isn't there a standard solution to solve this problem? 无论如何,是否没有解决此问题的标准解决方案?

Keys 按键

It really depends on your requirements. 这确实取决于您的要求。 There are tons of options here. 这里有很多选择。 For simple applications I generally use something that maps arbitrary configuration keys to values. 对于简单的应用程序,我通常使用将任意配置键映射到值的方法。 Then, the names of the keys can be specified as string constants, eg, somewhere: 然后,可以将键的名称指定为字符串常量,例如在某个地方:

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

This, of course, presents a problem of where to specify these constants. 当然,这带来了在哪里指定这些常数的问题。 You could specify them in some globally shared location (eg in your configuration class, or in some special constant values class, or even as an enum like you proposed). 您可以在一些全局共享的位置(例如,在配置类中,或在某些特殊的常量值类中,甚至像您建议的enum那样)指定它们。 This has the advantage of letting you see all key names in one place, thus easily avoiding conflicts. 这样的好处是可以让您在一个位置看到所有键名,从而轻松避免冲突。 This has the distinct major disadvantage, however, of breaking modularity and forcing all classes to be dependent on this collection of keys -- adding/removing/moving objects now requires you to modify your global key collection. 但是,这具有明显的主要缺点,即破坏模块化并强制所有类都依赖于此键集合-现在添加/删除/移动对象需要您修改全局键集合。

Another thing you could do is define configuration key names as string constants in the classes/packages that actually use them. 您可以做的另一件事是将配置键名称定义为实际使用它们的类/包中的字符串常量。 This does not break modularity. 这不会破坏模块化。 However, you now run the risk of conflicting key names with other unrelated classes. 但是,您现在冒着键名与其他不相关类冲突的风险。 Adding new keys means you have to go through every object that uses your configuration and make sure the new key name is not already in use. 添加新密钥意味着您必须遍历使用配置的每个对象,并确保新密钥名称尚未使用。

However, the conventional Java solution to that is to have the key names also include the package (and possibly class name) of the class that uses them. 但是,传统的Java解决方案是使键名也包括使用它们的类的包(可能还有类名)。 Eg: 例如:

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";
}

Sometimes it makes more sense to specify the package name only, if that is more representative of your situation. 有时,仅指定软件包名称更有意义,如果这样可以更好地代表您的情况。

The above ideas also hold true for other configuration schemes. 上述想法对于其他配置方案也适用。 Eg with the Preferences API, the path to the keys can be derived from the package names. 例如,使用Preferences API,可以从包名称中得出键的路径。 That API even provides things like systemNodeForPackage() that take care of this for you. 该API甚至提供了诸如systemNodeForPackage()类的东西来为您处理。

So, just do whatever leads to the clearest, most maintainable code. 因此,请尽一切可能导致最清晰,最可维护的代码。 Find a balance between simplicity, modularity, and flexibility. 在简单性,模块化和灵活性之间找到平衡。 For simple throw-away applications there's nothing wrong with breaking "OOP" concepts and sticking them all in one place, as long as what you are doing is clear. 对于简单的一次性应用程序,只要您清楚自己在做什么,就可以打破“ OOP”概念并将它们全部放在一个位置。 Otherwise, store the key values where they are primarily used, and take advantage of package and class names to ensure uniqueness and avoid key namespace pollution. 否则,请将键值存储在主要使用它们的位置,并利用包和类名来确保唯一性并避免键名空间的污染。

Note that an enum may not necessarily be the most appropriate or convenient data type to store key names in, if key names are split between multiple packages. 请注意,如果密钥名称在多个包之间分割,则enum不一定是存储密钥名称的最合适或最方便的数据类型。 You could certainly come up with some clever system to make that work, but more often than not string constants are both adequate and easy to work with. 您当然可以想出一些聪明的系统来完成这项工作,但是通常,字符串常量既足够而且容易使用。 However, an enum could be an appropriate solution if you are storing more than just the key (eg the value type). 但是,如果您存储的不仅仅是键(例如,值类型),则enum可能是合适的解决方案。


Value Types 值类型

As for value types by the way, you have many options there too. 顺便说一下,对于值类型,那里也有很多选择。 You could have conversion be done by the client class instead of enforcing it in the configuration side -- but, of course, the latter is very convenient in many cases. 您可以由客户端类完成转换,而不是在配置端强制执行转换-但是,当然,在许多情况下,后者非常方便。

The problem with using an enum is that you are essentially defining all your configuration keys in one place (see above) and it is difficult to extend the available sets of configuration keys in a modular way when classes are added or removed. 使用enum的问题在于,您实际上是在一个位置定义所有配置键(请参见上文),并且在添加或删除类时很难以模块化的方式扩展可用的配置键集。

You could create a general purpose configuration key class, that can be instantiated as needed, instead of using fixed enum , eg: 您可以创建一个通用配置密钥类,可以根据需要进行实例化,而不是使用固定的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) {
        ...
    }

}

The above can be easily improved through use of generic types. 通过使用泛型类型,可以轻松地改进上述内容。

Then in your client classes: 然后在您的客户类中:

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);
}

Speaking of key names again: You could even make the package name prefix addition be a function of Conf.Key , similar to how Preferences.systemNodeForPackage() works, by making it take the declaring class's type as a parameter and extracting the package name, so the above declarations become, eg: 再说一次键名:您甚至可以使包名前缀添加成为Conf.Key的函数,类似于Preferences.systemNodeForPackage()工作方式,方法是使声明类的类型作为参数并提取包名,因此,以上声明变为:

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

Conclusion 结论

I'm glossing over a lot because, like I said, there are infinite options. 我蒙蔽了很多,因为,就像我说的,有无限的选择。 I can't really cover every case of every option here but hopefully you get the idea. 在这里,我无法真正涵盖每种选择的所有情况,但希望您能理解。 It's more about approaching it in a sane way, rather than approaching it in one specific "correct" way. 与其说是一种理性的方法,还不如说是一种特定的“正确”方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM