关于form表单提交数据与项目中javabean绑定的功能实现

我修改了框架中getBean的实现

写一个ApiController继承Controller并且重写getBean方法

public class ApiController extends Controller {

    protected void get() {
        renderJson(Constant.method404);
    }

    protected void post() {
        renderJson(Constant.method404);
    }

    protected void delete() {
        renderJson(Constant.method404);
    }

    protected void put() {
        renderJson(Constant.method404);
    }

    @Before(Tx.class)
    public void index() throws Exception {
        String method = getRequest().getMethod();
        if (Constant.POST.equalsIgnoreCase(method)) {
            post();
        } else if (Constant.GET.equalsIgnoreCase(method)) {
            get();
        } else if (Constant.DELETE.equalsIgnoreCase(method)) {
            delete();
        } else if (Constant.PUT.equalsIgnoreCase(method)) {
            put();
        } else {
            renderJson(Constant.method404);
        }
    }

    /**
     * 重写getBean方法
     * @param beanClass
     * @param beanName
     * @param <T>
     * @return
     */
    @Override
    public <T> T getBean(Class<T> beanClass, String beanName) {
        return (T) InjectorCore.injectBean(beanClass, beanName, this.getRequest(), false);
    }
}

新建一个InjectorCore并实现一个injectBean方法

public class InjectorCore {

    private static <T> T createInstance(Class<T> objClass) {
        try {
            return objClass.newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @SuppressWarnings("unchecked")
    public static final <T> T injectBean(Class<T> beanClass, String beanName, HttpServletRequest request, boolean skipConvertError) {
        Object bean = createInstance(beanClass);

        TypeConverterCore converter = TypeConverterCore.me();
        Map<String, String[]> parasMap = request.getParameterMap();

        injectBean(bean, beanClass, parasMap, beanName, converter, request, skipConvertError);

        return (T) bean;
    }

    private static boolean injectBean(Object bean, Class<?> beanClass, Map<String, String[]> parasMap,
                                      String beanName, TypeConverterCore converter,
                                      HttpServletRequest request, boolean skipConvertError) {
        String modelNameAndDot = StrKit.notBlank(beanName) ? beanName + "[%s]" : null;//产生用于获取参数值的前缀
        Method[] methods = beanClass.getMethods();//获取要塞入参数的class内的方法
        boolean isEmpty = true;//true表示这个bean里面没有内容,false表示有内容
        for (Method method : methods) {
            String methodName = method.getName();
            if (methodName.startsWith("set") == false || methodName.length() <= 3) {    // only setter method
                continue;
            }
            Class<?>[] types = method.getParameterTypes();//获取方法返回值类型
            if (types.length != 1) {
                continue;
            }
            //获取方法名,去掉set
            String attrName = StrKit.firstCharToLowerCase(methodName.substring(3));
            String paraName = modelNameAndDot != null ? String.format(modelNameAndDot, attrName) : attrName;

            Class<?> type = types[0];
            if (!converter.isExistence(type) && type != String.class) {//如果返回类型不是普通类型,则进行单独处理
                Object newbean = null;

                if (type == List.class) {
                    newbean = injectListBean(beanClass, parasMap, attrName, paraName, converter, request, skipConvertError);
                } else {
                    newbean = createInstance(type);
                    injectBean(newbean, type, parasMap, paraName, converter, request, skipConvertError);
                }


                try {
                    method.invoke(bean, newbean);
                } catch (Exception e) {
                    if (skipConvertError == false) {
                        throw new RuntimeException(e);
                    }
                }
                continue;
            }
            //如果不是特殊类型和自定义类型则从request中获取参数放入bean中
            if (parasMap.containsKey(paraName)) {
                try {
                    String paraValue = request.getParameter(paraName);
                    if (!StringUtils.isNotNull(paraValue)) {
                        continue;
                    }
                    Object value = paraValue != null ? converter.convert(type, paraValue) : null;
                    method.invoke(bean, value);
                    isEmpty = false;
                } catch (Exception e) {
                    if (skipConvertError == false) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
        return isEmpty;
    }


    private static List<Object> injectListBean(Class<?> type, Map<String, String[]> parasMap,
                                               String attrName, String paraName, TypeConverterCore converter,
                                               HttpServletRequest request, boolean skipConvertError) {
        Field[] fields = type.getDeclaredFields();
        List<Object> list = new ArrayList<>();
        for (Field f : fields) {
            Class fieldClazz = f.getType();
            if (fieldClazz.isAssignableFrom(List.class) && f.getName().equals(attrName)) {//如果是list类型并且与要查询的参数名相同
                Type fc = f.getGenericType();
                ParameterizedType pt = (ParameterizedType) fc;
                Class<?> genericClazz = (Class) pt.getActualTypeArguments()[0];//获取到list中的泛型
                if (converter.isExistence(genericClazz) || genericClazz == String.class) {
                    boolean isEmpty = false;
                    while (!isEmpty) {
                        String keyName = paraName + "[]";
                        String[] vals = parasMap.get(keyName);
                        if (vals != null) {
                            for (String v : vals) {
                                Object vvalue = null;
                                try {
                                    vvalue = v != null ? converter.convert(genericClazz, v) : null;
                                } catch (ParseException e) {
                                    e.printStackTrace();
                                }
                                list.add(vvalue);
                            }
                        }
                        isEmpty = true;
                    }
                    break;
                }

                if (fc instanceof ParameterizedType) {//如果是泛型参数的类型
                    //开始走获取列表中泛型数据的流程
                    boolean isEmpty = false;
                    int i = 0;
                    while (!isEmpty) {
                        Object beanListT = createInstance(genericClazz);
                        String keyName = paraName + "[" + i + "]";
                        isEmpty = injectBean(beanListT, genericClazz, parasMap, keyName, converter, request, skipConvertError);
                        if (isEmpty) {
                            break;
                        }
                        list.add(beanListT);
                        i++;
                    }
                }

                break;
            }
        }
        return list;
    }


}

新建一个TypeConverterCore用来保存和处理不同类型参数的判断与转换(我看到新的代码里对应的com.jfinal.core.converter.TypeConverter中新增的几种类型可以使用新的替换掉private TypeConverterCore()部分)

public class TypeConverterCore{


    private final Map<Class<?>, IConverter<?>> converterMap = new HashMap<Class<?>, IConverter<?>>();
    private static TypeConverterCore me = new TypeConverterCore();

    private TypeConverterCore() {
        regist(Integer.class, new Converters.IntegerConverter());
        regist(int.class, new Converters.IntegerConverter());
        regist(Long.class, new Converters.LongConverter());
        regist(long.class, new Converters.LongConverter());
        regist(Double.class, new Converters.DoubleConverter());
        regist(double.class, new Converters.DoubleConverter());
        regist(Float.class, new Converters.FloatConverter());
        regist(float.class, new Converters.FloatConverter());
        regist(Boolean.class, new Converters.BooleanConverter());
        regist(boolean.class, new Converters.BooleanConverter());
        regist(java.util.Date.class, new Converters.DateConverter());
        regist(java.sql.Date.class, new Converters.SqlDateConverter());
        regist(java.sql.Time.class, new Converters.TimeConverter());
        regist(java.sql.Timestamp.class, new Converters.TimestampConverter());
        regist(java.math.BigDecimal.class, new Converters.BigDecimalConverter());
        regist(java.math.BigInteger.class, new Converters.BigIntegerConverter());
    }

    public static TypeConverterCore me() {
        return me;
    }

    public <T> void regist(Class<T> type, IConverter<T> converter) {
        converterMap.put(type, converter);
    }

    /**
     * 将 String 数据转换为指定的类型
     * @param type 需要转换成为的数据类型
     * @param s 被转换的 String 类型数据,注意: s 参数不接受 null 值,否则会抛出异常
     * @return 转换成功的数据
     */
    public final Object convert(Class<?> type, String s) throws ParseException {
        // mysql type: varchar, char, enum, set, text, tinytext, mediumtext, longtext
        if (type == String.class) {
            return ("".equals(s) ? null : s);  // 用户在表单域中没有输入内容时将提交过来 "", 因为没有输入,所以要转成 null.
        }
        s = s.trim();
        if ("".equals(s)) {    // 前面的 String跳过以后,所有的空字符串全都转成 null,  这是合理的
            return null;
        }
        // 以上两种情况无需转换,直接返回, 注意, 本方法不接受null为 s 参数(经测试永远不可能传来null, 因为无输入传来的也是"")
        //String.class提前处理

        // --------
        IConverter<?> converter = converterMap.get(type);
        if (converter != null) {
            return converter.convert(s);
        }
        if (JFinal.me().getConstants().getDevMode()) {
            throw new RuntimeException("Please add code in " + TypeConverter.class  + ". The type can't be converted: " + type.getName());
        } else {
            throw new RuntimeException(type.getName() + " can not be converted, please use other type of attributes in your model!");
        }
    }

    /**
     * 通过传入的type来判断是否是需要通过转化的类型
     * @param type 需要转换成为的数据类型
     * @return 是否存在
     */
    public boolean isExistence(Class<?> type){
        boolean b=converterMap.containsKey(type);
        return b;
    }

}

一下是测试代码

Entityone.java

public class Entityone implements Serializable {
    private static final long serialVersionUID = -8438235889469075812L;


    private String entityname;
    private int a;
    private boolean b;
    private List<Entitythree> list;
    private Entitytwo entitytwo;

    public List<Entitythree> getList() {
        return list;
    }

    public void setList(List<Entitythree> list) {
        this.list = list;
    }

    public String getEntityname() {
        return entityname;
    }

    public void setEntityname(String entityname) {
        this.entityname = entityname;
    }

    public int getA() {
        return a;
    }

    public void setA(int a) {
        this.a = a;
    }

    public boolean isB() {
        return b;
    }

    public void setB(boolean b) {
        this.b = b;
    }

    public Entitytwo getEntitytwo() {
        return entitytwo;
    }

    public void setEntitytwo(Entitytwo entitytwo) {
        this.entitytwo = entitytwo;
    }
}

Entitytwo.java

public class Entitytwo implements Serializable {
    private static final long serialVersionUID = 6211438465654735844L;

    private String nametow;

    private List<Entitythree> list;

    private List<String> lists;

    public List<String> getLists() {
        return lists;
    }

    public void setLists(List<String> lists) {
        this.lists = lists;
    }

    public String getNametow() {
        return nametow;
    }

    public void setNametow(String nametow) {
        this.nametow = nametow;
    }

    public List<Entitythree> getList() {
        return list;
    }

    public void setList(List<Entitythree> list) {
        this.list = list;
    }
}

Entitythree.java

public class Entitythree implements Serializable {
    private static final long serialVersionUID = 7135457012628287367L;
    private String ccc;
    private int a;

    public String getCcc() {
        return ccc;
    }

    public void setCcc(String ccc) {
        this.ccc = ccc;
    }

    public int getA() {
        return a;
    }

    public void setA(int a) {
        this.a = a;
    }
}

TestController.java

public class TestController extends ApiController {

    public void ajax2() {
        Entityone entityone = getBean(Entityone.class, "");
        renderJson(entityone.getList());
    }
}

ajax:

$.ajax({
    type: "post",
    url: "/test/ajax2",
    data: {
        "a": 1,
        "b": false,
        "entitytwo": {
            "list": [{"a": 33, "ccc": "ccccc"}, {"a": 33, "ccc": "ccccc"}],
            "nametow": "2222",
            "lists": ["aaaa", "bbbb"]
        },
        "list": [{"a": 33, "ccc": "ccccc"}, {"a": 33, "ccc": "ccccc"}]
    },
    dataType: "json",
    success: function (data) {
        console.log(data);
    }
})

最终效果:

发送数据:

QQ截图20180531165010.png

接受数据并打印

QQ截图20180531165024.png

大家可以看下是否能用在实际应用场景中

评论区

JFinal

2018-05-31 22:08

injectListBean 可以注入 List Bean 数据,这个功能是 jfinal 所没有的,感谢你的分享

前几天还有人问题这个功能呢,好像是 @山东小木 问的这个需求,看看这个是否满足 @山东小木 需求

山东小木

2018-05-31 23:59

@JFinal 需求类似 也是getBean 的时候 bean里有数组 这个实现的是list 我提出的是数组 特别是表单里checkbox多选后过来的数据 希望可以灵活的用字符串或者数组接 如果是字符串接 就是 1,2,3,4,5这种形式 如果是数组 自然就是String[] array 这个实现了list的也很赞 都需要的 实际项目中很多时候表单提交数据后台接收的bean五花八门 建议jfinal能有内置实现 javabean里的 list array和字符串的转换

zhc

2018-06-01 09:25

@JFinal com.jfinal.core.Controller getModels()一直是todo的状态啥时候能实现下呀(@^_^@)~

netwild

2018-06-04 20:17

对我很有用处,感谢分享

小辉大大

2019-01-10 11:15

我想知道ApiController类、InjectorCore类、TypeConverterCore类都import了那些jar包和类,我用不了你写的方法,方便告诉吗?

热门分享

扫码入社