比如有如下相互关联的类:
public class User {
private String gender;
private String name;
private int age;
private Box box;
......
}
public class Box {
private Eraser eraser;
private Pen pen;
......
}
public class Eraser {
private int coler;
private int weight;
......
}
public class Pen {
private String name;
private int length;
......
}
属性文件中的配置:
gender=male
name=XX
age=21
box.eraser.coler=256
box.eraser.weight=13
box.pen.name=nike
box.pen.length=10
按照一般的处理方式,是先解析配置文件,接着一个一个的创建对象,再给它们赋值,然后建立各个对象之间的关联关系。这样做代码量会很大,而且不方便维护。
有没有方便的方式直接构建对象树后就自动赋值了?有的,可以使用ognl
maven中引入ongl的jar:
<dependency>
<groupId>ognl</groupId>
<artifactId>ognl</artifactId>
<version>3.1.15</version>
</dependency>
先实现ObjectNullHandler,用于告诉OGNL,当属性为null时如何创建这个属性对应的对象
public class DefaultObjectNullHandler extends ObjectNullHandler {
public Object nullPropertyValue(Map context, Object target, Object property) {
if (property instanceof String) {
try {
String propertyName = (String) property;
Class<?> propertyType = PropertyUtils.getPropertyType(target, propertyName);
Object propertyInstance = propertyType.newInstance();
PropertyUtils.setProperty(target, propertyName, propertyInstance);
return propertyInstance;
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
}
写一个例子测试一下:
public static void doTest() {
try {
DefaultObjectNullHandler defaultObjectNullHandler = new DefaultObjectNullHandler();
// 注册一个ObjectNullHandler
OgnlRuntime.setNullHandler(Object.class, defaultObjectNullHandler);
User user = new User();
Ognl.setValue("gender", user, "male");
Ognl.setValue("name", user, "XX");
Ognl.setValue("age", user, 21);
Ognl.setValue("box.eraser.coler", user, 256);
Ognl.setValue("box.eraser.weight", user, 13);
Ognl.setValue("box.pen.name", user, "nike");
Ognl.setValue("box.pen.length", user, 10);
} catch (OgnlException e) {
e.printStackTrace();
}
}
只需要通过以上几行代码,就可以自动把对象树构建出来,并且赋上属性值。代码简单明了,用起来也非常方便。
如果对象树中出现了数组怎么办:
public class Box {
private Eraser eraser;
private Pen[] pens;
}
配置可以改成:
box.pens[0].name=nike
box.pens[0].length=10
ObjectNullHandler的实现中加入创建数组的逻辑:
public class DefaultObjectNullHandler extends ObjectNullHandler {
public Object nullPropertyValue(Map context, Object target, Object property) {
if (property instanceof String) {
try {
String propertyName = (String) property;
Class<?> propertyType = PropertyUtils.getPropertyType(target, propertyName);
Object propertyInstance = null;
if (propertyType.isArray()) {
// 这里要注意,创建数组时要指定数组大小。配置中的数组索引编号不能大于等于这个值,否则会报NullPointerException
propertyInstance = Array.newInstance(propertyType.getComponentType(), 10);
Object componentInstance = propertyType.getComponentType().newInstance();
Array.set(propertyInstance, 0, componentInstance);
} else {
propertyInstance = propertyType.newInstance();
}
PropertyUtils.setProperty(target, propertyName, propertyInstance);
return propertyInstance;
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
}
测试例子如下:
public static void doTest() {
try {
DefaultObjectNullHandler defaultObjectNullHandler = new DefaultObjectNullHandler();
// 注册一个ObjectNullHandler
OgnlRuntime.setNullHandler(Object.class, defaultObjectNullHandler);
User user = new User();
Ognl.setValue("box.pen[0].name", user, "nike");
Ognl.setValue("box.pen[0].length", user, 10);
} catch (OgnlException e) {
e.printStackTrace();
}
}
如果出现了list怎么办?这里有两种情况,List中是基础数据类型,List中是自定义类
public class Box {
private List<String> aliasList;
private List<Pen> penList;
}
配置文件:
box.aliasList[0]="xx"
box.aliasList[1]="12"
box.aliasList[2]="10000"
box.penList[0].name="xx"
box.penList[1].name="12"
box.penList[2].name="10000"
ObjectNullHandler的实现中加入创建数组的逻辑:
public class DefaultObjectNullHandler extends ObjectNullHandler {
private Class defaultListClass = ArrayList.class;
public Object nullPropertyValue(Map context, Object target, Object property) {
if (property instanceof String) {
try {
String propertyName = (String) property;
Class<?> propertyType = PropertyUtils.getPropertyType(target, propertyName);
Object propertyInstance = null;
if (List.class.isAssignableFrom(propertyType)) {
propertyInstance = defaultListClass.newInstance();
List list = (List)propertyInstance;
if ("aliasList".equal(propertyName)){
list.add(null);
list.add(null);
list.add(null);
} else if ("penList".equal(propertyName)) {
list.add(new Pen());
list.add(new Pen());
list.add(new Pen());
}
} else if (propertyType.isArray()) {
// 这里要注意,创建数组时要指定数组大小。配置中的数组索引编号不能大于等于这个值,否则会报NullPointerException
propertyInstance = Array.newInstance(propertyType.getComponentType(), 10);
Object componentInstance = propertyType.getComponentType().newInstance();
Array.set(propertyInstance, 0, componentInstance);
} else {
propertyInstance = propertyType.newInstance();
}
PropertyUtils.setProperty(target, propertyName, propertyInstance);
return propertyInstance;
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
}
测试代码如下:
public static void doTest() {
try {
DefaultObjectNullHandler defaultObjectNullHandler = new DefaultObjectNullHandler();
OgnlRuntime.setNullHandler(Object.class, defaultObjectNullHandler);
User user = new User();
Ognl.setValue("box.aliasList[0]", user, "xx");
Ognl.setValue("box.aliasList[1]", user, "12");
Ognl.setValue("box.aliasList[2]", user, "10000");
Ognl.setValue("box.penList[0].name", user, "xx");
Ognl.setValue("box.penList[1].name", user, "12");
Ognl.setValue("box.penList[2].name", user, "10000");
} catch (OgnlException e) {
e.printStackTrace();
}
}
如果不想在创建List后添加多个null对象,可以使用org.apache.commons.collections.list.GrowthList,Ognl在调用GrowthList.set(...)方法时可直接扩容,而不会像调用其它List.set(...)方法那样会报错。
以上已经把OGNL构建对象树的一些典型情况列举出来了。除了使用ONGL外,也可以使用spring-boot中的@ConfigurationProperties来完成相同的工作,在实际项目中最终选择使用哪种方式,就要看个人偏好了。
除了以上方法,还有一种间接的方法可以将属性映射到对象,就是jackson-dataformat-properties将先properties转化为json,再将json转化为对象。而将json转化为对象可以找到的工具就很多了,这里就不再多讲了。
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-properties</artifactId>
</dependency>