第2章 Spring MVC整体架构
本章我们学习Spring MVC的各个组件的功能职责、组件之间的接口定义以及各个组件之间的交互和通信,进而从整体架构的层次上理解Spring MVC框架的基本工作原理。
2.1 组件之间的整体交互流程
一个HTTP请求发送到Web容器,Web容器就会封装一个HTTP请求(HttpServletRequest)对象,这个对象包含所有的HTTP请求信息,例如:HTTP参数以及参数值,HTTP请求头的各种元数据等。同时,Web容器会创建一个HTTP响应(HttpServletResponse)对象,用以发送HTTP响应内容给客户端用户。然后,Web容器传递HTTP请求(HttpServletRequest)对象和HTTP响应(HttpServletResponse)对象给Servlet对象的service方法。
实际上,Spring MVC的入口是一个定制化的Servlet,称为派遣器Servlet(DispatcherServlet)。这个派遣器Servlet得到HTTP请求(HttpServletRequest)对象和HTTP响应(HttpServletResponse)对象后,一个典型的Spring MVC工作流程就开始了。
接下来发生的Spring MVC工作流程如图2-1所示。
派遣器Servlet首先查找所有注册的处理器映射器(HandlerMapping)对象,然后,遍历所有的处理器映射器对象,直到一个处理器映射器对象返回一个非空的处理器执行链(HandlerExecutionChain)对象。处理器执行链对象包含一个需要处理当前HTTP请求的一个处理器(Handler)对象,如图2-1的第1步所示。
一个处理器对象被设计成了一个通用的对象类型,这里需要一个处理器适配器(HandlerAdaptor)来派遣这个控制流到一个处理器对象,因为只有支持这种类型的处理器对象的处理器适配器才知道如何去传递控制流给这个类型的处理器对象。
拿到了处理器对象以后,派遣器Servlet对象查找所有注册的处理器适配器对象,然后,遍历所有的处理器适配器对象查询是否有一个处理器适配器对象支持这个处理器对象,如图2-1第2步所示。
如果有这样的一个处理器适配器对象,则派遣器Servlet对象将控制权转交给这个派遣器适配器对象,如图表2-1第3步所示。派遣器适配器对象和真正的处理器对象是成对出现的,所以,这个支持的处理器适配器对象知道如何去使用这个处理器来处理当前请求。
最简单的一个处理器则是控制器对象(Controller)。处理器适配器对象将传递HTTP请求对象和HTTP响应对象给控制器对象,并且期待控制器返回模型和视图(ModelAndView)对象,如图2-1第4步所示。这里,模型和视图对象包含着一组模型数据和视图逻辑名称,并且最终返回给派遣器Servlet对象。
派遣器Servlet对象然后查找所有注册的视图解析器(ViewResolver)对象,并且遍历所有的视图解析器对象,直到一个视图解析器对象返回一个物理的视图(View)对象,如图表2-1第5步所示。
最后,派遣器Servlet把得到的一组模型数据传递给得到的物理视图对象,如图表2-1第6步所示。然后,视图对象则会使用表现层技术,把模型数据展现成UI界面,并且通过HTTP响应对象发送给HTTP客户端。
2.2 组件以及组件的接口
通过上一节对组件之间的整体交互流程的分析,我们看到Spring MVC是由若干组件组成的,这些组件相互独立又相互协调共同完成Spring MVC的整体工作流程。其中,每个组件都有清晰的接口定义,接口后面都有一个设计良好的类实现体系结构,清晰的抽象出公用的逻辑并且实现在通用的抽象类里,同时提供各种常用的具体实现类,进而实现一个清晰的、高可扩展的、可插拔的MVC体系结构。
这里我们介绍这些典型的组件,组件的功能以及组件所定义的接口。
2.2.1 派遣器Servlet(DispatcherServlet)
派遣器Servlet对象是Spring MVC中最核心的组件之一,也是Spring MVC的总控组件。它是一个实现类而并不是一个接口,从类的继承角度来看,派遣器Servlet类继承自FrameworkServlet类,而FrameworkServlet继承自HttpServlet类,每一层集成完成一定的功能,集成的层次越往上完成的功能约通用,抽象的程度就越高,这样的类的层级划分使得派遣器Servlet的类体系结构清晰明了、任务分明、容易扩展,派遣器Servlet类结构如下图2-2所示。
当应用初始化时,派遣器Servlet对象通过内部的Spring Web应用程序环境,找到相应的Spring MVC的各个组件,如果这些组件没有显式配置,Spring MVC将会根据默认加载策略初始化各个模块的默认实现。
当服务一个HTTP请求时,它通过一套注册的处理器映射对象找到一个处理器对象,然后,从一套注册的处理器适配器对象中找到一个支持这个处理器对象的处理器适配器对象,并且通过它把控制流转发给这个处理器对象,处理器对象结束业务逻辑的调用后把模型数据和逻辑视图传回派遣器Servlet对象。
派遣器Servlet对象再通过视图解析器对象得到真正的视图对象,把控制权交给视图对象,同时传入模型数据,视图对象会按照一定的视图层逻辑展现这些数据给HTTP的客户端用户。
2.2.2 处理器映射(HandlerMapping)
处理器映射对象是用于映射一个请求对象到一个处理器对象。一个Spring MVC实例可能会包含多个处理器映射对象,按照处理器映射对象所在的顺序,第一个返回非空处理器执行链对象的处理器映射,会被当作为有效的处理器映射对象。处理器执行链对象包含一个处理器对象和一组能够应用在处理器对象上的拦截器对象(Interceptor).
处理器映射的类结构如下图2-3所示。
HandlerMapping是一个接口,在这个接口中,通过输入一个HTTP请求对象给方法getHandler,这个方法就会输出一个处理器执行链对象。进而我们能够从处理器执行链对象获得一个处理器对象。
2.2.3 处理器适配器(HandlerAdaptor)
处理器适配器对象是用来转接一个控制流到一个指定类型的处理器对象。一个类型的处理器对象通常会对应一个处理器适配器对象的一个实现。处理器适配器对象能够判断自己是否支持某个处理器对象。如果一个处理器适配器对象支持这种类型的处理器,那么这个处理器适配器对象就可以使用这个处理器对象处理当前的这个HTTP请求。
然而,处理器对象是通用的Object类型的一个实例, 这样做是为了提供高可扩展性,允许Spring MVC可以任意的去集成其他框架的处理器对象,只需要为这个需要支持的处理器对象提供一个定制化的处理器适配器对象即可,就可以在不改变任何上层派遣器Servlet代码以及下层控制器对象代码的前提下,实现集成任意其他框架。其中,HttpRequestHandlerAdapter是用来处理流文件的处理器适配器,SimpleServletHandlerAdapter是用来转接给Sevlet的处理器适配器,RequestMappingHandlerAdapter是用来处理注释的方法的控制器。
处理器适配器的类结构如下图2-4所示。
在这个接口中,通过输入一个处理器对象给supports方法,这个方法就会返回是否支持这个处理器。通过传入一个HttpServletRequest,HttpServletResponse和一个Hanlder对象给handle方法,这个方法就会传递控制权给处理器对象。当处理器对象处理这个HTTP请求后返回数据模型和逻辑视图名称的组合对象,处理器适配器会把这个返回结果进而返回给派遣器Servlet。
最后,getLastModified方法是用来处理一个带有lastModified头信息的HTTP请求的。不是所有的处理器适配器都需要支持这个方法的。
2.2.4 处理器对象/控制器对象(Handler/Controller)
处理器对象是用于处理业务逻辑的一个基本单元,它通过传入的HTTP请求对象来决定如何处理业务逻辑和执行哪些服务,执行服务后返回相应的模型数据和逻辑视图名。一个请求的控制流是由处理器适配器传递给处理器对象的,一个类型的处理器对象一定会对应一个支持的处理器适配器。这样可以实现,处理器适配器和处理器之间的任意匹配,具有灵活的可插拔性。
处理器对象是一个通用的Object类型,控制器类型是最典型的一个处理器类型,控制器的类结构如下图2-5所示。
控制器是Spring MVC中最简单的一个处理器,这个处理器有清晰的接口定义,通常这个类型的处理器对象是通过一个简单控制处理器适配器对象(SimpleControllerHandlerAdapter)来传递控制的。
在这个接口中,通过输入一个HTTP请求对象和一个HTTP响应对象给handleRequest方法,这个方法就会返回一个处理后的模型数据和逻辑视图名的组合对象。这个对象将通过处理器适配器进而返回给派遣器Servlet。
Spring MVC之所以具有高可扩展性,在于处理器适配器和处理器的设计。这样可以让任意的一个处理器对象插入到Spring MVC的体系结构中,使它具有无限的扩展性。
2.2.5 视图解析器(ViewResolver)
视图解析器对象用于映射一个逻辑视图名称到一个真正的视图对象。当控制器处理完业务逻辑之后,通常会返回需要显示的数据模型对象和视图的逻辑名称,这样就需要一个视图解析器对象通过视图的逻辑名称解析出一个真正的视图对象,然后传递控制流给这个视图对象,由视图对象展示数据给HTTP的客户端用户。
视图解析器的类结构如图2-6所示。
在视图解析器接口中,通过输入一个逻辑视图名称和本地对象(Locale)给resolveViewName方法,这个方法就会返回一个事实上的物理视图对象。本地对象可以用来查找本地化的资源或者资源包。
2.2.6 视图(View)
视图用来把模型数据通过某种显示方式反馈给客户,通常是通过执行JSP页面来完成的。也可以通过其他的更复杂的显示技术来完成这个展示过程,例如,XML视图、JSON视图、JSTL视图,Tiles视图,报表视图和二进制文件视图等。
视图的类结构如图2-7所示。
在视图接口中,通过输入HTTP请求对象、HTTP响应对象和一个模型Map对象,这个组件就会把模型Map通过一定的显示方式输出到客户的HTTP响应对象,这就是提供给用户请求的最终响应。getContentType()能够返回这个视图所支持的内容类型,例如,XML, JSON, text/html等。
2.3 本章小结
本章我们学习Spring MVC的实现原理,具体分析了Spring MVC架构中的各个组件、组件的功能以及组件的接口定义,并分析了组件之间的整体交互流程。
从下一章开始,我们将从源码开始,深入剖析Spring MVC架构的具体实现方法。