Spring MVC Form:抽象类列表的数据绑定 [英] Spring MVC Form: data binding of a list of abstract class

查看:220
本文介绍了Spring MVC Form:抽象类列表的数据绑定的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我继承了一些我需要添加功能的代码,它涉及一个基于Spring和Spring Form构建的程序,带有一个列表和绑定的抽象元素

I've inherited some code that I need add functionality to and it involves a program built on Spring and Spring Form with a list and binding abstract elements

所以我们有一个像下面的抽象类

So we have an abstract class like the following

public abstract class A
{
    private int id;
    private String name;
    private int type;
    ....getters and setters below

}

还有一些实现,例如

public class AImplOne extends A
{
    private String text;
    ...getters, setters etc
}

public class AImplTwo extends A
{
    private int mediaID;
    ...getters, setters etc
}

所以AImpl类实现A,但根据实现类型有一个额外的信息。

So the AImpl classes implement A, but have an extra piece of information each depending on the implementation type.

然后我们有一个B类,其中包含A对象的列表,其中包含A的列表实现(可以是不同实现类型的混合列表)

Then we have a class B that has list of A objects which contains a list of the A implementations (can be mixed list of different implementation types)

public class B
{
    private List<A> aList;
    public B() { aList = new ArrayList<A>(); }
    ..getters and setters, etc
}

现在我们有一个在Spring中构建的表单如下所示,用于创建B对象,包括进入aList的A的实现。以下JSP代码位于具有loopStatus值$ {index}的循环内。这也是通过一个Controller加载的,它有一个类A

Now we have a form built in Spring as follows that is used to create B objects including the implementations of A that go into aList. The following JSP code is inside of a loop that has a loopStatus value ${index}. This is also loaded via a Controller, that has class A

以下JSP代码在一个循环中,为表单创建输入:

The following JSP code is in a loop that creates the inputs for the form:

<form:form method="POST" modelAttribute="B">
</form:form>

第一次迭代将为AImplOne对象设置数据

The first iteration will set data for a AImplOne object

<form:input type="hidden" path="aList[${index}].id" value="0" />
<form:input type="hidden" path="aList[${index}].type" value="Type1" />      
<form:input type="hidden" path="aList[${index}].name" value="X" />
<form:input type="hidden" path="aList[${index}].text" value="aaaa" />

然后在下一次迭代中我们为AImplTwo类设置数据

And then in the next iteration we set the data for a class AImplTwo type

<form:input type="hidden" path="aList[${index}].id" value="0" />
<form:input type="hidden" path="aList[${index}].type" value="Type2" />      
<form:input type="hidden" path="aList[${index}].name" value="X" />
<form:input type="hidden" path="aList[${index}].mediaID" value="12" />

所以我遇到的问题是加载这个JSP失败并抛出错误

So the problem I've run into is that loading this JSP fails and throws an error

Caused by: org.springframework.beans.InvalidPropertyException: Invalid property 'aList[0]' of bean class [B]: Illegal attempt to get property 'aList' threw exception; nested exception is org.springframework.beans.NullValueInNestedPathException: Invalid property 'aList' of bean class [B]: Could not instantiate property type [A] to auto-grow nested property path: java.lang.InstantiationException

这是完全可以理解的,因为aList具有抽象类A的元素,因此它无法实例化该类A.但是,基于输入我可以确定一个是AImplOne而另一个是AImplTwo

This is completely understandable as aList has elements of abstract class A so it can't instantiate that class A. However, based on the inputs I can determine that one is AImplOne and the other is AImplTwo

我正在努力做的,本质上是实现可以覆盖Springs默认表单绑定的功能为了能够正确初始化正确的实现,并能够动态生成这个aList,以便[AImplOne,AImpleTwo]作为内容。

What I'm trying to be able to do, is essentially implement functionality that can override Springs default form binding for this to be able to correctly initialize the correct implementation and be able to dynamically generate this aList to have [AImplOne, AImpleTwo] as it's content.

是否有一个解析器类或者我可以实现的东西,为程序提供这个自定义功能(读取数据并实例化正确的对象)

Is there a resolver class or something along those lines that I can implement to provide this custom functionality for the program (read the data and instantiate the correct object)

推荐答案


我正在努力做的,实质上是实现
功能,可以覆盖弹簧默认表单绑定
能够正确初始化正确的实现,并且
能够动态生成此aList,以便[AImplOne,AImpleTwo]
作为内容。

What I'm trying to be able to do, is essentially implement functionality that can override Springs default form binding for this to be able to correctly initialize the correct implementation and be able to dynamically generate this aList to have [AImplOne, AImpleTwo] as it's content.

您可以在控制器类中使用自定义init binder方法执行此操作:

You can do this using custom init binder method in your controller class:

// Additionally using name of object used with @ModelAttribute
// as "value" parameter of this annotation is not working in some cases
@InitBinder
public void initBinder(
    WebDataBinder webDataBinder, HttpServletRequest httpServletRequest) {

    // You only want to init this when form is submitted
    if (!"POST".equalsIgnoreCase(httpServletRequest.getMethod()) {
        return;
    }

    // Filter out all request when we have nothing to do
    Object nonCastedTarget = webDataBinder.getTarget();
    if (nonCastedTarget == null || !(nonCastedTarget instanceof B)) {
        return;
    }


    // TODO: Better cache this in static final field instead
    Pattern pattern = Pattern.compile("aList\\[(\\d+)]\\.type");

    Map<Integer, String> types = new HashMap<>();
    Enumeration<String> parameterNames = httpServletRequest.getParameterNames();
    while (parameterNames.hasMoreElements()) {
        String element = parameterNames.nextElement();
        Matcher matcher = pattern.matcher(element);
        if (!matcher.matches()) {
            continue;
        }

        types.put(
            Integer.parseInt(matcher.group(1)),
            httpServletRequest.getParameter(element)
        );
    }


    B target = (B) nonCastedTarget;
    List<A> aList = target.getAList();
    if (aList == null) {
        target.setAList(new ArrayList<>());
    }

    types.keySet().stream().sorted().forEach(key -> {

        switch (types.get(key)) {
            case "Type1":
                target.getAList().add(new AImplOne());
                break;

            case "Type2":
                target.getAList().add(new AImplTwo());
                break;

            default:
                throw new IllegalStateException("Unknown type: " + key);
        }
    });
}

上面的代码只是一个简单的工作版本,例如几乎没有实现错误处理,没有提取的常量 - 所以你必须改进它以使它更适合生产。

The code above is just a simple working version, e.g. almost no error handling is implemented, no extracted constants - so you must improve it to make it more production-ready.

这篇关于Spring MVC Form:抽象类列表的数据绑定的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆