`
Ivan0513
  • 浏览: 211582 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

反射的应用 将form表单的数据自动封装为对象

阅读更多

我们经常做表单提交,然后把一大堆页面传过来的参数一一通过set方法赋值到对象中;

还经常遇到一个表单提交同一个类的多个对象,

甚至遇到:一个表单提交多种不同类的对象。

 

学习完反射后,很希望做一个比较通用的工具类,不再每次为上面的事情做重复劳动。

 

例如:页面有以下输入框:

<input type="text" name="name"  value="Jack"/>

<input type="text" name="name"  value="Mike"/>

<input type="text" name="name"  value="Rose"/>

 

<input type="text" name="age"  value="10"/>

<input type="text" name="age"  value="21"/>

<input type="text" name="age"  value="22"/>

 

<input type="text" name="birthday"  value="1978-07-22"/>

<input type="text" name="birthday"  value="1989-07-21 05:33"/>

<input type="text" name="birthday"  value="1999-08-21 23:33:12"/>

 

希望可以通过工具类,自动获得List<Person>,这个List有3个Person对象,

Person 1 : Jack,10,1978-07-22

Person 2 : Mike,21,1989-07-21 05:33

Person 3 : Rose,22,1999-08-21 23:33:12

 

第一步-准备工作 将字符串变为合适的数据类型

页面传过来的,都是String,而我们的bean里面的属性,是五花八门的数据类型。

我们首先需要准备一些小方法,将一个String的值转化成我们需要的数据类型的值。

 

 

public final static Map<String, String> toTypeMethods;
	static {
		//支持的转换类型:暂时为boolean,byte,char,int,long,float,double及他们的包装类 + String,java.util.Date,BigDecimal这几种类型
		toTypeMethods = new HashMap<String, String>();
		toTypeMethods.put("boolean", "toBoolean");
		toTypeMethods.put("byte", "toByte");
		toTypeMethods.put("char", "toString");
		toTypeMethods.put("character", "toString");
		toTypeMethods.put("string", "toString");
		toTypeMethods.put("int", "toInteger");
		toTypeMethods.put("integer", "toInteger");
		toTypeMethods.put("long", "toLong");
		toTypeMethods.put("float", "toFloat");
		toTypeMethods.put("double", "toDouble");
		toTypeMethods.put("date", "toDate");
		toTypeMethods.put("bigdecimal", "toBigDecimal");
	}

	/**
	 * 这个方法的作用是:告诉我对象的属性、字符串,
         * 这样我就可以将字符串转化成属性对应的数据类型的值了。
	 * 例如:在类中Person的birthday定义为Date,
         * 我就会把"2010-10-01"转为整形的Date类型的2010-10-01
	 * 如果你把birthday定义为int,并传入了"2010-10-01",我的转换方法会返回null,
         * 你也可以抛出异常,这个关键是看转换的方法实现,你可以按自己的要求更改
	 * @param field 对象的属性
	 * @param string 页面的参数
	 * @param 将string转化为属性类型的值
	 */
	public static Object toTypeValue(Field field, String string)
			throws Exception{
			String fieldTypename = field.getType().getSimpleName(); //属性的类型(不包含报名)
			//通过属性的类型,从map中找到要调用的方法的名字,这些方法是将一个String的值转化成我们需要的数据类型的值
			String methodName = toTypeMethods.get(fieldTypename.toLowerCase()); 
			//获取方法
			Method method = 
				BeanReflectUtil.class.
				getDeclaredMethod(methodName, string.getClass()); 
			//调用方法,返回属性类型的值
			return method.invoke(null, string); //如果底层方法是静态的,那么可以忽略指定的 obj 参数。该参数可以为 null。 
		}

	//下面这些小方法是为了将一个String的值转化成我们需要的数据类型的值
	@SuppressWarnings("unused")
	private static Boolean toBoolean(final String s) {
		return NumberUtil.toBoolean(s, true);
	}
	@SuppressWarnings("unused")
	private static Byte toByte(final String s) {
		return NumberUtil.toByte(s, (byte)0);
	}

	@SuppressWarnings("unused")
	private static String toString(final String s) {
		return s;
	}

	@SuppressWarnings("unused")
	private static Integer toInteger(final String s) {
		return NumberUtil.toInt(s, 0);
	}
	@SuppressWarnings("unused")
	private static Long toLong(final String s) {
		return NumberUtil.toLong(s, 0);
	}
	@SuppressWarnings("unused")
	private static Float toFloat(String s) {
		return NumberUtil.toFloat(s, (float)0);
	}
	@SuppressWarnings("unused")
	private static Double toDouble(String s) {
		return NumberUtil.toDouble(s, 0.0);
	}
	@SuppressWarnings("unused")
	private static Date toDate(String s) { //支持部分日期格式
		try {
			return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(s);
		} catch(Exception e1) {
			try {
				return new SimpleDateFormat("yyyy-MM-dd HH:mm").parse(s);
			}catch(Exception e2) {
				try {
					return new SimpleDateFormat("yyyy-MM-dd").parse(s);
				} catch (Exception e3) {
					return null;
				}
			}
		} 
	}

	@SuppressWarnings("unused")
	private static BigDecimal toBigDecimal(String s) {
		try {
			return new BigDecimal(s);
		} catch(Exception e) {
			return new BigDecimal(0);
		}
	}
 

 

    NumberUtil.toXXX是为了将String转换为其他数据类型,它们的实现很简单,以整形为例:

/***
 * 将字符串变成整形
 * @param value 源
 * @param defaultInt 变成整形时若抛出异常则返回默认值
 * @return
 */
public static int toInt(String value, int defaultInt) {
	int result = 0;
	try {
		result = Integer.parseInt(value);
	} catch(NumberFormatException e) {
		result = defaultInt;
	}
	return result;
}

 

 如何使用:

现在我在页面获得了"20",需要把"20"赋给age

//希望大家看看前一篇文章(1)发射getter And Setter 的基本内容:http://ivan0513.iteye.com/admin/blogs/728396
Person p = new Person();
//将字符串"20" 转换为Integer 20
Object age = toTypeValue(p.getClass().getDeclaredField("age"), "20");
//将整形20赋值给对象Person的age,这里我故意不使用setAge方法,因为以后我们从页面换取参数,
//我们又怎会知是哪个对象的哪个属性呢。需要通用的反射。
//invokeSetMethod在附件BeanReflectUtil.java中
BeanReflectUtil.invokeSetMethod(Person.class, "age", p, age);
//打印验证
System.out.println(p.getAge());

 那么控制台就会打印出:

20 

 

完成上面的工具方法,那么,离自动将form表单的参数封装成对象集合的路就不远了!

第二步-获取页面的参数,封装返回对象集合List

/***
 * 从页面取关于这个bean的元素数组
 * 页面与类的字段名称必须一致
 * @param c 要从页面中获取哪个类的信息
 * return Map 字段名,字段值数组
 */
public static Map<String, String[]> getFieldValues(HttpServletRequest request, Class<?> c) {
 Map<String, String[]> fieldValues = new HashMap<String, String[]>();
//这个类有哪些属性
 String[] fieldNames = BeanReflectUtil.getDeclaredFieldNames(c);
 for(int i=0; i<fieldNames.length; i++) {
//循环属性,获取页面相应参数数组
  String[] values = request.getParameterValues(fieldNames[i]);
  fieldValues.put(fieldNames[i], values);
 }
 return fieldValues;
}
/**
 * 返回潜在对象可能的个数
 * 本方法假定假设以下条件成立
 * 1.页面的参数可能不会有对象的所有属性,但至少存在1个,
 * 即Person{name, age, birthday}在页面中,至少有<input type="text" name="age" value="20" />(以文本框为例,不一定是文本框)
 * 2.在存在的前提下,不同属性的参数个数一样
 * 即<input type="text" name="age" id="age1" />  <input type="text" name="name" id="name1" />
 * <input type="text" name="age" id="age2" /> <input type="text" name="name" id="name2" />
 * 其他特殊情况恕不支持,如:
 * <input type="text" name="age" id="age1" />  <input type="text" name="name" id="name1" /> <input type="text" name="birthday" id="birthday1" />
 * <input type="text" name="age" id="age2" /> <input type="text" name="name" id="name2" /> <!--有2个age,name,却只有1个birthday-->
 * @param fieldValues
 * @return
 */
public static int getParameterLenth(HttpServletRequest request, Class<?> c) {
 
 int parameterLenth = 0;
 String[] fieldNames = BeanReflectUtil.getDeclaredFieldNames(c);
 for(int i=0; i<fieldNames.length; i++) {
  String[] values = request.getParameterValues(fieldNames[i]);
  if(values != null) {
   parameterLenth = values.length;
   break;
  }
 }
 return parameterLenth;
}
/**
 * 将页面元素自动封装成bean,而且是多个bean
 * @param request
 * @param c
 * @return
 * @throws Exception
 * @throws NoSuchFieldException
 */
public static List<?> assembleObjectList(HttpServletRequest request, Class<?> c) 
 throws Exception, NoSuchFieldException {
 Object[] objArray = null;
 //1.获取页面元素(关于这个bean的页面元素)
 Map<String, String[]> fieldValues = getFieldValues(request, c);
 List objList = null;
 if(fieldValues != null) {
  //2.获取bean的数量
  int objLength = getParameterLenth(request, c);
  objArray = new Object[objLength];
  objList = new ArrayList();
  //3.生成实例
  for(int i=0; i<objLength; i++) {
   objArray[i] = c.newInstance();
   objList.add(objArray[i]);
  }
  //为每个实例的每个元素赋值(如果页面有该参数数组)
  Iterator<String> keyIt = fieldValues.keySet().iterator();
  while(keyIt.hasNext()) {
   String fieldName = keyIt.next();
   Field field = c.getDeclaredField(fieldName); //获取参数对应的字段
   String[] fieldValue = fieldValues.get(fieldName); //获取参数数组
   if(fieldValue != null) {
    for(int i=0; i<objArray.length; i++) { //循环:为每个对象的字段,附上参数数组的值(通过set方法)
     //1.准备字段的Set方法
     Method m = BeanReflectUtil.assembleSetMethod(c, field);
     //2.页面的值(String类型)转变为该字段对应类型的值
     Object fieldObj = BeanReflectUtil.toTypeValue(field, fieldValue[i]); 
     //3.调用Set方法
     m.invoke(objArray[i], fieldObj); //为对象objArray[i]调用set方法,为上文中的field字段附上fieldObj这个值
    }
   }
  }
 }
 
//  return objArray;
 return objList;
}

 

使用:

如页面中有:<form>

<input type="text" name="age" id="age1" />  
<input type="text" name="name" id="name1" />
<input type="text" name="age" id="age2" /> 
<input type="text" name="name" id="name2" />
</form>

 

提交后,后台封装List<Person>,只需以下代码:

List<Person> list = (List<Person>) BaiscDataUtil.assembleObjectList(request, Person.class);

 

 

分享到:
评论
3 楼 kui2008521 2014-03-18  
附件呢?看不到啊?
2 楼 yzz9i 2011-09-29  
看上去很值得学习! 
1 楼 youyougulin 2011-08-08  
赞一个 不错用上了

相关推荐

Global site tag (gtag.js) - Google Analytics