Spring MVC采用了松散耦合、可插拔的组件结构。Spring MVC通过一套MVC注解,让POJO成为处理请求的控制器,无需实现任何接口。Spring MVC还支持REST风格的URL请求。
1.1 Spring MVC体系结构概述
Spring MVC框架围绕DispatcherServlet这个核心展开,它截获请求并将其分派给相应的处理器处理。
1.1.1 体系结构
Spring MVC是基于Model 2实现的技术框架,Model 2 是经典MVC模型在Web应用中的变体,这个改变主要源于HTTP协议的无状态性。
Spring MVC处理请求流程:
(1)客户端发出一个HTTP请求,如果匹配DispatcherServlet的请求映射路径(在web.xml中指定),则Web容器将请求转交DispatcherServlet处理。
(2)DispatcherServlet接收到这个请求,根据请求的信息(请求参数等)及HandleMapping的配置找到处理请求的Handler。
(3)通过HandleAdapter对Handle进行封装,再以统一的适配器接口调用Handle。
(4)处理器完成业务逻辑的处理后返回一个ModelAndView给DispatcherServlet,ModelAndView包含逻辑视图名和模型数据信息。
(5)ModelAndView包含的是“逻辑视图名”而非真正的视图对象,DispatcherServlet借由ViewResolver完成逻辑视图名到真实对象的解析工作。
(6)得到真实视图对象View后,DispatcherServlet就使用这个View对象对ModelAndView中的模型数据进行视图渲染。
(7)客户端响应。
1.1.2 配置DispatcherServlet
1.配置DispatcherServlet,截获特定的URL
传统的配置方式,就是在web.xml中配置一个Servlet,并通过<servlet-mapping>指定其处理的URL。
2.DispatcherServlet内部逻辑
其中initStrategies方法将在WebApplicationContext初始化后自动执行,此时Spring上下文中Bean已经初始化完毕。该方法的工作原理:通过反射机制查找并装配Spring容器中用户显示自定义的组件Bean,如果找不到,则装配默认的组件实例。
1.2 注解驱动的控制器
1.2.1 使用@RequestMapping映射请求
在POJO类定义出标注@Controller,再通过<context:component-scan/>扫描相应的类包,即可使POJO成为一个能处理HTTP请求的控制器。
用户可以创建数量不限的控制器,分别处理不同的业务请求。每个控制器可拥有多个处理请求的方法,每个方法负责不同的请求操作。如何将请求映射到控制器方法是Spring MVC框架的重要任务之一,这项任务由@RequestMapping承担。
1.通过请求URL进行映射
@RequestMapping使用value值指定请求的URL。@RequestMapping在类处指定的URL相对于Web应用的路径,而在方法定义出指定的URL则相对于类定义处指定的URL。
1.2.2 请求处理方法签名详细说明
1.使用@RequestParam绑定请求参数
Java类反射对象默认不记录方法入参的名称,因此需要再方法入参处使用此注解指定其对应的请求参数。value:参数名。required:是否必须,默认为true。
2.使用@CookieValue绑定请求中的Cookie值
3.使用@RequestHeader绑定请求报文头的属性值
4.使用命令/表单对象绑定请求参数值
5.使用Servlet API对象作为入参
6.使用I/O对象作为入参
1.2.3 处理模型数据
1.ModelAndView
控制器处理方法的返回值如果为ModelAndView,则其既包含视图信息,又包含模型数据信息。可以简单地将模型数据看成一个Map<String, Object>对象。
2.@ModelAttribute
方法入参对象添加到模型中,则仅需在相应入参前使用此注解即可。
3.Map及Model
Spring MVC在内部使用一个org.springframework.ui.Model接口存储模型数据,它的功能类似于java.util.Map,但它比Map容易。org.springframe.ui.ModelMap实现了Model接口,而org.springframework.ui.ExtendedModelMap扩展于ModelMap的同时实现了Model接口。
Spring MVC在调用方法前会创建一个隐含的模型对象,作为模型数据的存储容器,称之为“隐含模型”。如果处理方法的入参为Map或Model,则Spring MVC会将隐含模型引用传递给这些入参。在方法体内,开发者可以通过这个入参对象访问到模型中的所有数据,也可以向模型中添加新的属性数据。
3.@SessionAttributes
在多个请求之间共用某个模型属性数据,则可以在控制器中标注一个@SessionAttributes,Spring MVC会将模型中对应的属性暂存到HttpSession中。
1.3 处理方法的数据绑定
1.3.1 数据绑定流程剖析
Spring MVC通过反射机制对目标处理方法的签名进行分析,将请求消息绑定到处理方法的入参中。数据绑定的核心部件是DataBinder。运行机制如下:
Spring MVC主框架将ServletRuquest对象及处理方法的入参对象实例传递给DataBinder,DataBinder首先调用装配在Spring Web上下文中的ConversionService组件进行数据类型换等工作,将ServletRequest中的消息传递到入参对象中,然后调用validator组件对已经绑定了请求消息的入参对象进行合法性校验,最终生成数据绑定BindingResult对象。
1.3.2 数据转换
1.ConversionService
Spring类型转换的核心接口,它位于org.springframework.core.convert,也是该包中的唯一一个接口。其中定义了4个方法。
2.Spring支持的转换器
Spring在org.springframework.core.convert.converter包中定义了3种类型的转换器接口,实现任意一个转换器接口都可以作为自定义转换器注册到ConversionServiceFactoryBean中。
1.3.3 数据格式化
如何从格式化数据中获取真正的数据以完成数据绑定,并将处理完成的数据输出为格式化的数据,是Spring格式化框架要解决的问题。Spring引入了一个新的格式化框架,这个框架位于org.springframework.format类包中。
1.Formatter<T>
Formatter<T>接口扩展于Printer<T>和Parse<T>接口。
2.注解驱动格式化的重要接口
为了让注解和格式化的属性类型关联起来,Spring在Formatter<T>所在的包中还提供了一个AnnotationFormatterFactory<A extends Annotation>接口。
1.3.4 数据校验
很多时候,同样的数据验证会出现在不同的层中,这样会导致代码冗余,违反了DRY原则。为了避免这样的情况,最好将验证逻辑和相应的域模型进行绑定,将代码验证的逻辑集中管理起来。
1.JSR-303
JSR-303是Java为Bean数据合法性校验所提供的标准框架,它已经包含在JavaEE 6.0中。JSR-303通过在Bean属性上标注类似于@NotNull,@Max等标准的注解指定校验规则,并通过标准的验证接口对Bean进行验证。
2.Spring校验框架
Spring拥有自己独立的数据校验框架,同时支持JSR-303标准的校验框架。Spring的DataBinder在进行数据绑定,可同时调用校验框架完成数据校验工作。在Spring MVC中,则可直接通过注解驱动的方式进行数据校验。Spring的org.springframework.validation是校验框架所在的包。
3.Spring MVC数据校验
<mvc:annotation-driven/>会默认装配一个LocalValidatorFactoryBean,通过在处理方法的入参上标注@Valid注解,即可让Spring MVC在完成数据绑定后执行数据校验工作。
1.4 视图和视图解析器
请求方法执行完成后,最终返回一个ModelAndView对象。对应那些返回String,View,或ModelMap等类型的处理方法,Spring MVC也会在内部将它们装配成一个ModelAndView对象,该对象包含了视图逻辑名和模型对象的信息。
Spring MVC借助视图解析器(ViewResolver)得到最终的视图对象(View),这可能是我们常见的JSP视图,也可能是基于FreeMarker、Velovity模板技术的视图,还可能是PDF、Excel、XML、JSON等各种形式的视图。
1.4.1 认识视图
为了实现视图模型和具体实现技术的解耦,Spring在org.springframework.web.servlet包中定义了一个高度抽象的View接口,该接口中定义了两个方法。
1.4.2 视图解析器
所有的视图解析器都实现了ViewREsolver接口,该接口仅有一个方法。
1.4.3 JSP和JSTL
1.使用InternalResourceViewResolver
2.使用Spring表单标签
通过Spring表单标签,可以很容易地将模型数据中的表单/命令对象绑定到HTML表单元素中。
通过GET请求获取表单页面,通过POST请求提交表单页面,因此,获取和提交表单页面的URL是相同的。只要满足这个,<form:form>标签就无须通过action属性指定表单提交的模板URL。
1.4.4 模板视图
FreeMarker和Velocity是除JSP外被使用最多的页面模板技术。页面模板编写好页面结构,模板页面中使用一些特殊的变量标识符绑定Java对象的动态数据。
Velocity视图和FreeMarker的使用过程大同小异。FreeMarker是一个模板引擎,是一个基于模板生成文本输出的通用工具。FreeMarker可以基于模板产生HTML,XML,Java源代码等多种类型的输出内容。用户可以使用FreeMarker生成基于模板的一段字符内容、直接输出为一个文件,或作为结果在程序中使用。
1.4.5 Excel
要使用Excel展示用户列表,则仅需扩展Spring的AbstractExcelView或AbstractJExcelView即可。实现buildExcelDocument()方法,在方法中使用模型数据即可构造Excel文档。AbstractExcelView基于POI API,而AbstractJExcelView基于JExcelAPI。
1.5 本地化解析
1.5.1 本地化
Web应用根据客户端浏览器的设置判断客户端的本地化类型。
1.5.2 使用CookieLocaleResolver
要使用Cookie保存本地化类型信息,服务器要做的事情很简单,只需在Spring MVC上下文中配置一个CookieLocaleResolver就可以了,DispatcherServlet会自动识别本地化解析器并装配它。
1.5.3 使用SessionLocaleResolver
SessionLocaleResolver查找Session中属性名为SessionLocaleResolver。LOCAL_SESSION_ATTRIBUTE_NAME的属性,并将其转换为Locale对象,以此作为客户端的本地化类型。
1.5.4 使用LocaleChangeInterceptor
很多国际网站都允许通过一个请求参数控制网站的本地化。网站使用者可以通过对URL的控制返回不同本地化的页面。在Spring MVC中,这一需求可以通过LocaleChangeInterceptor过滤器来完成。
1.6 文件上传
Spring MVC为文件上传提供了直接支持,这种支持是通过即插即用的MultipartResolver实现的。Spring使用Jakarta Commons FileUpload技术实现了一个MultipartResolver实现类:CommonsMultipartResolver。需要先在上下文中配置MultipartResolver。
1.6.1 配置MultipartResolver
1.6.2 编写控制器和文件上传表单页面
1.7 WebSocket支持
1.7.1 使用WebSocket
它很好地解决了浏览器和服务器全双工通信的问题,而且相对于传统的解决方案更好地节省了服务器资源和带宽并达到实时通信。Spring 4.0主要为WebSocket通信提供了以下支持:发送和接收消息的API;用来发送消息的模板;支持SockJS,用来解决浏览器、服务器及代理不支持WebSocket的问题。
WebSocket最为常见的应用场景是实现服务器和基于浏览器的的应用之间的通信。在浏览器中使用JavaScript开启了一个到达服务器的连接,服务器通过这个发送更新到浏览器中。
1.8 杂项
1.8.1 静态资源处理
通过<url-pattern>/</url-pattern>的配置,所有URL请求都将被Spring MVC的DispatcherServlet截获。
1.</mvc:default-servlet-handler>
在smart-servlet.xml中配置<mvc:default-servlet-handler/>后,会在Spring MVC上下文中定义应该org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler,它充当一个检查员的角色,对进入DispatcherServlet的URL进行筛查。如果发现是静态资源的请求,就将该请求转由Web应用服务器默认的Servlet处理;如果不是静态资源,则由DispatcherServlet继续处理。
2.采用<mvc:resources/>
由Spring MVC框架自己处理静态资源,并添加一些有用的附加功能。
<mvc:resources/>允许静态资源放置在任何地方。依据当前Page Speed、YSlow等浏览器优化原则对静态资源提供优化。
1.8.2 装配拦截器
当收到请求时,DispatcherServlet将请求交给处理器映射(HandlerMapping),让它找出对应该请求的HandlerExecutionChain对象。HandlerExecutionChain是一个执行链,它包含一个处理该请求的处理器(Handler),同时包含若干个对该请求实施拦截的拦截器(HandlerInterceptor)。当HandlerMapping返回HandlerExecutionChain后,DispatcherServlet将请求交给定义在HandlerExecutionChain中的拦截器和处理器一并处理。
1.8.3 异常处理
Spring MVC通过HandlerExecptionResolver处理程序的异常,包括处理器映射、数据绑定及处理器执行时发生的异常。仅有一个接口方法。
1.DefaultHandlerExceptionResolver
将Spring MVC的异常转换为相应的状态码。
2.AnnotationMethodHandlerExceptionResolver
它允许通过@ExceptionHandler注解指定处理特定异常的方法。
3.SimpleMappingExceptionResolver
如果希望对所有异常进行统一处理,则可使用之,它将异常类名映射为视图名,即发生异常时使用对应的视图报告异常。
1.8.4 RequestContextHolder
能够在Controller中获取request和session对象。如果直接使用该工具,会抛出空指针异常,需要再web.xml中配置一个监听器。