Spring MVC
继续上次关于REST的话题,REST简述对REST设计思想做了一个简单的介绍。那么,实际应用中,如何构造Restful的Web服务呢?在 Java™ 中,可以使用以下几种方法来创建 RESTful Web Service:
-
使用 JSR 311(311)及其参考实现Jersey
-
使用 Restlet 框架
-
Spring 3.0即以后版本,REST 支持被无缝整合到 Spring 的 MVC 中了
本文先介绍使用 Spring MVC。
Spring MVC是什么
首先要理解Spring MVC,它是基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,类似Struts。
-
前端控制器是DispatcherServlet
-
应用控制器分为处理器映射器(Handler Mapping)将请求映射到处理器,处理器适配器(HandlerAdapter)支持任意的类作为处理器,视图解析器(View Resolver)进行视图管理
-
页面控制器/处理器为Controller接口的实现,也可以是任何的POJO类
核心流程
-
1.客户端发送请求,DispatcherServlet作为统一访问点,进行全局的流程控制
-
2.DispatcherServlet委托HandlerMapping, HandlerMapping把请求映射为HandlerExecutionChain对象(包含一个Handler处理器、多个HandlerInterceptor拦截器)对象
-
3.DispatcherServlet委托HandlerAdapter,HandlerAdapter把处理器包装为适配器,从而支持多种类型的处理器
-
4.HandlerAdapter根据适配的结果调用真正的处理器的方法,完成功能处理并返回一个ModelAndView对象(包含模型数据、逻辑视图名)
-
5.ViewResolver把逻辑视图名解析为具体的View
-
6.View根据传进来的Model模型数据进行渲染
-
7.返回控制权给DispatcherServlet,由DispatcherServlet返回响应给客户端
一.前端控制器DispatcherServlet配置
其本质就是一个Servlet,在web.xml中配置:
DispatcherServlet会默认加载WEB-INF/[DispatcherServlet的Servlet名字]-servlet.xml配置文件,这里会加载springmvc-servlet.xml。
url-pattern配置小结
配置servlet的时,容器会首先查找完全匹配,再查找目录匹配,最后查找扩展名匹配。 如果一个请求匹配多个“目录匹配”,容器会选择最长的匹配。
(1)“*.action”这是比较传统的方式,简单实用;
(2)“/”是rest风格的方式,后面会具体提到;
(3)“/*”请求虽然会顺利发送到Controller,但跳转到jsp页面时会被拦截,这种方式不可取。
</code></pre>
使用Spring构造restful url,通常会配置为`/`,但这种方式也会把js、jpg、css等静态资源拦截住,导致页面无法加载这些资源。这里有三种解决方案(推荐第二种):
* 1.使用mvc:default-servlet-handler
它会把url注册到SimpleUrlHandlerMapping的urlMap中,把对静态资源的访问由HandlerMapping转到`org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler`处理。DefaultServletHttpRequestHandler使用就是各个Servlet容器自己的默认Servlet。
* 2.使用mvc:resources
```xml
```
`/images/**`映射到ResourceHttpRequestHandler进行处理,location指定静态资源的位置。
* 3.在DispatcherServlet之前,让容器default的Servlet先拦截
##### 常见容器默认Servlet名字:
Tomcat, Jetty, JBoss, GlassFish:default
Google App Engine:_ah_default
Resin为:resin-file
WebLogic:FileServlet
WebSphere:SimpleFileServlet
如果配置listener来加载配置文件,Spring会创建一个全局的WebApplicationContext上下文,称为根上下文(root application context):
__与DispatcherServlet不同的是,DispatcherServlet可以同时配置多个,每个DispatcherServlet有一个自己的WebApplicationContext上下文,相互之间不可见。这个上下文继承了根上下文中所有东西。__
----------------------
#### 二.配置HandlerMapping、HandlerAdapter、HandlerInterceptor
` `是一种简写形式,自动注册DefaultAnnotationHandlerMapping与AnnotationMethodHandlerAdapter,即与上述配置等价,但自此就无法指定Interceptor了。
##### HandlerMapping接口常用实现类:
* BeanNameUrlHandlerMapping:表示将请求的URL和Bean名字映射,如URL为 “context/cyj”,则Spring配置文件必须有一个名字为“/cyj”的Bean。
* SimpleUrlHandlerMapping:通过配置文件,把一个URL映射到Controller。
* DefaultAnnotationHandlerMapping(3.0)/RequestMappingHandlerMapping(3.1):通过注解,把一个URL映射到Controller类上。
__多个HandlerMapping的执行顺序问题,order值比较小的会优先执行__
* DefaultAnnotationHandlerMapping的order属性值是:0
* ` `自动注册的 SimpleUrlHandlerMapping的order属性值是:2147483646
* ` `自动注册的SimpleUrlHandlerMapping的order属性值是:2147483647
##### HandlerAdapter接口常用实现类:
* SimpleControllerHandlerAdapter:表示所有实现了org.springframework.web.servlet.mvc.Controller接口的Bean可以作为Spring Web MVC中的处理器。
* AnnotationMethodHandlerAdapter(3.0)/RequestMappingHandlerAdapter(3.1):通过注解,把一个URL映射到Controller类的方法上。
##### HandlerInterceptor:一般我们会使用抽象类`org.springframework.web.servlet.handler.HandlerInterceptorAdapter`
HandlerInterceptor是一个接口,一般需要自己扩展该接口,除了指定给HandlerMapping外,还可以全局配置:
同样,` `是一种简写形式,与上述配置1等价。
-----------------------
#### 三.配置ViewResolver
viewClass:JstlView表示JSP模板页面需要使用JSTL标签库
prefix和suffix:查找视图页面的前缀和后缀,如果逻辑视图名为cyj,则该jsp视图页面应该存放在“WEB-INF/jsp/cyj.jsp”
##### 常用实现类
* UrlBasedViewResolver:通过配置文件,把一个视图名交给到一个View来处理
* InternalResourceViewResolver:加入了JSTL的支持
---------------------
#### 四.处理器/页面控制器Controller
在2.5之前,通过实现`org.springframework.web.servlet.mvc.Controller`接口定义处理器类。2.5引入注解式处理器支持,通过`@Controller`和 `@RequestMapping`注解定义处理器类(需要注册DefaultAnnotationHandlerMapping与AnnotationMethodHandlerAdapter)。
--------------------
#### 五.视图页面
一般为使用JSTL标签库的jsp页面,不赘述。
#### 六.其他
#### #1.中文乱码解决方案
##### 2.自动检测组件
##### 3.国际化
Spring会自动搜索message.properties、message\_zh\_CN.properties等国际化配置文件。
在页面中,引入Spring标签库:
如果有多个配置文件:
------------------------
#### 参考文档
[Spring官方在线文档库](http://docs.spring.io/spring/docs/)
[Spring MVC 教程](http://elf8848.iteye.com/blog/875830/)