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前缀了。

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