Java反射的簡單應用

目前項目中涉及到前端編輯後,向後臺做請求更新、寫入到數據庫的場景。
前端是一個表格控件,可以在單元格中編輯各種類型的數據。

舊做法

  • 前端JS構造或者提取JSON對象(從表格控件中根據id獲取行數據);
  • 對JSON對象進行裝飾,剔除前端自身增加的一些字段,去除可能影響對象轉換的一些空字段;
  • ajax請求到後臺Spring MVC框架;
  • Spring MVC做對象轉換,從JSON對象轉換爲對應的類對象;
  • JPA執行對象保存,並返回保存結果;
  • 前端執行刷新;

弊端

這樣的處理有如下的問題:
- 前後端交互的信息量較大(本來僅修改了一個字段,但是更新的時候需要上傳整個對象的所有信息);
- 後端對於更新信息不夠明確(接收到的是整個的對象,後臺搞不清楚前臺究竟做了什麼事情需要更新);
- 前端代碼很囉嗦,需要做構造對象的事情,而且需要剔除掉那些可能在Spring MVC的轉換中搗亂的字段(前臺顯示必要,增加了一些字段);

改造構思

基於前面的分析,考慮的做法就是:前端編輯了哪個屬性數據,就向後臺請求更新哪個屬性數據;不做額外的事情。
這樣,前端向後臺發送的數據就是:id,field,value;也就是數據ID、編輯的屬性、屬性的新值。
存在的一個問題就是:前端編輯的數據包括字符串、整型、浮點數、布爾型等各種類型;而JS作爲一個弱類型語言,不能強求去做數據類型解析的事情。
綜上考慮:
後臺利用Java反射機制來獲取屬性類型,然後獲取相應的set方法進行更新存儲。

實現

  1. Controller層:
    增加相應的接口方法updateXXXInfo,接收前端請求,參數爲id,field,value(三者都是String類型,id爲uuid);
  2. Service層:
    根據id獲取到對應的Java類對象(JPA的findOne方式);
    做相關的有效性校驗;
    使用反射進行更新,代碼如下;
public static boolean update(Class clazz, Object object, String field, String value) {
        String formatFieldName = field.substring(0, 1).toUpperCase() + field.substring(1);
        String getMethodName = "get" + formatFieldName;
        String setMethodName = "set" + formatFieldName;

        try {
            Method getMethod = clazz.getMethod(getMethodName);
            Class type = getMethod.getReturnType();
            Method setMethod = clazz.getMethod(setMethodName, type);

            String typeName = type.getSimpleName();
            String trimedValue = value.trim();
            if (typeName.equalsIgnoreCase("String")) {
                setMethod.invoke(object, trimedValue);
            } else if (typeName.equalsIgnoreCase("Boolean")) {
                setMethod.invoke(object, Boolean.parseBoolean(trimedValue));
            } else if (typeName.equalsIgnoreCase("Double")) {
                setMethod.invoke(object, Double.parseDouble(trimedValue));
            } else if ((typeName.equalsIgnoreCase("Integer")) || typeName.equalsIgnoreCase("int")) {
                setMethod.invoke(object, Integer.parseInt(trimedValue));
            } else if (typeName.equalsIgnoreCase("Byte")) {
                setMethod.invoke(object, Byte.parseByte(trimedValue));
            } else if (typeName.equalsIgnoreCase("Short")) {
                setMethod.invoke(object, Short.parseShort(trimedValue));
            } else if (typeName.equalsIgnoreCase("Long")) {
                setMethod.invoke(object, Long.parseLong(trimedValue));
            } else if (typeName.equalsIgnoreCase("Float")) {
                setMethod.invoke(object, Float.parseFloat(trimedValue));
            } else if (typeName.equalsIgnoreCase("Date")) {
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                setMethod.invoke(object, sdf.parse(trimedValue));
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

遺留的一點問題

上面的反射方法中,屬性的類型信息是通過get方法的returnType來獲取的(這樣做的原因在於,如果直接獲取Field,那麼存在父類中的Field獲取不到的問題)。
而get方法的獲取,需要注意boolean類型屬性的處理(IDE默認生成的get方法是以is做前綴,而不是get做前綴的)。
合理的處理方式是在反射方法中catch相關異常進行處理,因爲項目中boolean類型目前就一兩個字段,簡單處理爲get前綴了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章