最近,项目组要用到一个功能,就是用BeanUtils.copyProperties复制一个Map里的属性值到另外一个对象。
BeanUtils和PropertyUtils类是许多开源框架中频繁使用的两个工具,它们都能实现将一个类中的属性拷贝到另一个类中,这个功能甚至是spring实现依赖注入的基础。研究一下apache的comon包中如何实现这个两个工具,可以发现它们都是使用java.lang.reflect和java.beans这两个包下的几个类来实现的。
但是BeanUtils.copyProperties只支持两个对象之间的复制,其原理:是利用反射读取到第一个对象(源类)的所有属性,然后对这些属性集合进行for循环,再在for循环里面判断这些属性是否有set方法,有则再对第二个对象(目标类)进行循环取出属性一一对比,相等则调用目标类的set方法得到源类的get方法得到的值。
改后主要就是两点:第一:源类(Map类型)的Key作为属性和目标类的属性对比,相等则取出此Key的Value赋给目标类(当然还是用目标类此属性的set方法)。注意:如果是页面得到的getParameterMap()这样的Map,其值是一个数组,一般只需要取第0项就可以了。
源代码:
- /** 实现将源类属性拷贝到目标类中
- * @param source
- * @param target
- */
- public static void copyProperties(Object source, Object target) {
- try {
- // 获取目标类的属性信息
- BeanInfo targetbean = Introspector.getBeanInfo(target.getClass());
- PropertyDescriptor[] propertyDescriptors = targetbean
- .getPropertyDescriptors();
- // 对每个目标类的属性查找set方法,并进行处理
- for (int i = 0; i < propertyDescriptors.length; i++) {
- PropertyDescriptor pro = propertyDescriptors[i];
- Method wm = pro.getWriteMethod();
- if (wm != null) {// 当目标类的属性具有set方法时,查找源类中是否有相同属性的get方法
- BeanInfo sourceBean = Introspector.getBeanInfo(source.getClass());
- PropertyDescriptor[] sourcepds = sourceBean.getPropertyDescriptors();
- for (int j = 0; j < sourcepds.length; j++) {
- if (sourcepds[j].getName().equals(pro.getName())) { // 匹配
- Method rm = sourcepds[j].getReadMethod();
- // 如果方法不可访问(get方法是私有的或不可达),则抛出SecurityException
- if (!Modifier.isPublic(rm.getDeclaringClass().getModifiers())) {
- rm.setAccessible(true);
- }
- // 获取对应属性get所得到的值
- Object value = rm.invoke(source, new Object[0]);
- if (!Modifier.isPublic(wm.getDeclaringClass().getModifiers())) {
- wm.setAccessible(true);
- }
- // 调用目标类对应属性的set方法对该属性进行填充
- wm.invoke((Object) target, new Object[] { value });
- break;
- }
- }
- }
- }
- } catch (IntrospectionException e) {
- e.printStackTrace();
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- } catch (InvocationTargetException e) {
- e.printStackTrace();
- }
- }
修改后支持Map的代码:
- /**
- * 实现将源类属性拷贝到目标类中
- *
- * @param Map map
- * @param Object obj
- */
- public static void copyProperties(Map map, Object obj) throws Exception {
- // 获取目标类的属性信息
- BeanInfo targetbean = Introspector.getBeanInfo(obj.getClass());
- PropertyDescriptor[] propertyDescriptors = targetbean.getPropertyDescriptors();
- // 对每个目标类的属性查找set方法,并进行处理
- for (int i = 0; i < propertyDescriptors.length; i++) {
- PropertyDescriptor pro = propertyDescriptors[i];
- Method wm = pro.getWriteMethod();
- if (wm != null) {// 当目标类的属性具有set方法时,查找源类中是否有相同属性的get方法
- Iterator ite = map.keySet().iterator();
- while (ite.hasNext()) {
- String key = (String) ite.next();
- // 判断匹配
- if (key.equals(pro.getName())) {
- if (!Modifier.isPublic(wm.getDeclaringClass().getModifiers())) {
- wm.setAccessible(true);
- }
- Object value = ((String[]) map.get(key))[0];
- // 调用目标类对应属性的set方法对该属性进行填充
- wm.invoke((Object) obj, new Object[] { value });
- break;
- }
- }
- }
- }
- }
上次写的那个方法只适用于String类型。今天扩展了一下,写成了一个类,支持int/Integer、Date和自定义对象。
- import java.beans.BeanInfo;
- import java.beans.Introspector;
- import java.beans.PropertyDescriptor;
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
- import java.lang.reflect.Modifier;
- import java.text.ParseException;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- import java.util.Iterator;
- import java.util.Map;
- public class BeanUtils {
- public static String DATE_FORMAT = "yyyy-MM-dd";
- public static String[] TYPE_SIMPLE = {"java.lang.Integer","int","java.util.Date"};
- public static String TYPE_INTEGER = "java.lang.Integer,int";
- public static String TYPE_DATE = "java.util.Date";
- /**
- * 得到空格之后的字符
- *
- * @param String type
- * @param String str
- * @return Date
- * @throws ParseException
- */
- public static String splitSpace(String str) throws ParseException{
- if(str.contains(" ")){
- return str.split(" ")[1];
- } else {
- return str;
- }
- }
- /**
- * 判断是否是简单数据类型
- *
- * @param String type
- */
- public static boolean isSimpleType(String type) {
- for (int i = 0; i < TYPE_SIMPLE.length; i++) {
- if (type.equals(TYPE_SIMPLE[i])) {
- return true;
- }
- }
- return false;
- }
- /**
- * 把String类型转换为Integer
- *
- * @param String str
- * @return Integer
- */
- public static Integer parseInteger(String str){
- if(str == null || str.equals("")){
- return 0;
- } else {
- return Integer.parseInt(str);
- }
- }
- /**
- * 把String类型转换为Date
- *
- * @param String str
- * @return Date
- * @throws ParseException
- */
- public static Date parseDate(String str) throws ParseException{
- if(str == null || str.equals("")){
- return null;
- } else {
- SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
- Date date = sdf.parse(str);
- return date;
- }
- }
- /**
- * 转换对象(用户定义的对象)。设置对象的Id。
- *
- * @param Class clazz
- * @param String str
- * @return Object
- * @throws IllegalAccessException
- * @throws InstantiationException
- * @throws NoSuchMethodException
- * @throws SecurityException
- * @throws InvocationTargetException
- * @throws IllegalArgumentException
- * @throws ParseException
- */
- public static Object parseObject(Class clazz, String str) throws InstantiationException, IllegalAccessException, SecurityException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException {
- Object obj;
- if(str == null || str.equals("")){
- obj = null;
- } else {
- obj = clazz.newInstance();
- Method m = clazz.getMethod("setId",str.getClass());
- m.invoke(obj,str);
- }
- return obj;
- }
- /**
- * 根据类型进行转换
- *
- * @param Class clazz
- * @param String str
- * @return Object
- * @throws ParseException
- * @throws IllegalAccessException
- * @throws InstantiationException
- * @throws InvocationTargetException
- * @throws NoSuchMethodException
- * @throws IllegalArgumentException
- * @throws SecurityException
- */
- public static Object parseByType(Class clazz, String str) throws ParseException, InstantiationException, IllegalAccessException, SecurityException, IllegalArgumentException, NoSuchMethodException, InvocationTargetException{
- Object r = "";
- String clazzName = splitSpace(clazz.getName());
- if (isSimpleType(clazzName)){
- if (TYPE_INTEGER.contains(clazzName)) {
- r = parseInteger(str);
- } else if (TYPE_DATE.contains(clazzName)) {
- r = parseDate(str);
- }
- } else {
- r = parseObject(clazz, str);
- }
- return r;
- }
- /** 实现将源类(Map类型)属性拷贝到目标类中
- * @param Map map
- * @param Object obj
- */
- public static void copyProperties(Map map, Object obj) throws Exception {
- // 获取目标类的属性信息
- BeanInfo targetbean = Introspector.getBeanInfo(obj.getClass());
- PropertyDescriptor[] propertyDescriptors = targetbean.getPropertyDescriptors();
- // 对每个目标类的属性查找set方法,并进行处理
- for (int i = 0; i < propertyDescriptors.length; i++) {
- PropertyDescriptor pro = propertyDescriptors[i];
- Method wm = pro.getWriteMethod();
- if (wm != null) {
- Iterator ite = map.keySet().iterator();
- while (ite.hasNext()) {
- String key = (String) ite.next();
- // 判断匹配
- if (key.toLowerCase().equals(pro.getName().toLowerCase())) {
- if (!Modifier.isPublic(wm.getDeclaringClass().getModifiers())) {
- wm.setAccessible(true);
- }
- Object value = ((String[]) map.get(key))[0];
- String pt = splitSpace(pro.getPropertyType().getName());
- //判断类型是否匹配,不匹配则作强制转换
- if (!(pt.equals(value.getClass().getName()))) {
- value = parseByType(pro.getPropertyType(),value.toString());
- }
- // 调用目标类对应属性的set方法对该属性进行填充
- wm.invoke((Object) obj, new Object[] {value});
- break;
- }
- }
- }
- }
- }
- }