简体   繁体   English

Java:使用属性文件中的键名而不使用硬编码字符串作为键

[英]Java: Using key names from properties file without using hardcoded strings as keys

Problem statement 问题陈述

I have a properties file that's accessed throughout my java project. 我有一个属性文件,可以在我的java项目中访问。

Example contents of my .properties file: 我的.properties文件的示例内容:

appName=MyApp
appType=TypeA

Let's say I access the single property, appName , all throughout my java project 假设我在整个java项目中访问单个属性appName

props.getProperty("appName");

I don't want to iterate through the properties file to get the property value; 我不想遍历属性文件来获取属性值; I'm just simply getting a single property value from the properties file. 我只是从属性文件中获取单个属性值。 But I don't like the fact that I have to access the property by using a hardcoded string because it can lead to maintenance issues (ie changing all instances of the hardcoded string). 但我不喜欢我必须使用硬编码字符串访问该属性的事实,因为它可能导致维护问题(即更改硬编码字符串的所有实例)。

My current approach 我目前的做法

In my current approach, I have a utility class that creates static final variables representing the key names in the properties file, and then I use that variable to access the property value: 在我目前的方法中,我有一个实用程序类,它创建表示属性文件中的键名的静态最终变量,然后我使用该变量来访问属性值:

public static final String APP_NAME = "appName";
...
props.getProperty(APP_NAME);

But this seems like overkill because it's being redundant, and still a potential maintenance concern. 但这似乎有点过分,因为它是多余的,并且仍然是潜在的维护问题。 The key already exists in the properties file, and I'm declaring them again in my utility class. 密钥已存在于属性文件中,我在我的实用程序类中再次声明它们。

Is there a more "maintenance-free" way of accessing the key name in my code when using the get methods to access property values? 在使用get方法访问属性值时,是否有更“免维护”的方式来访问代码中的密钥名称?

No, you're doing it right. 不,你做得对。 And it's actually fairly maintenance-free. 它实际上相当免维护。

Using Java Enums would be slightly preferable - ie 使用Java Enums会略微优先 - 即

public class PropertiesWrapper {
   private final Properties props;
   ...
   public String get(MyEnum key) {  return props.get(key.toString());
}

The reason is that even if you make that String a constant, you can never change it without recompiling all code which uses the constant - because the compiler will replace the constant with "appName" at compile-time. 原因是,即使你将String变为常量,也不能在不重新编译使用常量的所有代码的情况下更改它 - 因为编译器会在编译时用“appName”替换常量。

If you use enums and remove an enum constant, code will still need recompiling, but it won't appear to be fine when it's actually now asking for the wrong thing. 如果你使用枚举并删除枚举常量,代码仍然需要重新编译,但它实际上现在要求错误的东西似乎不会很好。 Also, by using toString() instead of name() to get the property name, you are free to override toString() in your enum to return something different than the constant name. 此外,通过使用toString()而不是name()来获取属性名称,您可以在枚举中重写toString()以返回与常量名称不同的内容。

The down-side of using enums is that the system can't look up anything which was not known at compile-time without some alternate way to access the Properties . 使用枚举的缺点是系统无法在没有其他方法访问Properties情况下查找编译时未知的任何内容。

There might be a library out there that will read a properties file and generate it into a source file with getters on it. 可能有一个库将读取属性文件并将其生成到带有getter的源文件中。 You'd then have to compile that source file with your code. 然后,您必须使用您的代码编译该源文件。 That would be a pretty nifty library. 那将是一个非常漂亮的图书馆。 But, if this doesn't exist, I don't think there's any other way to do this. 但是,如果这不存在,我认为没有其他办法可以做到这一点。

Even if it exists, I don't see how it would be able to know that key1 is a String and key2 is an Integer . 即使它存在,我也看不出它如何能够知道key1是一个Stringkey2是一个Integer You'd still probably have to cast somewhere. 你可能仍然需要在某处施放。 That, or maintain a separate metadata file and then you're back to more maintenance. 那,或者维护一个单独的元数据文件,然后你又回到了更多的维护。

The problem is, you can change the property file keys at any time and the compiler has no way of knowing you did it. 问题是,您可以随时更改属性文件密钥,编译器无法知道您是否已执行此操作。

The best I can give you are libraries meant for reading configuration files. 我能给你的最好的是用于读取配置文件的库。 Check out the Apache Configuration library . 查看Apache配置库

A colleague and I are facing a very similar issue at the moment, and we did some research to see if this could be solved using reflection. 我和一位同事现在面临着一个非常相似的问题,我们做了一些研究,看看是否可以用反思解决这个问题。 Unfortunately, we did not manage to solve it, but I will try to elaborate on our problem and strategy. 不幸的是,我们没有设法解决它,但我会尝试详细说明我们的问题和策略。 Perhaps someone clever can use our baseline to build something that overcomes the limitations that set us back. 也许有些聪明的人可以使用我们的基线来构建能够克服限制条件的东西。

Problem statement: We want to read values in a properties file and assign these values to fields in a Java object (a settings object). 问题陈述:我们想要读取属性文件中的值,并将这些值分配给Java对象(设置对象)中的字段。 The key names do not matter for us and as such it would be optimal to simply use the names of the Java fields as the key names. 键名对我们来说无关紧要,因此最好只使用Java字段的名称作为键名。

This would bring two great benefits: 这将带来两大好处:

  1. First, it would allow us to remove a lot of redundant string constants. 首先,它允许我们删除大量冗余字符串常量。
  2. Second, it would allow us to define field assignment in a single place as our settings classes are implemented using inheritance. 其次,它允许我们在一个地方定义字段分配,因为我们的设置类是使用继承实现的。

We planned on defining a method in the super class that would use reflection to get all instance fields. 我们计划在超类中定义一个方法,该方法将使用反射来获取所有实例字段。 Onward from here, our strategy would be to loop through all fields and: 从这里开始,我们的策略是遍历所有领域并:

  1. get the name the field, 得到这个名字,
  2. use this name to look up a property in the properties file, 使用此名称在属性文件中查找属性,
  3. get the type of the field and use this type to convert the string value read from the properties file and subsequently assign it to the current field. 获取字段的类型并使用此类型转换从属性文件中读取的字符串值,然后将其分配给当前字段。

Using this approach, one would be able to add a new property simply by adding a new instance field. 使用这种方法,只需添加一个新的实例字段即可添加新属性。 No extra code for reading nor writing the new property would be required. 不需要额外的代码来阅读或写新属性。

Unfortunately this is not feasible due to two issues: 不幸的是,由于两个问题,这是不可行的:

  1. The Java compiler can remove information about field names, see this answer . Java编译器可以删除有关字段名称的信息,请参阅此答案
  2. Depending on the JVM, using Class#getDeclaredFields() might deny access. 根据JVM,使用Class#getDeclaredFields()可能会拒绝访问。

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

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