我继承了一些我需要添加功能的代码,它涉及一个基于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 ]因为它的内容。
是否有解析器类或类似的东西,我可以实现为程序提供此自定义功能(读取数据并实例化正确的对象)
答案 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);
}
});
}
上面的代码只是一个简单的工作版本,例如几乎没有实现错误处理,没有提取的常量 - 所以你必须改进它以使它更适合生产。