我们经常做表单提交,然后把一大堆页面传过来的参数一一通过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);