Spring MVC Form:抽象类

时间:2016-06-15 23:40:14

标签: java spring jsp spring-mvc spring-form

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

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

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,但是根据实现类型有一个额外的信息。

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

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

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

的Controller加载的

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

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

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

<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类型设置数据

<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失败并抛出错误

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

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

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

1 个答案:

答案 0 :(得分:0)

  

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

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

// 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);
        }
    });
}

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