Is there a good configuration library for Java which will allow me to read settings in a type-safe way? For example by taking my crafted IConfiguration inter开发者_如何学运维face with getters and setters declared and allowing me to read/write configuration via it.
It's a bit boring to read settings using properties.getProperty("group.setting")
and then cast it to needed type. Apache commons configuration allows to use something like config.getDouble("number")
, but "number" here is a string again and I would like to be able to do something like double value = config.GetNumber()
.
OWNER library does what you want.
I think typesafe's config library is what you might need. Here is the link
It is a type-safe library. Here is an example on how to use this library:
import com.typesafe.config.ConfigFactory
Config conf = ConfigFactory.load();
int bar1 = conf.getInt("foo.bar");
Config foo = conf.getConfig("foo");
int bar2 = foo.getInt("bar");
The main reason why I recommend this library is that it can read HOCON files. It stands for "Human-Optimized Config Object Notation" and is a superset of JSON:
{
"foo" : {
"bar" : 10,
"baz" : 12
}
}
It has many features to make it more readable. Like omitting special characters (,
,:
,{}
). And the coolest things is inheritance:
standard-timeout = 10ms
foo.timeout = ${standard-timeout}
bar.timeout = ${standard-timeout}
And if you duplicate a field with an object value, then the objects are merged with last-one-wins. So:
foo = { a : 42, c : 5 }
foo = { b : 43, c : 6 }
means the same as:
foo = { a : 42, b : 43, c : 6 }
Please look at the project's readme file to find more about this great configuration library https://github.com/typesafehub/config
You can define an XML configuration file with XSD and then generate JAXB source code with XJC. Every XSD type is mapped to a Java class, which you can easily marshal/unmarshal
An example. XSD:
<xsd:simpleType name="GroupType">
<xsd:restriction base="xsd:int">
<xsd:minInclusive value="1"/>
<xsd:maxInclusive value="255"/>
</xsd:restriction>
</xsd:simpleType>
Resulting Java code:
public class ElementType {
@XmlAttribute(name = "Group", required = true)
protected int group;
public int getGroup() {
return group;
}
public void setGroup(int value) {
this.group = value;
}
}
Taken from the tutorial:
http://jaxb.java.net/tutorial/
If you have a configuration interface such as:
public interface IConfig {
int getNumber();
void setNumber(int number);
String getSomeProperty();
void setSomeProperty(String someProperty);
}
then you could use a Proxy to map the methods to properties:
public class ConfigWrapper implements InvocationHandler {
@SuppressWarnings(value="unchecked")
public static <T> T wrap(Class c, Properties p) {
return (T)Proxy.newProxyInstance(c.getClassLoader(), new Class[] {c},
new ConfigWrapper(p));
}
private final Properties properties;
private ConfigWrapper(Properties p) {
this.properties = p;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
if(method.getName().startsWith("get")) {
if(method.getReturnType().equals(int.class)) {
return Integer.parseInt(
this.properties.getProperty(method.getName().substring(3)));
} else if(method.getReturnType().equals(String.class)) {
return this.properties.getProperty(method.getName().substring(3));
} else {
// obviously in a real application you'd want to handle more than just
// String and int
throw new RuntimeException(method.getName() + " returns unsupported type: "
+ method.getReturnType());
}
} else if(method.getName().startsWith("set")) {
this.properties.setProperty(method.getName().substring(3),
args[0].toString());
return null;
} else {
throw new RuntimeException("Unknown method: " + method.getName());
}
}
}
To use:
Properties p = new Properties();
IConfig config = wrap(IConfig.class, p);
config.setNumber(50);
config.setSomeProperty("some value");
System.out.println(config.getNumber());
System.out.println(config.getSomeProperty());
System.out.println(p);
Calling one of the interface methods results in a call to ConfigWrapper.invoke()
, which updates or retrieves from the Properties object (depending on whether you called a getter or setter).
Note that this implementation is distinctly unsafe since:
- values already in the Properties object aren't validated until the getter is called
- setter and getter don't have to accept/return the same type
I'm not aware of an existing library to do this, maybe because Properties objects are fairly limited in terms of the configuration they can describe.
My Config4J library (which is mature and well-documented but not widely known) provides most of what you want.
In particular, Config4J provides type-safe "lookup" (that is, "get") operations for built-in types such as string, boolean, int, float, and durations (such as "500 milliseconds"
or "2.5 days"
). A duration string is automatically converted to an integer value denoting (your choice of) milliseconds or seconds.
The library also provides building blocks so you can perform type-safe lookups on strings of the form "<float> <units>"
(for example, "2 cm"
and "10.5 meters"
for distances) or "<units> <float>"
(for example, "$0.99"
or "£10.00"
for money).
The library also provides a trivial-to-use schema validator (in case that is of interest to you).
The way Config4J fails to meet your stated requirements is that the "insert" (that is, "put") functionality of the library works only in terms of strings. So you would have to convert an int/boolean/float/int-denoting-duration/whatever value into a string and then "insert" that into the Config4J object. However, that -to-string conversion is not usually a burdensome task.
After inserting some name=value pairs into the Config4J object, you can then call dump()
to serialise the entire object into a string that you can then write back to a configuration file.
Reading Chapters 2 and 3 of the "Getting Started Guide" (PDF, HTML) should give you an a good-enough overview of Config4J to decide if it fits your needs. You might also want to look at the "simple-encapsulation" demo, which is supplied in the source-code download (compressed tar, zip).
I don't think so, because java is not a dynamic language. The only way this is achievable is if you had a code generator that would get your properties file and generate the configuration class. Or if you manually add a getter for each property.
commons-configuration is the best way to go, I think.
You should be able to get some traction by using XStream to serialize the configuration class. But the downsides are:
- The config file won't be in one of the standard properties file formats. It will be arbitrary XML instead.
- The serialization format will be fragile. Minor changes to the configuration class could render old config files unreadable.
On the upside, a format based on arbitrary XML can represent configuration items that have structure much better than the standard properties file formats can.
精彩评论