Я ознакомился с документацией и исходным кодом 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";
}
....
}
В пользовательском интерфейсе у меня есть динамическая форма для различных типов учетных данных. Мой запрос 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
на отдельный контроллер. Это звучит неправильно: я хочу, чтобы все пользовательские операции находились в одном и том же классе контроллеров.
Любое простое решение? Могу ли я каким-либо образом использовать DataBinder, PropertyEditor или WebArgumentResolver?
Спасибо !!!!!
Я не уверен, но вы должны использовать классы ViewModel на своих контроллерах вместо объектов домена. Затем внутри вашего метода saveAccount вы должны проверить эту ViewModel, и если все пойдет правильно, вы сопоставите ее со своей моделью предметной области и сохраните ее.
Поступая таким образом, вы получаете еще одно преимущество. Если вы добавите любое другое свойство в свой класс UserAccount домена, например: private bool isAdmin. Если ваш веб-пользователь отправит вам параметр POST с isAdmin=true, который будет привязан к классу домена пользователя и сохранен.
Ну, я бы сделал так:
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)
{
//...
}