我已经阅读了许多有关服务层和 Controller 之间差异的理论,并且对如何在实践中实现这一点有一些疑问。 Service layer and controller: who takes care of what?的一个答案说:
并从http://www.bennadel.com/blog/2379-a-better-understanding-of-mvc-model-view-controller-thanks-to-steven-neiland.htm:
目前,我正在使用Spring MVC开发一个Web应用程序,并且我有这样的方法来保存更改的用户的电子邮件:
/**
* <p>If no errors exist, current password is right and new email is unique,
* updates user's email and redirects to {@link #profile(Principal)}
*/
@RequestMapping(value = "/saveEmail",method = RequestMethod.POST)
public ModelAndView saveEmail(
@Valid @ModelAttribute("changeEmailBean") ChangeEmailBean changeEmailBean,
BindingResult changeEmailResult,
Principal user,
HttpServletRequest request){
if(changeEmailResult.hasErrors()){
ModelAndView model = new ModelAndView("/client/editEmail");
return model;
}
final String oldEmail = user.getName();
Client client = (Client) clientService.getUserByEmail(oldEmail);
if(!clientService.isPasswordRight(changeEmailBean.getCurrentPassword(),
client.getPassword())){
ModelAndView model = new ModelAndView("/client/editEmail");
model.addObject("wrongPassword","Password doesn't match to real");
return model;
}
final String newEmail = changeEmailBean.getNewEmail();
if(clientService.isEmailChanged(oldEmail, newEmail)){
if(clientService.isEmailUnique(newEmail)){
clientService.editUserEmail(oldEmail, newEmail);
refreshUsername(newEmail);
ModelAndView profile = new ModelAndView("redirect:/client/profile");
return profile;
}else{
ModelAndView model = new ModelAndView("/client/editEmail");
model.addObject("email", oldEmail);
model.addObject("emailExists","Such email is registered in system already");
return model;
}
}
ModelAndView profile = new ModelAndView("redirect:/client/profile");
return profile;
}
您可以看到我对服务层有很多请求,并且确实从 Controller 重定向-这是业务逻辑。请显示此方法的更好版本。
还有另一个例子。我有这种方法,它返回用户的个人资料:
/**
* Returns {@link ModelAndView} client's profile
* @param user - principal, from whom we get {@code Client}
* @throws UnsupportedEncodingException
*/
@RequestMapping(value = "/profile", method = RequestMethod.GET)
public ModelAndView profile(Principal user) throws UnsupportedEncodingException{
Client clientFromDB = (Client)clientService.getUserByEmail(user.getName());
ModelAndView model = new ModelAndView("/client/profile");
model.addObject("client", clientFromDB);
if(clientFromDB.getAvatar() != null){
model.addObject("image", convertAvaForRendering(clientFromDB.getAvatar()));
}
return model;
}
方法convertAvaForRendering(clientFromDB.getAvatar())放置在此 Controller 的父类(super class)中,此方法正确放置,还是必须放置在服务层中?
请帮忙,对我来说真的很重要。
最佳答案
在两个示例中,为什么需要强制转换Client
?那是代码的味道。
由于对服务层的调用也是建立数据库事务边界的调用,因此进行多个调用意味着它们在不同的事务中执行,因此不一定彼此一致。
这是不鼓励多次通话的原因之一。 @ArthurNoseda在his answer中提到了其他很好的理由。
在您的第一种情况下,应该只调用一次服务层,例如像这样的东西:
if (changeEmailResult.hasErrors()) {
return new ModelAndView("/client/editEmail");
}
try {
clientService.updateUserEmail(user.getName(),
changeEmailBean.getCurrentPassword(),
changeEmailBean.getNewEmail());
} catch (InvalidPasswordException unused) {
ModelAndView model = new ModelAndView("/client/editEmail");
model.addObject("wrongPassword", "Password doesn't match to real");
return model;
} catch (DuplicateEmailException unused) {
ModelAndView model = new ModelAndView("/client/editEmail");
model.addObject("email", oldEmail);
model.addObject("emailExists", "Such email is registered in system already");
return model;
}
refreshUsername(newEmail);
return new ModelAndView("redirect:/client/profile");
您也可以使用返回值代替异常。
如您所见,这将把更改电子邮件的业务逻辑委托(delegate)给服务层,同时将所有与UI相关的 Action 保留在它们所属的 Controller 中。