
SpringMVC 框架结构

SpringMVC 框架的默认的实现者

HTTP请求地址映射
通过URL限定:URL表达式
@RequestMapping不但支持标准的URL,还支持Ant风格(即?、*、和**的字符)的和带{XXX}占位符的URL。以下URL都是合法的:
- /user/*/createUser 匹配/user/aaa/createUser、 /user/bbb/createUser等URL。
- /user/**/createUser 匹配/user/createUser、 /user/aaa/bbb/createUser等URL。
- /user/createUser?? 匹配/user/createUseraa、 /user/createUserbb等URL。
- /user/{userId} 匹配user/123、 user/abc等URL。
- /user/**/{userId} 匹配user/aaa/bbb/123、 user/aaa/456等URL。
- company/{companyId}/user/{userId}/detail 匹配company/123/user/456/detail等的URL。
通过URL限定:绑定{XXX}中的值
1 | ("/{userId}") |
1 |
|
通过请求方法限定:请求方法
请求方法,在HTTP中这被叫做动词( verb),除了两个大家熟知的( GET和POST)之外,标准方法集合中还包含PUT、 DELETE、 HEAD和OPTIONS。这些方法的含义连同行为许诺都一起定义在HTTP规范之中。一般浏览器只支持GET和POST方法。
1 | 1 GET 使用GET方法检索一个表述( representation) ——也就是对资源的描述。多次执行同一GET请求,不会对系统造成影响, GET方法具有幂等性[指多个相同请求返回相同的结果]。 GET请求可以充分使用客户端的缓存。 |
通过请求方法限定:模拟请求方法
通过在web.xml中配置一个
org.springframework.web.filter.HiddenHttpMethodFilter
通过POST请求的_method参数指定请求方法, HiddenHttpMethodFilter
动态更改HTTP头信息。
通过请求/请求头参数限定:示例
通过请求参数限定
1
2
3
4(value="/delete", params="userId")
public String test1(@RequestParam("userId") String userId){
...
}通过请求头参数限定
1
2
3
4(value="/show",headers="content-type=text/*")②
public String test2(@RequestParam("userId") String userId){
...
}
通过请求/请求头参数限定:更多
params和headers分别通过请求参数及报文头属性进行映射,它们
支持简单的表达式,下面以params表达式为例说明, headers可以参照
params进行理解之。
- “param1”: 表示请求必须包含名为param1的请求参数。
- “!param1”: 表示请求不能包含名为param1的请求参数。
- “param1!=value1”: 表示请求包含名为param1的请求参数,但
其值不能为value1。
- {“param1=value1”,”param2”}: 请求必须包含名为param1和
param2的两个请求参数,且param1参数的值必须为value1。
HTTP请求数据的绑定
通过注解绑定:示意图

通过注解绑定:示例
1 | (value="/handle1") |
1 | (value="/handle2") |
- 过注解绑定:小心抛出异常
@RequestParam有以下三个参数。 - value:参数名。
- required:是否必需,默认为true,表示请求中必须包含对应的参数名,如
果不存在将抛出异常。 - defaultValue:默认参数名,设置该参数时,自动将required设为false。极
少情况需要使用该参数,也不推荐使用该参数。
1 | (value="/handle1") |
上面的处理方法 ,如果HTTP请求不包含“ userName”参数时,将产生异常!!
因此,如果不能保证存在”userName”的参数,必须使用:
@RequestParam(value = “userName”, required = false)
使用命令/表单对象绑定
所谓命令/表单对象并不需要实现任何接口,仅是一个拥有若干属性的POJO。
Spring MVC按:
“ HTTP请求参数名 = 命令/表单对象的属性名”的规则,自动绑定请求数据,支持“级联属性名”,自动进行基本类型数据转换。1
2
3
4
5
6
7
8
9(value = "/handle14")
public String handle14(User user) {
…
}
userName=xxx&password=yyy ----->>>>>class User{
private String userName;
private String password;
}
使用Servlet API对象作为入参
在Spring MVC中,控制器类可以不依赖任何Servlet API对象,但是Spring MVC并不阻止我们使用Servlet API的类作为处理方法的入参。值得注意的是, 如果处理方法自行使用HttpServletResponse返回响应,则处理方法的返回值设置成void即可。1
2
3
4
5(value = "/handle21")
public void handle21(HttpServletRequest request,HttpServletResponse response) {
String userName = WebUtils.findParameterValue(request, "userName");
response.addCookie(new Cookie("userName", userName));
}
1 | public String handle23(HttpSession session) { |
1 | public String handle24(HttpServletRequest request, |
使用Spring的Servlet API代理类
Spring MVC在org.springframework.web.context.request包中定义了若干个可代理Servlet原生API类的接口,如WebRequest和NativeWebRequest,它们也允许作为处理类的入参,通过这些代理类可访问请求对象的任何信息。1
2
3
4
5(value = "/handle25")
public String handle25(WebRequest request) {
String userName = request.getParameter("userName");
return "success";
}
使用IO对象作为入参
Spring MVC允许控制器的处理方法使用java.io.InputStream/java.io.Reader及java.io.OutputStream/java.io.Writer作为方法的入参
1 | (value = "/handle31") |
其他类型的参数
控制器处理方法的入参除支持以上类型的参数以外,还支持java.util.Locale、java.security.Principal,可以通过Servlet的HttpServletRequest 的getLocale()及getUserPrincipal()得到相应的值。如果处理方法的入参类型为Locale或Principal, Spring MVC自动从请求对象中获取相应的对象并传递给处理方法的入参。
1 | (value = "/handle32") |
使用@RequestBody/@ResponseBody
将HttpServletRequest的getInputStream()内容绑定到入参,将处理方法
返回值写入到HttpServletResponse的getOutputStream()中。1
2
3
4
5(value = "/handle41")
public String handle41(@RequestBody String requestBody ) {
System.out.println(requestBody);
return "success";
}
1 |
|
- 优点:处理方法签名灵活不受限
- 缺点:只能访问报文体,不能访问报文头
使用HttpEntity/ResponseEntity
1 | (value = "/handle43") |
1 | (params = "method=login") |
- 优点:处理方法签名受限
- 缺点:不但可以访问报文体,还能访问报文头
使用HttpEntity
/ResponseEntity 1
2
3
4
5
6(value = "/handle51")
public ResponseEntity<User> handle51(HttpEntity<User> requestEntity){
User user = requestEntity.getBody();
user.setUserId("1000");
return new ResponseEntity<User>(user,HttpStatus.OK);
}
对于服务端的处理方法而言,除使用@RequestBody/@ResponseBody或HttpEntity
输出XML和JSON

数据转换、格式化、检验
数据绑定机理

数据类型转换
低版本的Spring 只支持标准的PropertyEditor类型体系,不过
PropertyEditor存在以下缺陷:
- 只能用于字符串和Java对象的转换,不适用于任意两个Java类型之间的转
换; - 对源对象及目标对象所在的上下文信息(如注解、所在宿主类的结构等)不敏感,在类型转换时不能利用这些上下文信息实施高级转换逻辑。
有鉴于此, Spring 3.0在核心模型中添加了一个通用的类型转换模块,ConversionService是Spring类型转换体系的核心接口。Spring 3.0同时支持PropertyEditor和ConversionService 进行类型转换,在Bean配置、 Spring MVC处理方法入参绑定中使用类型转换体系进行工作。
PropertyEditor依然有效
对于简单的类型转换,依然建议使用PropertyEditor。按照PropertyEditor的协议,会自动查找Bean类相同类包是否存在
com.book.core.cache.expired
|CacheSpace.java
| CacheSpaceEditor.java1
2
3
4
5
6
7
8
9
10<bean id="expireManager"
class="com.book.core.cache.expire.SimpleCacheLogicExpireManager">
<property name="cacheSpaceList">
<list>
<value>comBookSpace:com/comBook/**</value>
<value>bookSpace:com/book/**:100</value>
<value>companySpace:com/company/**</value>
</list>
</property>
</bean>
强大的ConversionService
由于ConversionService在进行类型转换时,可以使用到Bean所在宿主类的上下文信息(包括类结构,注解信息),所以可以实施更加高级的类型转换,如注解驱动的格式化等功能。1
2
3
4public class User {
(pattern="yyyy-MM-dd")
private Date birthday;
}
以上User类,通过一个@DateTimeFormat注解,为类型转换提供了一些“额外”的信息,即代表日期的“源字符器”格式是“ yyyy-MM-dd”
基于ConversionService体系,定义自定义的类型转换器
定义自定义转换器:StringTOUserConverter
注册自定义转换器:1
2
3
4
5
6
7
8
9<mvc:annotation-driven conversion-service="conversionService"/>
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="com.baobaotao.domain.StringToUserConverter"/>
</list>
</property>
</bean>
格式化:带格式字符串<-->内部对象 相互转换-->

使用支持格式化的转换器
1 | <mvc:annotation-driven conversion-service="conversionService"/> |
值得注解的是, mvc:annotation-driven/标签内部默认创建的
ConversionService实例就是一个FormattingConversionServiceFactoryBean,
自动支持如下的格式化注解:
- @NumberFormatter:用于数字类型对象的格式化。
- @CurrencyFormatter:用于货币类型对象的格式化。
- @PercentFormatter:用于百分数数字类型对象的格式化。
数据校验框架
Spring 3.0拥有自己独立的数据校验框架,同时支持JSR 303标准的校验框架。 Spring 的DataBinder在进行数据绑定时,可同时调用校验框架完成数据校验工作。在Spring MVC中,则可直接通过注解驱动的方式进行数据校验。
Spring的org.springframework.validation是校验框架
所在的包
JSR 303
SR 303是Java为Bean数据合法性校验所提供的标准框架,它已经包含在Java EE6.0中。 JSR 303通过在Bean属性上标注类似于@NotNull、 @Max等标准的注解指定校验规则,并通过标准的验证接口对Bean进行验证。
你可以通过http://jcp.org/en/jsr/detail?id=303了解JSR 303的详细内容。
数据校验框架
mvc:annotation-driven/会默认装配好一个LocalValidatorFactoryBean,通过在处理方法的入参上标注@Valid注解即可让Spring MVC在完成数据绑定后执行数据校验的工作。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public class User {
(regexp="w{4,30}")
private String userName;
(min=2,max=100)
private String realName;
(pattern="yyyy-MM-dd")
private Date birthday;
(value="1000.00")
(value="100000.00")
(pattern="#,###.##")
private long salary;
}
注意: Spring本身没有提供JSR 303的实现,所以必须将JSR 303的实现者(如Hibernate Validator)的jar文件放到类路径下, Spring将自动加载并装配好JSR 303的实现者。
如何使用注解驱动的校验
1 |
|
在已经标注了JSR 303注解的表单/命令对象前标注一个@Valid,Spring MVC框架在将请求数据绑定到该入参对象后,就会调用校验框架根据注解声明的校验规则实施校验。
使用校验功能时,处理方法要如何签名??

Spring MVC是通过对处理方法签名的规约来保存校验结果的:
前一个表单/命令对象的校验结果保存在其后的入参中,这个保存校验结果的入参必须是BindingResult或Errors类型,这两个类都位于org.springframework.validation包中。
校验错误信息存放在什么地方??

- Spring MVC将HttpServletRequest对象数据绑定到处理方法的入
参对象中(表单/命令对象); - 将绑定错误信息、检验错误信息都保存到隐含模型中;
- 本次请求的对应隐含模型数据存放到HttpServletRequest的属性列
表中,暴露给视图对象。
页面如何显示错误信息
1 | <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF- |
数据模型控制
数据模型访问结构

访问数据模型:ModelAndView
1 | (method = RequestMethod.POST) |
访问数据模型: @ModelAttribute
- 使用方式一
1
2
3
4
5(value = "/handle61")
public String handle61(@ModelAttribute("user") User user){
user.setUserId("1000");
return "/user/createSuccess";
}
- Spring MVC将HTTP请求数据绑定到user入参中,然后再将user对象添加到数
据模型中。
- 使用方式二

访问数据模型: Map及Model
org.springframework.ui.Model和java.util.Map:1
2
3
4
5
6
7(value = "/handle63")
public String handle63(ModelMap modelMap){
modelMap.addAttribute("testAttr","value1");
User user = (User)modelMap.get("user");
user.setUserName("tom");
return "/user/showUser";
}
Spring MVC一旦发现处理方法有Map或Model类型的入参,就会将请求内在的隐含模型对象的引用传给这些入参。
访问数据模型: @SessionAttributes
如果希望在多个请求之间共用某个模型属性数据,则可以在控制器类标注一个@SessionAttributes, Spring MVC会将模型中对应的属性暂存到HttpSession中:
一场由@SessionAttributes引发的血案
->
org.springframework.web.HttpSessionRequire
dException: Session attribute ‘user’ required -
not found in session…
对入参标注@ModelAttribute(“xxx”)的处理方法, Spring MVC按如下流程处理( handle71(@ModelAttribute(“user”) User user)):
- 如果隐含模型拥有名为xxx的属性,将其赋给该入参,再用请求消息填充该入参对象直接返回,否则到2步 。
- 如果xxx是会话属性,即在处理类定义处标注了@SessionAttributes(“xxx”),则尝试从会话中获取该属性,并将其赋给该入参,然后再用请求消息填充该入参对象。 如果在会话中找不到对应的属性,则抛出HttpSessionRequiredException异常。 否则到 3。
- 如果隐含模型不存在xxx属性,且xxx也不是会话属性,则创建入参的对象
实例,再用请求消息填充该入参
如何避免?
视图及解析器
Spring MVC如何解析视图

视图解析器类型
InternalResourceViewResolver
- FreeMarkerViewResolver
- BeanNameViewResolver
- XmlViewResolver
- …
完成单一解析逻辑的视图解析器:
基于协商的视图解析器: - ContentNegotiatingViewResolver
该解析器是Spring 3.0新增的,它不负责具体的视图解析,而是作为一个中间人的角色根据请求所要求的MIME类型,从上下文中选择一个适合的视图解析器,再将视图解析工作委托其负责
基于协商的视图解析器
1 | <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver" |
其他
静态资源处理
Spring MVC 3.0提供的最
强大的功能之一!!!
1.静态资源处理方式
2.静态资源映射
静态资源处理:使REST风格的URL成为实现
优雅REST风格的资源URL不希望带.html或.do等后缀,以
下是几个优雅的URL。
- /blog/tom:用户tom的blog资源。
- /forum/java: java论坛板块资源。
- /order/4321:订单号为4321的订单资源;
静态资源处理:原理

静态资源处理:如何配置?
- 第一步: web.xml让所有请求都由Spring MVC处理
1 | <servlet> |
- 第二步: springServlet-servlet.xml 让Web应用服务器处理静态资源
1
<mvc:default-servlet-handler/>
获取应用服务器的默认Servlet,大多数应用服务器的Servlet的名称都是“ default”,如果默认不是“ default”则使用1
2<mvc:default-servlet-handler
default-servlet-name=“<defaultServletName>"/>
物理静态资源路径映射逻辑资源路径
1 | <mvc:resources mapping="/resources/**" |

1 | <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF- |
允许利用浏览器的缓存且不担心不同步
1 | <mvc:default-servlet-handler/> |
1 | public class ResourcePathExposer implements ServletContextAware { |
1 | <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF- |
本地化
本地化原理
一般情况下, Web应用根据客户端浏览器的设置判断客户端的本地化类型,用户可以通过IE菜单: 工具→Internet选项…→语言…在打开的“语言首选项”对话框中选择本地化类型。
浏览器中设置的本地化类型会包含在HTML请求报文头中发送给Web服务器,确切地说是通过报文头的Accept-Language参数将“语言首选项”对话框中选择的语言发送到服务器,成为服务器判别客户端本地化类型的依据。
SpringMVC本地化解析器
- AcceptHeaderLocaleResolver:根据HTTP报文头的Accept-Language
参数确定本地化类型,如果没有显式定义本地化解析器, Spring MVC默
认采用AcceptHeader- LocaleResolver。 - CookieLocaleResolver:根据指定Cookie值确定本地化类型。
- SessionLocaleResolver:根据Session中特定的属性值确定本地化类
型。 - LocaleChangeInterceptor:从请求参数中获取本次请求对应的本地化
类型。
LocaleChangeInterceptor:通过URL参数指定
很多国际型的网站都允许通过一个请求参数控制网站的本地化,如
www.xxx.com? locale=zh_CN返回对应中国大陆的本地化网页,而
www.xxx.com?locale=en返回本地化为英语的网页。
这样,网站使用者可以通过URL的控制返回不同本地化的页面,非常灵活。
1 | <bean id="localeResolver" |