java 服务层和控制器在实践中的区别
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/38677889/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Difference between service layer and controller in practice
提问by Yuriy
I have read many theories about differences between service layer and controller, and I have some questions about how to realize this in practice. One answer to Service layer and controller: who takes care of what?says:
我已经阅读了很多关于服务层和控制器之间差异的理论,我对如何在实践中实现这一点有一些疑问。服务层和控制器的一个答案:谁负责什么?说:
I try to restrict controllers to doing work related to validating http parameters, deciding what service method to call with what parameters, what to put in the httpsession or request, what view to redirect or forward to, or similar web-related stuff.
我试图限制控制器做与验证 http 参数相关的工作,决定用什么参数调用什么服务方法,在 httpsession 或请求中放入什么,重定向或转发到什么视图,或类似的 web 相关的东西。
Red Flags: My Controller architecture might be going bad if:
The Controller makes too many requests to the Service layer. The Controller makes a number of requests to the Service layer that don't return data. The Controller makes requests to the Service layer without passing in arguments.
危险信号:如果出现以下情况,我的控制器架构可能会变坏:
Controller 向 Service 层发出过多请求。控制器向服务层发出许多不返回数据的请求。控制器向服务层发出请求而不传入参数。
At the moment I am developing a web app with Spring MVC, and I have such method for saving changed user's email:
目前我正在使用 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;
}
You can see that I have a lot of requests to the service layer, and I do redirecting from controller - that is business logic. Please show better version of this method.
您可以看到我对服务层有很多请求,并且我从控制器进行重定向 - 这就是业务逻辑。请展示此方法的更好版本。
And another example. I have this method, which returns user's profile:
还有一个例子。我有这个方法,它返回用户的个人资料:
/**
* 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;
}
method convertAvaForRendering(clientFromDB.getAvatar()) is placed in super class of this controller, it is right placing of this method, or he must be placed in service layer??
方法convertAvaForRendering(clientFromDB.getAvatar())放在这个控制器的超类中,这个方法是正确的放置,还是必须放在服务层??
Help please, it is really important for me.
请帮助,这对我来说真的很重要。
采纳答案by Andreas
In both examples, why do you need to cast Client
? That's a code smell.
在这两个示例中,为什么需要强制转换Client
?那是代码异味。
Since the call to the service tier is also the call that establishes the database transaction boundary, making multiple calls means that they are executed in different transactions, and are therefore not necessarily consistent with each other.
由于对服务层的调用也是建立数据库事务边界的调用,所以多次调用意味着它们在不同的事务中执行,因此彼此不一定一致。
That is one of the reasons why multiple calls are discouraged. @ArthurNoseda mentions other good reason in his answer.
这就是不鼓励多次调用的原因之一。@ArthurNoseda 在他的回答中提到了其他很好的理由。
In your first case, there should be a single call to the service tier, e.g. something like this:
在您的第一种情况下,应该对服务层进行一次调用,例如:
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");
You could also use return value instead of exceptions.
您还可以使用返回值而不是异常。
As you can see, this will delegate the business logic of changing the email to the service tier, while keeping all UI related actions in the controller where they belong.
如您所见,这会将更改电子邮件的业务逻辑委托给服务层,同时将所有 UI 相关操作保留在它们所属的控制器中。
回答by Arthur Noseda
A Spring Controller
is generally tied to the Spring API (with classes like Model
, ModelAndView
...) or the Servlet API (HttpServletRequest
, HttpServletResponse
...). Methods can return String
outcomes that are resolved to the name of a template (JSP...). Controller
are most certainly biased toward Web GUIs, with a strong dependence upon Web technology.
SpringController
通常与 Spring API(具有类似Model
, ModelAndView
... 的类)或 Servlet API ( HttpServletRequest
, HttpServletResponse
... ) 相关联。方法可以返回String
解析为模板名称(JSP...)的结果。Controller
肯定偏向于 Web GUI,对 Web 技术有很强的依赖性。
Service
s on the other hand shouldbe designed with business logic in mind and no assumptions about the client. We could remote the service, expose it as Web Service, implement a Web front end, or a Swing client. A Service
shouldnot depend upon Spring MVC, Servlet API, and the like. That way, if you need to retarget your application, you could reuse most of the business logic.
Service
另一方面,s 的设计应该考虑到业务逻辑,而不是对客户端做任何假设。我们可以远程服务,将其公开为 Web 服务,实现 Web 前端或 Swing 客户端。AService
不应依赖于 Spring MVC、Servlet API 等。这样,如果您需要重新定位您的应用程序,您可以重用大部分业务逻辑。
As for the note concerning too many calls to the service layer from the controller layer, it mostly is a matter of performance, which IMHO is something different. If each call to the service layer queries a database, you might experience performance issues. It the service layer and the controller layer are not running in the same JVM, you could experience performance issues either. This is another very important aspect of designing your application, but it would indicate that you should fa?ade your services calls to provide coarser-grained operations to the controller layer.
至于关于控制器层对服务层调用过多的注意事项,主要是性能问题,恕我直言是不同的。如果对服务层的每次调用都查询数据库,您可能会遇到性能问题。如果服务层和控制器层不在同一个 JVM 中运行,您也可能会遇到性能问题。这是设计应用程序的另一个非常重要的方面,但它表明您应该伪装服务调用以向控制器层提供更粗粒度的操作。