Property Injection in Java With CDI
This great primer for Contexts and Dependency Injection covers the need for it and a simple implementation in a Java EE environment that focuses on injecting properties.
Join the DZone community and get the full member experience.
Join For FreeOne of the more common tasks faced in Java application development environments is the need to obtain constant values (Strings, numbers, etc.) from external properties files or the environment. Out of the box, Java provides methods such as System.getenv
and Properties.load
for retrieving such values, but their use can often lead to excess boilerplate code and logic checking for missing values in order to apply defaults, etc. Using CDI or CDI extensions, most of the boilerplate can be avoided, moving property retrieval and usage out of the way.
Consider the simple case where we have a class declaring a String field that must be set dynamically at runtime to a property value. The assumption is that this class is being managed by a CDI runtime such as a Java EE container.
In this example, we would like to set the value of `simple` to be the value of a system property, if available. Otherwise, set the value to be the property contained in a standard format properties file on the class path. Finally, set the value to be an empty String if nothing is found. By convention, both the system property and the name of the properties file match the fully qualified name of the class plus the field. Exceptions are thrown to the caller for the sake of brevity.
package com.example.injection;
public class Example {
private String simple = null;
public String getSimple() throws Exception {
if (this.simple == null) {
// Do we have a System property to use?
String systemSimple =
System.getProperty("com.example.injection.Example.simple");
if (systemSimple == null) {
/* No System property found, check in
* Example.properties on class path
*/
Properties classProperties = new Properties();
ClassLoader loader = getClass().getClassLoader();
String resName = "com/example/injection/Example.properties";
try (InputStream in = loader.getResourceAsStream(resName)) {
classProperties.load( in );
}
this.simple = classProperties.getProperty("simple", "");
} else {
this.simple = systemSimple;
}
}
return this.simple;
}
}
There is quite a bit of code here for something that, on the surface, seemed to be a simple task. The level of complexity increases if we want to parse the property into another type of object such as an Integer
or a Date
, or if we need to cache the Properties for performance reasons. No developer wants to pollute the code with methods like this (not to mention test it).
Enter CDI and CDI extensions. In a Java EE environment or other runtime that supports CDI, we can replicate the functionality above in a much simpler way.
package com.example.injection;
import javax.inject.Inject;
import io.xlate.inject.Property;
public class Example {
@Inject
@Property(defaultValue = "")
private String simple;
public String getSimple() {
return this.simple;
}
}
This example makes use of a small library Property Inject to obtain values from system properties and/or property files. Using default naming conventions, all of the logic from the earlier example is handled under the hood. When necessary, we can override the default behavior and specify the name of the system property to use, the URL containing the Properties we want to reference, and the name of the key within those properties.
Consider the example where we would like to override the property location and naming. Below, the CDI extension will first attempt to find the value in the system property called `global.simple` (e.g. command line argument -Dglobal.simple="really simple"
. If not found, the properties file named `config/my-app.properties` will be loaded from the class path and searched for entry `example.simple`. Finally, if nothing has been found, the value will default to null
since no defaultValue
has been defined.
package com.example.injection;
import javax.inject.Inject;
import io.xlate.inject.Property;
import io.xlate.inject.PropertyResource;
public class Example {
@Inject
@Property(name = "example.simple",
resource = @PropertyResource("classpath:config/my-app.properties"),
systemProperty = "global.simple")
private String simple;
public String getSimple() {
return this.simple;
}
}
In addition to Strings, Property Inject also supports the injections of all Java primitive types and their wrapper classes, BigInteger
, BigDecimal
, Date
, JsonArray
, JsonObject
, and java.util.Properties
collections themselves.
How are you using properties in your CDI-enabled applications today?
Opinions expressed by DZone contributors are their own.
Comments