繁体   English   中英

spring-mvc中抽象类的数据绑定

[英]Data binding of an abstract class in spring-mvc

我已经通过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;
    }

这种方法的问题在于,在任何其他方法(如listAccounts )之前调用prepareUserAccountBean方法也是不合适的。

一个强大的解决方案是将prepareUserAccountBeansaveUserAccount到单独的Controller。 听起来不对:我希望所有与用户相关的操作都驻留在同一个控制器类中。

有什么简单的解决 我可以以某种方式利用DataBinder,PropertyEditor或WebArgumentResolver吗?

谢谢!!!!!

我看不到任何简单而优雅的解决方案。 也许是因为问题不是如何在Spring MVC中绑定抽象类的数据,而是:为什么首先在表单对象中使用抽象类? 我想你不应该。

从表单发送到控制器的对象称为“表单(支持)对象”,原因如下:对象属性应反映表单字段。 如果您的表单包含用户名和密码字段,那么您的类中应该有用户名和密码属性。

因此credentials应具有UserPasswordCredentials类型。 这将跳过您的“抽象实例化尝试”错误。 两个解决方案:

  • 建议:将UserAccount.credentials的类型从Credentials更改为UserPasswordCredentials。 我的意思是,除UserPasswordCredentials外,UserAccount可能具有哪些凭据? 更重要的是,我敢打赌,您的数据库userAccounts具有存储为凭据的用户名和密码,因此您也可以直接在UserAccount中使用UserPasswordCredentials类型。 最后,Spring建议使用“现有业务对象作为命令或表单对象”( 请参阅doc ),因此修改UserAccount将是最佳选择。
  • 不推荐:按原样保留UserAccount,然后创建UserAccountForm类。 此类与UserAccount具有相同的属性,但UserAccountForm.credentials具有UserPasswordCredentials类型。 然后在列出/保存时,一个类(例如UserAccountService)进行转换。 此解决方案涉及一些代码重复,因此只有在您有充分理由(您无法更改的遗留实体等)时才使用它。

我不确定,但您应该在控制器上使用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)
{
    //...
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM