Bean utilities, in short, allow setting and reading bean properties. Several features make it different from other similar libraries. First, it is the fastest bean manipulation utility. Second, it may work with attributes and properties in the same time: when class doesn't define setters and getters, its attributes may be used. Third, there is a nice variety of available functions that suits most needs. Code is divided in logical sections, so it is easy to use even in specific way, if there is such requirement.
Bean property is a class field with its (optional) setter and getter (aka accessors) methods.
When accessing properties, BeanUtil first tries to use accessors methods.
If such doesn't exist, BeanUtil failbacks to using the field of the same visibility.
Therefore, existence of accessors methods is not required and depends on usage, what sometimes may be handy.
BeanUtil is used internaly inside Jodd library, so this behavior applies everywhere.
public class Foo {
private String readwrite; // with gettter and setter
private String readonly; // with getter
...
}
Usage:
Foo foo = new Foo(); BeanUtil.setProperty(foo, "readwrite", "data"); BeanUtil.getProperty(foo, "readwrite"); BeanUtil.setDeclaredProperty(foo, "readonly", "data");
Lines #2 and #3 show common and expected BeanUtil usage: setting value of read-write property through it's
accessors methods.
Setting readonly property
in above example is only possible with setDeclaredProperty() method. It first tries to use
setReadonly() method, but since such doesn't exist, field is used directly.
BeanUtil supports nested properties. Besides other bean, nested property may be also
a list, a map or an array element:
BeanUtil.getProperty(cbean, "list[0].map[foo].foo"); BeanUtil.setProperty(cbean, "arr[4].map[elem.boo].foo", "test");
When accessing nested properties, BeanUtil goes one property at time and expects that
all nested properties (except the very last one) exist i.e. to be not-null. Above example is executed as shown by this following pseudo-code:
cbean.getList().get(0).get("foo").getFoo();
cbean.getArr()[4].get("elem.boo").setFoo("test");
Setting of nested properties fails if one of the (middle) elements on the path is null.
To overcome this behavior, properties may be set in forced mode:
BeanUtil.setPropertyForced(x, "y.foo", value); BeanUtil.setPropertyForced(x, "yy[2].foo", "xxx");
If the object x in above example has uninitialized property y, BeanUtil first creates a
new instance of y's type, that will be set to property y. Then,
foo property of newly created object y will be set. In the second example, yy is an array.
If it is uninitialized, BeanUtil will create a new array of length 3. Then, it will create a new
instance of yy's type that will be stored as third element of the array. Finally, the foo property is
set.
In forced mode, BeanUtil tries to instantiate uninitialized properties needed for setting the final property.
Instantiation depends of the type: if it is a simple bean, no-args constructor will be invoked. If it is a list, new ArrayList
will be created. Similar applies for arrays and map types. Additionally, BeanUtil will check the length of existing
initialized arrays and lists and if the current size is not enough, list or array will be expanded by adding null elements up
to the new size.
When creating a new element of an list, BeanUtil will consider existing generics information in order to
create element of correct type.
Property setting may fail from various reasons, causing an unchecked exception (BeanUtilException) to be thrown. Sometimes this is not
desired behavior. For such usages, BeanUtil offers silent version of methods that will not throw an exception at all.
If setting is not successful, simple nothing will happened.
In some (rare) occasions BeanUtil has to work on arrays, lists or maps directly instead on beans. In such cases it is
needed to somehow reference this object, in order to use its elements. For such situations, BeanUtil introduce special
self-reference property name: "*this". It simply points to current reference.
For example, lets say that it is needed to reference some element from context map (or array, or list) using BeanUtil
and to set value of one of it's properties: context[foo].boo. Since context is the target bean
used as an argument of BeanUtil, it is needed be able to somehow access it in the property name.
This is possible with self-reference:
BeanUtil.setProperty(ctx, "*this[foo].boo", value);
Although it's purpose is to be used on very start of nested property name, self-reference may be used anywhere in between. So the following two property names are identical:
BeanUtil.getProperty(fb4, "data[0].bbean.abean.fooProp"); BeanUtil.getProperty(fb4, "*this.data.*this[0].*this.bbean.abean.fooProp");
BeanUtil also offers convenient way to test if some property exists:
BeanUtil.hasProperty(fb, "fooInteger")
When setting properties, BeanUtil converts type of provided value to match the destination. For this purpose it uses Jodds type converter utility.
Getting properties always returns an Object. If you need to cast it to some type, you can use ReflectUtil#castType. The following snippet (from Liferay portal) shows the usage:
public boolean getBoolean(Object bean, String param, boolean defaultValue) {
Boolean booleanValue = null;
if (bean != null) {
Object value = BeanUtil.getProperty(bean, param);
beanValue = ReflectUtil.castType(value, Boolean.class);
} catch (Exception ex) {
// log error
}
if (booleanValue == null) {
return defaultValue;
} else {
return booleanValue.booleanValue();
}
}
There is more: BeanTool class has more bean-related utilities.
There are several methods for copying one bean to other. Another set of methods performs applying: copying just non-null values to destination bean.
BeanLoader is a class that describes how a bean can be loaded from given source. For example, bean can be loaded from a Map or from ServletRequest.
Very useful in debugging, BeanTool is able to create a string of beans state by reading values of all attributes.
BeanTemplateParser is a string template with JSP-alike markers that indicates where provided context values will be injected. So, BeanTemplateParser class provides a simple, small, but very useful templating mechanism! Usage is quite simple:
// prepare template
String template = "Hello ${user.name}. Today is ${dayName}.";
...
// prepare context
Foo foo = new Foo();
foo.getUser().setName("John Doe");
foo.setDayName("Saturday");
...
// parse
BeanTemplateParser btp = new BeanTemplateParser();
String result = btp.parse(template, foo);
// result == "Hello John Doe. Today is Saturday."
All marked names are considered as property names of a provided context. BeanTemplateParser looks for all declared values, so context beans doesn't even have to have declared accessors methods. And last, it is possible to escape the $ sign with a backslash (\), very similar to JSPs.
BeanTemplateParser is configurable. User can set the escape character, or starting and ending strings (${}). There is an option if missing keys should be resolved, and the replacement value for missing keys. Good practice is to create and configure one BeanTemplateparser instance that will be used in your code.
BeanUtil seems almost 20% faster compared to Common's BeanUtils v1.8.
However, the performance is not the only reason why BeanUtil is a good choice, as seen above.
You can download the performance test source.