可能大家都有遇到这样的情况,项目中有与数据库对应实体,但是传送给客户端的对象只需要实体中的某些字段,如果是采用json格式的话可以使用jackson的@JsonIgnore忽略掉不需要的字段。但是如果采用其他方式可能就没有这么好的现成功能,需要自己定义对应的vo或者dto,然后将实体的字段逐个取值赋给vo或者dto。这其中确实有让人感觉啰嗦的地方,实体与vo或者dto中的字段名基本是一一对应的,有没有某种方式让它把能够对应上的字段自动赋值,个别字段再特殊处理呢?答案是肯定的,用java的反射机制可以实现这个想法。以下为我写的一个工具类。
package com.shannonchou.framework.util;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import com.…….domain.Article;
import com.…….dto.pb.PBAboutArticle.PBArticle;
/**
* 利用反射机制自动包装对象,比如将一个domain对象自动包装为dto、vo
* 可以根据属性或者方法名对应,找不到对应项的字段不赋值,在debug模式下打印出找不到对应项的字段
* 属性名对照规则:
* 1.去除下划线
* 2.全部转为小写
* 比如 aField 与 a_field_ 都被处理为 afield,因此对应成功。
* 方法名对照规则:
* 1.将源对象的所有getter方法和目标对象的所有setter方法分别去掉方法名前的“get”和“set”。
* 2.再去掉下划线,并全部转换为小写。
* 3.然后将处理后的方法名对照,能对应上的就赋值。
* 比如 getId() 与setId() 都被处理为id,因此对应成功。
* @date 2013-7-23 下午4:38:19
* @author shannonchou
*
*/
public class AutoWrapper {
private static Logger logger = Logger.getLogger(AutoWrapper.class);
/**
* 自动将from实例包装为to对象的实例,根据属性对照
* @param from 来源实例
* @param to 目标对象
* @return 目标对象的实例
* @date 2013-7-23 下午7:24:04
* @author shannonchou
*/
public static Object autoWrapByFields(Object from, Class<?> to) {
if (from == null) {
logger.debug("parameter form is null");
return null;
}
Object toObject = null;
try {
toObject= to.newInstance();
} catch (InstantiationException e1) {
logger.error("get instance fail:" + to.getName());
e1.printStackTrace();
return null;
} catch (IllegalAccessException e1) {
logger.error("get instance fail:" + to.getName());
e1.printStackTrace();
return null;
}
return autoWrapByFields(from, toObject);
}
/**
* 自动将from实例包装为to对象的实例,根据属性对照,
* 该方法用于目标对象没有默认构造函数的情况,直接由调用者完成目标对象的实例化
* @param from 来源实例
* @param to 目标实例
* @return
* @date 2013-7-23 下午7:29:36
* @author shannonchou
*/
public static Object autoWrapByFields(Object from, Object to) {
if (from == null) {
logger.debug("parameter form is null");
return null;
}
Map<String, Field> fromFieldMap = fieldMap(from.getClass());
Field[] toFields = to.getClass().getDeclaredFields();
for (Field field : toFields) {
Field fromField = null;
fromField = fromFieldMap.get(flat(field.getName()));
if (fromField == null) {
logger.debug("no such field:" + field.getName());
continue;
}
try {
field.setAccessible(true);
fromField.setAccessible(true);
Object fieldValue = fromField.get(from);
if (fieldValue != null) {
field.set(to, fieldValue);
}
} catch (IllegalArgumentException e) {
logger.error("set filed failed:" + field.getName());
e.printStackTrace();
} catch (IllegalAccessException e) {
logger.error("set filed failed:" + field.getName());
e.printStackTrace();
}
}
return to;
}
/**
* 将字符串转换为“平坦的”,无‘_’,全小写
* @param str
* @return
* @date 2013-7-23 下午8:01:18
* @author shannonchou
*/
public static String flat(String str) {
str = StringUtils.remove(str, '_');
return str.toLowerCase();
}
/**
* 获取对象的属性map,将field的名字平坦化作为key
* @param obj 对象
* @return
* @date 2013-7-23 下午8:25:59
* @author shannonchou
*/
public static Map<String, Field> fieldMap(Class<?> obj) {
HashMap<String, Field> map = new HashMap<String, Field>();
Field fileds[] = obj.getDeclaredFields();
for (Field field : fileds) {
map.put(flat(field.getName()), field);
}
return map;
}
/**
* 自动将from实例包装为to对象的实例,根据from的getter方法与to的setter方法对照
* @param from
* @param to
* @return
* @date 2013-7-24 下午5:02:43
* @author shannonchou
*/
public static Object autoWrapByMethod(Object from, Class<?> to) {
if (from == null) {
logger.debug("parameter form is null");
return null;
}
Object toObject = null;
try {
toObject= to.newInstance();
} catch (InstantiationException e1) {
logger.error("get instance fail:" + to.getName());
e1.printStackTrace();
return null;
} catch (IllegalAccessException e1) {
logger.error("get instance fail:" + to.getName());
e1.printStackTrace();
return null;
}
return autoWrapByMethod(from, toObject);
}
/**
* 自动将from实例包装为to对象的实例,根据from的getter方法与to的setter方法对照,
* 该方法用于目标对象没有默认构造函数的情况,直接由调用者完成目标对象的实例化
* @param from 来源实例
* @param to 目标实例
* @return
* @date 2013-7-23 下午7:29:36
* @author shannonchou
*/
public static Object autoWrapByMethod(Object from, Object to) {
if (from == null) {
logger.debug("parameter form is null");
return null;
}
Map<String, Method> toSetMap = setterMap(to.getClass());
Map<String, Method> fromGetMap = getterMap(from.getClass());
Iterator<String> iter = toSetMap.keySet().iterator();
while (iter.hasNext()) {
String toFlatName = iter.next();
Method fromMethod = null;
fromMethod = fromGetMap.get(toFlatName);
if (fromMethod == null) {
logger.debug("no such method(ignore case):get" + toFlatName);
continue;
}
Method toMethod = toSetMap.get(toFlatName);
toMethod.setAccessible(true);
fromMethod.setAccessible(true);
try {
Object fromValue = fromMethod.invoke(from);
if (fromValue != null) {
toMethod.invoke(to, fromValue);
}
} catch (IllegalArgumentException e) {
logger.debug("invoke method failed, parameter type mismatch, from:" + fromMethod.getName() + ", to:" + toMethod.getName());
} catch (IllegalAccessException e) {
logger.debug("invoke method failed, from:" + fromMethod.getName() + ", to:" + toMethod.getName());
e.printStackTrace();
} catch (InvocationTargetException e) {
logger.debug("invoke method failed, from:" + fromMethod.getName() + ", to:" + toMethod.getName());
e.printStackTrace();
}
}
return to;
}
/**
* 获取类的getter方法的map,key为方法名去除前面的get,然后平坦化,非get开头的方法不会纳入map中
* @param clazz
* @return
* @date 2013-7-24 下午4:34:46
* @author shannonchou
*/
public static Map<String, Method> getterMap(Class<?> clazz) {
Map<String, Method> methodMap = new HashMap<String, Method>();
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
String noGetName = removeGet(method.getName());
if (StringUtils.isNotBlank(noGetName)) {
methodMap.put(noGetName, method);
}
}
return methodMap;
}
/**
* 获取类的setter方法的map,key为方法名去除前面的set,然后平坦化,非set开头的方法不会纳入map中
* @param clazz
* @return
* @date 2013-7-24 下午4:34:46
* @author shannonchou
*/
public static Map<String, Method> setterMap(Class<?> clazz) {
Map<String, Method> methodMap = new HashMap<String, Method>();
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
String noSetName = removeSet(method.getName());
if (StringUtils.isNotBlank(noSetName)) {
methodMap.put(noSetName, method);
}
}
return methodMap;
}
/**
* 去除方法名前的“set”,并将其转为“平坦化”的,比如setTypeName被转为typename
* @param str
* @return 如果str为非set开头将返回null
* @date 2013-7-24 下午4:18:41
* @author shannonchou
*/
public static String removeSet(String str) {
String flatName = flat(str);
if (flatName.startsWith("set")) {
return flatName.replaceFirst("set", "");
} else {
return null;
}
}
/**
* 去除方法名前的“get”,并将其转为“平坦化”的,比如getTypeName被转为typename
* @param str
* @return 如果str为非get开头将返回null
* @date 2013-7-24 下午4:21:17
* @author shannonchou
*/
public static String removeGet(String str) {
String flatName = flat(str);
if (flatName.startsWith("get")) {
return flatName.replaceFirst("get", "");
} else {
return null;
}
}
public static void main(String[] args) {
logger.setLevel(Level.DEBUG);
Article article = new Article();
article.setId(35L);
article.setPublishTime(new Date());
PBArticle pbArticle = ((PBArticle.Builder) autoWrapByMethod(article, PBArticle.newBuilder())).build();
logger.info("id:" + pbArticle.getId());
}
}
main方法中的例子是将一个实体赋值到一个Protocol Buffers对象中。
该类有4个方法可以使用:
1.public static Object autoWrapByFields(Object from, Class<?> to)
2.public static Object autoWrapByFields(Object from, Object to)
3.public static Object autoWrapByMethod(Object from, Class<?> to)
4.public static Object autoWrapByMethod(Object from, Object to)
1、2是根据字段名赋值,即便是private的也可以。3、4是根据方法名并通过getter、setter方法取值赋值,这是因为某些对象的getter、setter方法中会有些特殊处理,比如main方法中展示的Protocol Buffers对象,在调用其setter方法时它还会改变其他标志字段,如果直接给字段赋值的话由于没有改变标志位,它会认为该字段没有被赋过值。1、3是在方法中对to类进行实例化,因此需要保证有默认构造函数,2、4是对to实例赋值,可以用在to没有默认构造函数的情况下。