MVC架构解析:控制器(Controller)篇
控制器用于接收请求,校验参数,调用 Model 层获取业务数据,构造和绑定上下文,并转给 View 层去渲染。也就是说,控制器是 MVC 的大脑,它知道接下去该让谁去做什么事。
请求处理流程
1. 路径映射和视图指向
当 HTTP 请求从客户端送达的时候,这个黑盒要完成一系列使命,那么它就有一个入口路由和一个出口路由:
- 入口路由就是路径映射,根据配置的规则,以及请求 URI 的路径,找到具体接收和处理这个请求的控制器逻辑;
- 出口路由就是视图指向,根据配置的规则,以及控制器处理完毕后返回的信息,找到需要渲染的视图页面。
这两件事情,我们当然可以使用原始的 if-else 来完成,但是一般的 MVC 都提供了更清晰和独立的解决方案。
- 放到配置文件中,好处是所有的映射都在一个文件里,方便管理。但是对于任何一个控制器逻辑,要寻找它对应的配置信息,需要去别的位置(即上文的 XML 中)寻找。这是一种代码横向分层解耦的方式,即分层方式和业务模块无关,或者说二者是“正交”的,这种方式我在 [第 11 讲] 讲解 IoC(控制反转)时会继续介绍。
- 使用注解,和控制器逻辑放在一起,好处是映射本身是和具体的控制器逻辑放在一起,当然,它们并非代码层面的耦合,而是通过注解的方式分离开。坏处是,如果需要考察所有的映射配置,那么就没有一个统一的文件可供概览。这是一种代码纵向分层解耦的方式,也就是说,配置是跟着业务模块走的。
无论使用以上哪一种方法,本质上都逃不过需要显式配置的命运。但无论哪种方法,其实都已经够简单了,可历史总是惊人的相似,总有帮“难伺候”的程序员,还是嫌麻烦!于是就有人想出了一个“终极偷懒”的办法——免掉配置。
这就需要利用 CoC 原则(Convention over Configuration,即规约优于配置)。比如,在使用 Spring MVC 这个 MVC 框架时,声明了 ControllerClassNameHandlerMapping 以后,对于这样没有配置任何映射信息的方法,会根据 Controller 类名的规约来完成映射:
public class BooksController extends AbstractController {
@Override
protected ModelAndView handleRequestInternal() throws Exception {
...
}
}
2. 请求参数绑定
请求被送到了指定的控制器方法,接下去,需要从 HTTP 请求中把参数取出来,绑定到控制器这一层,以便使用。整个控制器的流程中,有两次重要的数据绑定,这是第一次,是为了控制器而绑定请求数据,后面在视图上下文构造这一步中还有一次绑定,那是为了视图而进行的。
3. 参数验证
参数验证的操作因为和请求对象密切相关,因此通常都是在控制器层完成的。在参数验证没有通过的情况下,往往会执行异常流程,转到错误页面,返回失败请求。
4. 视图上下文绑定
在控制器中,我们经常需要将数据传入视图层,它可能会携带用户传入的参数,也可能会携带在控制器中查询模型得到的数据,而这个传入方式,就是将数据绑定到视图的上下文中。
总结思考
今天我们学习了 MVC 架构中的控制器层,整个控制器的逻辑比较密集,从请求抵达,到转出到视图层去渲染,控制器的逻辑通常包括下面这几步,但是,严格说起来,下面这些步骤的任何一步,根据实际情况,都是可以省略的。
image.png 公众号:码农架构