反射的應用 將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的值轉化成我們需要的資料型別的值。
Java程式碼-
public final static
- 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轉換為其他資料型別,它們的實現很簡單,以整形為例:
Java程式碼- /***
- * 將字串變成整形
- * @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
Java程式碼- //希望大家看看前一篇文章(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());
那麼控制檯就會打印出:
Java程式碼- 20
完成上面的工具方法,那麼,離自動將form表單的引數封裝成物件集合的路就不遠了!
第二步-獲取頁面的引數,封裝返回物件集合List
Java程式碼- /***
- * 從頁面取關於這個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