从 Java 中的配置文件读取配置参数的最佳方法是什么?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26908854/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 03:30:33  来源:igfitidea点击:

What is the best way of reading configuration parameters from configuration file in Java?

javaoopconfigurationinitializationruntime

提问by Sagar Pudi

Let us assume up to runtime we do not know what are the details of configuration(may user need to configure these parameters in configfile before running the application.

让我们假设直到运行时我们都不知道配置的细节是什么(用户可能需要config在运行应用程序之前在文件中配置这些参数。

I want to read those configuration details and need to reuse them wherever I need them in my application. For that I want to make them as global constants(public static final).

我想阅读这些配置细节,并需要在我的应用程序中需要它们的任何地方重用它们。为此,我想将它们作为全局常量(public static final)。

So, My doubt is, is there any performance implications if I read from configfile directly from the required class? since,runtime values I can not directly put in separate Interface.

所以,我的疑问是,如果我config直接从所需的类中读取文件,是否有任何性能影响?因为,运行时值我不能直接放在单独的Interface.

I am thinking it will impact performance.Please suggest me any better way to do this.

我认为它会影响性能。请建议我任何更好的方法来做到这一点。

UPDATE:Can I use separate final class for configuration details? putting all configuration details as constants in a separate public final class
(To read all configuration details at once from the configuration file and storing them as global constants for later use in application)

更新:我可以使用单独的最终类来获取配置详细信息吗?将所有配置详细信息作为常量单独放置public final class
(从配置文件中一次读取所有配置详细信息并将它们存储为全局常量以供以后在应用程序中使用)

采纳答案by Stephen C

I am thinking it will impact performance.

我认为它会影响性能。

I doubt that this will be true.

我怀疑这是真的。

Assuming that the application reads the configuration file just once at startup, the time taken to read the file is probably irrelevant to your application's overall performance. Indeed, the longer the application runs, the less important startup time will be.

假设应用程序在启动时只读取一次配置文件,读取文件所花费的时间可能与应用程序的整体性能无关。事实上,应用程序运行的时间越长,启动时间就越不重要。

Standard advice is to only optimize for application performance when you have concrete evidence (i.e. measurements) to say that performance isa significant issue. Then, only optimize those parts of your code that profiling tells you are reallya performance bottleneck.

标准建议是仅在您有具体证据(即测量)表明性能一个重要问题时才针对应用程序性能进行优化。然后,仅优化分析告诉您确实是性能瓶颈的代码部分。



Can I use separate final class for configuration details

我可以使用单独的最终类来获取配置详细信息吗

Yes it is possible to do that. Nobody is going to stop you1. However, it is a bad idea. Anything that means that you need to recompile your code to change configuration parameters is a bad idea. IMO.

是的,有可能做到这一点。没有人会阻止你1。然而,这是一个坏主意。任何意味着您需要重新编译代码以更改配置参数的事情都是一个坏主意。海事组织。



To read all configuration details at once from the configuration file and storing them as global constants for later use in application.

从配置文件中一次读取所有配置详细信息并将它们存储为全局常量以供以后在应用程序中使用。

Ah ... so want to read the values of the "constants" instead of hard-wiring them.

啊......所以想要读取“常量”的值而不是硬连接它们。

Yes, that is possible. And it makes more sense than hard-wiring configuration parameters into the code. But it is still not a good idea (IMO).

是的,这是可能的。它比将配置参数硬连接到代码中更有意义。但这仍然不是一个好主意(IMO)。

Why? Well lets look at what the code has to look like:

为什么?好吧,让我们看看代码的样子:

public final class Config { 
    public static final int CONST_1;
    public static final String CONST_2;

    static {
        int c1;
        String c2;
        try (Scanner s = new Scanner(new File("config.txt"))) {
            c1 = s.nextInt();
            c2 = s.next();
        } catch (IOException ex) {
            throw RuntimeException("Cannot load config properties", ex);
        }
        CONST_1 = c1;
        CONST_2 = c2; 
    }
}

First observation is that makes no difference that the class is final. It is declaring the fieldsas finalthat makes them constant. (Declaring the class as finalprevents subclassing, but that has no impact on the staticfields. Static fields are not affected by inheritance.)

第一个观察结果是,该类没有区别final。它声明了字段,因为final这使它们保持不变。(将类声明为final可防止子类化,但这对static字段没有影响。静态字段不受继承影响。)

Next observation is that this code is fragile in a number of respects:

下一个观察是这段代码在许多方面都很脆弱:

  • If something goes wrong in the static initializer block. the unchecked exception that is thrown by the block will get wrapped as an ExceptionInInitializerError(yes ... it is an Error!!), and the Configclass will be marked as erroneous.

  • If that happens, there is no realistic hope of recovering, and it possibly even a bad idea to try and diagnose the Error.

  • The code above gets executed when the Configclass is initialized, but determining when that happens can be tricky.

  • If the configuration filename is a parameter, then you have the problem of getting hold of the parameter value ... before the static initialization is triggered.

  • 如果静态初始化程序块出现问题。块抛出的未经检查的异常将被包装为一个ExceptionInInitializerError(是的......它是一个Error!!),并且Config该类将被标记为错误。

  • 如果发生这种情况,就没有恢复的现实希望,尝试诊断Error.

  • 上面的代码在Config类初始化时执行,但确定何时发生可能会很棘手。

  • 如果配置文件名是一个参数,那么在触发静态初始化之前,您会遇到获取参数值的问题...。

Next, the code is pretty messy compared with loading the state into a instance variables. And that messiness is largely a result of having to work within the constraints of static initializers. Here's what the code looks like if you use finalinstance variables instead.

接下来,与将状态加载到实例变量中相比,代码相当混乱。这种混乱很大程度上是由于必须在静态初始化程序的约束内工作。如果您改用final实例变量,代码如下所示。

public final class Config { 
    public final int CONST_1;
    public final String CONST_2;

    public Config(File file) throws IOException {
        try (Scanner s = new Scanner(file)) {
            CONST_1 = s.nextInt();
            CONST_2 = s.next();
        } 
    }
}

Finally, the performance benefits of static finalfields over finalfields are tiny:

最后,static final字段相对于final字段的性能优势很小:

  • probably one or two machine instructions each time you access one of the constants,

  • possibly nothing at all if the JIT compiler is smart, and you handle the singleton Configreference appropriately.

  • 每次访问其中一个常量时可能会有一两条机器指令,

  • 如果 JIT 编译器很聪明,并且您Config适当地处理单例引用,则可能什么都没有。

In either case, in the vast majority of cases the benefits will be insignificant.

在任何一种情况下,在绝大多数情况下,收益都是微不足道的。



1 - OK ... if your code is code-reviewed, then someone will probably stop you.

1 - 好的……如果您的代码经过代码,那么有人可能会阻止您。

回答by Sanjeev Kumar

It will be only one time cost if you are putting them in a property file and reading the file at the start of your application and initialize all the parameters as system parameters(System.setProperty) and then define constants in your code like

如果您将它们放入属性文件并在应用程序开始时读取该文件并将所有参数初始化为系统参数(System.setProperty),然后在代码中定义常量,则只需一次

public static final String MY_CONST = System.getProperty("my.const");

But ensure the initialization at start of your application before any other class is loaded.

但请确保在加载任何其他类之前在应用程序启动时进行初始化。

回答by Denis Borovikov

Putting configuration keys directly to classes is bad: configuration keys will be scattered over the code. Best practice is separation of application code and configuration code. Usually dependency injection framework like spring is used. It loads a configuration file and constructs the objects using configuration values. If you need some configuration value in your class you should create a setter for this value. Spring will set this value during context initialization.

将配置键直接放在类中是不好的:配置键将分散在代码中。最佳实践是分离应用程序代码和配置代码。通常使用 spring 等依赖注入框架。它加载配置文件并使用配置值构造对象。如果您在类中需要一些配置值,您应该为该值创建一个 setter。Spring 将在上下文初始化期间设置此值。

回答by Yusuf Kapasi

One more way is to define a class and read the properties file in that class. This class needs to be at the Application level and can be marked as Singleton. Marking the class as Singleton will avoid multiple instances to be created.

另一种方法是定义一个类并读取该类中的属性文件。这个类需要在Application级别,可以标记为Singleton。将类标记为 Singleton 将避免创建多个实例。

回答by Sri777

Have you ever heard of apache commons configuration http://commons.apache.org/proper/commons-configuration/? It is the best configuration reader I have ever found and even am using it in my application which is running in production since 1 year. Never found any issues, very easy to understand and use, great performance. I know its a bit of dependency to your application but trust me you will like it.

你听说过 apache 公共配置http://commons.apache.org/proper/commons-configuration/吗?它是我发现的最好的配置阅读器,甚至在我的应用程序中使用它,该应用程序自 1 年以来一直在生产中运行。从未发现任何问题,非常易于理解和使用,性能出色。我知道它对您的应用程序有一点依赖性,但相信我,您会喜欢它。

All you need to do is

你需要做的就是

Configuration config = new ConfigSelector().getPropertiesConfiguration(configFilePath);
String value = config.getString("key");
int value1 = config.getInt("key1");
String[] value2 = config.getStringArray("key2");
List<Object> value3 = config.getList("key3");

And thats it. Your config object will hold all the config values and you can just pass that object to as many classes as you want. With so many available helpful methods you can extract whichever type of key you want.

就是这样。您的配置对象将保存所有配置值,您可以将该对象传递给任意数量的类。有了这么多可用的有用方法,您可以提取所需的任何类型的密钥。

回答by chromanoid

I recommend using JAXB or a similar binding framework that works with text based files. Since a JAXB implementation is part of the JRE, it's pretty easy to use. As DenisI advise against configuration keys.

我建议使用 JAXB 或类似的绑定框架来处理基于文本的文件。由于 JAXB 实现是 JRE 的一部分,因此它非常易于使用。作为丹尼斯,我建议不要使用配置键。

Here is a simple example for an easy to use and still pretty mighty way to configure you application with XML and JAXB. When you use a DI framework you can just add a similar config object to the DI context.

这是一个简单的示例,它提供了一种使用 XML 和 JAXB 配置应用程序的易于使用且仍然非常强大的方法。当您使用 DI 框架时,您只需将类似的配置对象添加到 DI 上下文即可。

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class ApplicationConfig {

    private static final JAXBContext CONTEXT;
    public static final ApplicationConfig INSTANCE;

    // configuration properties with defaults
    private int number = 0;
    private String text = "default";
    @XmlElementWrapper
    @XmlElement(name = "text")
    private List<String> texts = new ArrayList<>(Arrays.asList("default1", "default2"));

    ApplicationConfig() {
    }

    static {
        try {
            CONTEXT = JAXBContext.newInstance(ApplicationConfig.class);
        } catch (JAXBException ex) {
            throw new IllegalStateException("JAXB context for " + ApplicationConfig.class + " unavailable.", ex);
        }
        File applicationConfigFile = new File(System.getProperty("config", new File(System.getProperty("user.dir"), "config.xml").toString()));
        if (applicationConfigFile.exists()) {
            INSTANCE = loadConfig(applicationConfigFile);
        } else {
            INSTANCE = new ApplicationConfig();
        }
    }

    public int getNumber() {
        return number;
    }

    public String getText() {
        return text;
    }

    public List<String> getTexts() {
        return Collections.unmodifiableList(texts);
    }

    public static ApplicationConfig loadConfig(File file) {
        try {
            return (ApplicationConfig) CONTEXT.createUnmarshaller().unmarshal(file);
        } catch (JAXBException ex) {
            throw new IllegalArgumentException("Could not load configuration from " + file + ".", ex);
        }
    }

    // usage
    public static void main(String[] args) {
        System.out.println(ApplicationConfig.INSTANCE.getNumber());
        System.out.println(ApplicationConfig.INSTANCE.getText());
        System.out.println(ApplicationConfig.INSTANCE.getTexts());
    }
}

The configuration file looks like this:

配置文件如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<applicationConfig>
    <number>12</number>
    <text>Test</text>
    <texts>
        <text>Test 1</text>
        <text>Test 2</text>
    </texts>
</applicationConfig>

回答by matsev

What kind of configuration file do you have in mind? If it is a properties file, this might suit you:

你想到了什么样的配置文件?如果它是一个属性文件,这可能适合您:

public class Configuration {

    // the configuration file is stored in the root of the class path as a .properties file
    private static final String CONFIGURATION_FILE = "/configuration.properties";

    private static final Properties properties;

    // use static initializer to read the configuration file when the class is loaded
    static {
        properties = new Properties();
        try (InputStream inputStream = Configuration.class.getResourceAsStream(CONFIGURATION_FILE)) {
            properties.load(inputStream);
        } catch (IOException e) {
            throw new RuntimeException("Failed to read file " + CONFIGURATION_FILE, e);
        }
    }

    public static Map<String, String> getConfiguration() {
        // ugly workaround to get String as generics
        Map temp = properties;
        Map<String, String> map = new HashMap<String, String>(temp);
        // prevent the returned configuration from being modified 
        return Collections.unmodifiableMap(map);
    }


    public static String getConfigurationValue(String key) {
        return properties.getProperty(key);
    }

    // private constructor to prevent initialization
    private Configuration() {
    }

}

You could also return the Propertiesobject immediately from the getConfiguration()method, but then it could potentially be modified by the code that access it. The Collections.unmodifiableMap()does not make the configuration constant(since the Propertiesinstance gets its values by the load()method after it was created), however since it is wrapped in an unmodifiablemap, the configuration cannot be changed by other classes.

您也可以Properties立即从getConfiguration()方法中返回对象,但随后它可能会被访问​​它的代码修改。在Collections.unmodifiableMap()不使配置常数(因为Properties例如由获取其值load()方法被创建之后),但是,因为它被包裹在一个不可修改的图,该配置不能被其它类改变。

回答by Sebri Zouhaier

  protected java.util.Properties loadParams() throws IOException {
  // Loads a ResourceBundle and creates Properties from it
  Properties prop = new Properties();
  URL propertiesFileURL = this.getClass().getResource("/conf/config.properties");
  prop.load(new FileInputStream(new File(propertiesFileURL.getPath())));
  return prop;
  }


Properties prop = loadParams();
String prop1=(String) prop.get("x.y.z");

回答by Adriaan Koster

There are different types of configuration.

有不同类型的配置。

Usually some sort of bootstrapping configuration, for example to connect to a database or service, is needed to be able to start the application. The J2EE way to specify database connection parameters is via a 'datasource' specified in your container's JNDI registry (Glassfish, JBoss, Websphere, ...). This datasource is then looked up by name in your persistence.xml. In non-J2EE applications it is more common to specify these in a Spring context or even a .properties file. In any case, you usually need something to connect your application to some sort of data store.

通常需要某种引导配置,例如连接到数据库或服务,才能启动应用程序。J2EE 指定数据库连接参数的方法是通过在容器的 JNDI 注册表(Glassfish、JBoss、Websphere 等)中指定的“数据源”。然后在您的persistence.xml 中按名称查找此数据源。在非 J2EE 应用程序中,更常见的是在 Spring 上下文甚至 .properties 文件中指定这些。在任何情况下,您通常都需要一些东西来将您的应用程序连接到某种数据存储。

After bootstrapping to a data store an option is to manage config values inside this datastore. For example if you have a database you can use a separate table (represented by e.g. a JPA Entity in your application) for configuration values. If you don't want/need this flexibility you can use simple .properties file for this instead. There is good support for .properties files in Java (ResourceBundle) and in frameworks like Spring. The vanilla ResourceBundle just loads the properties once, the Spring helper offers configurable caching and reloading (this helps with the performance aspect which you mentioned). Note: you can also use Properties backed by a data store instead of a file.

引导到数据存储后,一个选项是管理此数据存储内的配置值。例如,如果您有一个数据库,您可以使用单独的表(例如在您的应用程序中由 JPA 实体表示)作为配置值。如果您不想要/不需要这种灵活性,您可以使用简单的 .properties 文件来代替。Java ( ResourceBundle) 和Spring 等框架对 .properties 文件有很好的支持。vanilla ResourceBundle 只加载一次属性,Spring 助手提供可配置的缓存和重新加载(这有助于您提到的性能方面)。注意:您还可以使用由数据存储而不是文件支持的属性。

Often both approaches coexist in an application. Values that never change within a deployed application (like the application name) can be read from a properties file. Values that might need to be changed by an application maintainer at runtime without redeployment (e.g. the session timeout interval) might better be kept in a reloadable .properties file or in a database. Values that can be changed by users of the application should be kept in the application's data store and usually have an in-application screen to edit them.

通常这两种方法在一个应用程序中共存。可以从属性文件中读取在部署的应用程序中永远不会更改的值(如应用程序名称)。应用程序维护者可能需要在运行时更改而不重新部署的值(例如会话超时间隔)最好保存在可重新加载的 .properties 文件或数据库中。应用程序用户可以更改的值应该保存在应用程序的数据存储中,并且通常有一个应用程序内屏幕来编辑它们。

So my advise is to separate your configuration settings into categories (e.g. bootstrap, deployment, runtime and application) and select an appropriate mechanism to manage them. This also depends on the scope of your application, i.e. is it a J2EE web app, a desktop app, command-line utility, a batch process?

所以我的建议是将您的配置设置分成几类(例如引导程序、部署、运行时和应用程序)并选择适当的机制来管理它们。这也取决于您的应用程序的范围,即它是 J2EE Web 应用程序、桌面应用程序、命令行实用程序还是批处理?

回答by Bhargav Modi

Well this is a great problem which is faced in every one's life once in a will. Now coming to the problem, this can be solved by creating a singleton class which has instance variables same as in configuration file with default values. Secondly this class should have a method like getInstance()which reads the properties once and every times returns the same object if it exists. For reading file we can use Environmental variable to get path or something like System.getenv("Config_path");. Reading the properties (readProperties()method) should read each item from config file and set the value to the instance variables of singleton object. So now a single object contains all the configuration parameter's value and also if the parameter is empty than default value is considered.

嗯,这是一个很大的问题,在每个人的生活中都会遇到一次。现在来解决这个问题,这可以通过创建一个单例类来解决,该类具有与配置文件中相同的实例变量和默认值。其次,这个类应该有一个方法 getInstance(),它读取属性一次,如果存在,则每次返回相同的对象。为了读取文件,我们可以使用环境变量来获取路径或类似System.getenv("Config_path");. 读取属性(readProperties()方法)应该从配置文件中读取每个项目并将值设置为单例对象的实例变量。所以现在单个对象包含所有配置参数的值,如果参数为空,则考虑默认值。