我已经通过Spring文档和源代码,仍然没有找到我的问题的答案。
我在域模型中有这些类,并希望在spring-mvc中将它们用作支持表单对象。
public abstract class Credentials {
private Long id;
....
}
public class UserPasswordCredentials extends Credentials {
private String username;
private String password;
....
}
public class UserAccount {
private Long id;
private String name;
private Credentials credentials;
....
}
我的控制器:
@Controller
public class UserAccountController
{
@RequestMapping(value = "/saveAccount", method = RequestMethod.POST)
public @ResponseBody Long saveAccount(@Valid UserAccount account)
{
//persist in DB
return account.id;
}
@RequestMapping(value = "/listAccounts", method = RequestMethod.GET)
public String listAccounts()
{
//get all accounts from DB
return "views/list_accounts";
}
....
}
在UI上,我有不同凭据类型的动态表单。我的POST请求通常如下:
name name
credentials_type user_name
credentials.password password
credentials.username username
如果我尝试向服务器提交请求,则抛出以下异常:
org.springframework.beans.NullValueInNestedPathException: Invalid property 'credentials' of bean class [*.*.domain.UserAccount]: Could not instantiate property type [*.*.domain.Credentials] to auto-grow nested property path: java.lang.InstantiationException
org.springframework.beans.BeanWrapperImpl.newValue(BeanWrapperImpl.java:628)
我最初的想法是使用@ModelAttribute
@ModelAttribute
public PublisherAccount prepareUserAccountBean(@RequestParam("credentials_type") String credentialsType){
UserAccount userAccount = new PublisherAccount();
Class credClass = //figure out correct credentials class;
userAccount.setCredentials(BeanUtils.instantiate(credClass));
return userAccount;
}
这种方法的问题是prepareUserAccountBean
方法在任何其他方法(如listAccounts
)之前被调用,这是不合适的。
一个强大的解决方案是将prepareUserAccountBean
和saveUserAccount
移出到单独的Controller。听起来不对:我希望所有与用户相关的操作都驻留在同一个控制器类中。
任何简单的解决方案?我可以以某种方式利用DataBinder,PropertyEditor或WebArgumentResolver吗?
谢谢!!!!!
答案 0 :(得分:3)
我看不到任何简单而优雅的解决方案。也许是因为问题不是如何在Spring MVC中绑定抽象类的数据,而是:为什么首先在表单对象中使用抽象类?我想你不应该。
从表单发送到控制器的对象称为“表单(支持)对象”,原因如下:对象属性应反映表单字段。如果您的表单有用户名和密码字段,那么您的班级应该有用户名和密码属性。
因此credentials
应该有UserPasswordCredentials
类型。这将跳过您的“抽象实例化尝试”错误。两个解决方案:
答案 1 :(得分:-1)
我不确定,但您应该在控制器上使用ViewModel类而不是Domain Objects。然后,在saveAccount方法中,您将验证此ViewModel,如果一切正常,您将其映射到您的域模型并保留它。
通过这样做,你有另一个优势。如果您将任何其他属性添加到您的域UserAccount类,例如: private bool isAdmin 。如果您的Web用户向您发送一个带有isAdmin = true的POST参数,该参数将绑定到用户域类并保持不变。
嗯,这就是我的方式:
public class NewUserAccount {
private String name;
private String username;
private String password;
}
@RequestMapping(value = "/saveAccount", method = RequestMethod.POST)
public @ResponseBody Long saveAccount(@Valid NewUserAccount account)
{
//...
}