本文学习资源来自:《SpringMVC实战指南》
《Spring 3.x企业应用开发实战》
实例
- 使用MyEclipse新建web站点
添加Spring引用
修改 web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<display-name>Web</display-name>
<!-- <listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param> -->
<!-- 添加下面部分 -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 注释掉这部分,是把xml写到src/下的写法
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
-->
</web-app>
web.xml说明
- load-on-startup:表示启动容器时初始化该Servlet;
- url-pattern:表示哪些请求交给Spring Web MVC处理, “/” 是用来定义默认servlet映射的。也可以如“*.html”表示拦截所有以html为扩展名的请求。
在WEB-INF下新建文件:dispatcherServlet-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- 自动扫描的包名 -->
<context:component-scan base-package="com.test.controller"></context:component-scan>
<!-- 默认的注解映射的支持 -->
<mvc:annotation-driven />
<!-- 视图解释类 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/Views/" />
<!--可为空,方便实现自已的依据扩展名来选择视图解释类的逻辑 -->
<property name="suffix" value=".jsp" />
</bean>
</beans>
新建Hello.java
package com.test.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class Hello {
@RequestMapping(value = "/hello")
public String hello() {
System.out.println("spring mvc hello world!");
return "hello";
}
}
项目结构如下:
运行
访问:
http://localhost:8080/Web/hello
命令行输出结果:
Spring MVC3.0新特性
- 支持REST风格的URL
- 添加更多注解,可完全注解驱动
- 引入HTTP输入输出转换器
- 和数据转换、格式化、验证框架无缝集成
- 对静态资源处理提供特殊支持
- 更加灵活的控制器方法签名,可完全独立于Servlet API
Spring MVC 框架结构
package com.baoming.web;
...
@Controller
@RequestMapping("/user")
public class UserController{
@RequestMapping(value="/register")
public String register(){
return "user/register";
}
}
SpringMVC运行流程分析
说明:
1. 若一个请求匹配 DispatcherServlet 的请求映射路径(在 web.xml 中指定), WEB容器将该请求转交给 DispatcherServlet 处理
2. DispatcherServlet 接收到请求后, 将根据请求信息(包括 URL、HTTP 方法、请求头、请求参数、Cookie 等)及 HandlerMapping 的配置找到处理请求的处理器(Handler).可将 HandlerMapping 看成路由控制器,将 Handler 看成目标主机。
3. 当 DispatcherServlet 根据 HandlerMapping 得到对应当前请求的 Handler 后,通过 HandlerAdapter 对 Handler 进行封装,再以统一的适配器接口调用 Handler。
4. 处理器完成业务逻辑的处理后将返回一个 ModelAndView 给 DispatcherServlet,ModelAndView 包含了视图逻辑名和模型数据信息
5. DispatcherServlet 借助 ViewResoler 完成逻辑视图名到真实视图对象的解析
6. 得 到 真 实 视 图 对 象 View 后 , DispatcherServlet 使 用 这 个 View 对ModelAndView 中的模型数据进行视图渲染
HTTP请求映射原理
@RequestMapping
不但支持标准的URL,还支持Ant网格(即?、、*等字符)和带{xxx}占位的URL。
示例
- /user/*/createUser
- /user/**/createUser
- /user/createUser??
- /user/{userId}
- /user/**/{userId}
- comapny/{companyId}/user/{userId}/detail
通过URL限定,绑定{xxx}中的值
@RequestMapping("/{userId}")
public ModelAndView showDetail(@PathVariable("userId") String userId){
ModelAndView mav = new ModelAndView();
mav.setViewName("user/showDetail");
mav.addObject("user", userService.getUserById(userId));
return mav;
}
示例中URL中的{xxx}占位符可以通过@PathVariable(“xxx”)绑定到操作方法的入参中。
@Controller
@RequestMapping("/owners/{ownerId}")
public class RelativePathUriTemplateController {
@RequestMapping("/pets/{petId}")
public void findPet(@PathVariable String ownerId,
@PathVariable String petId, Model model) {
…
}
}
如果@PathVariable不指定参数名,只有在编译时打开debug开关(javac-debug=no)时才行,不建议这样操作。
通过请求方法限定:请求方法
示例
@RequestMapping(value="/delete", method=RequestMethod.POST )
public String test1(@RequestParam("userId") String userId){
return "user/test1";
}
通过在web.xml中配置一个org.springframework.web.filter.HiddenHttpMethodFilter通过POST请求的_method参数指定请求方法,HiddenHttpMethodFilter动态更改HTTP头信息。
通过请求参数限定
@RequestMapping(value="/delete", params="userId")
public String test1(@RequestParam("userId") String userId){
...
}
通过请求头参数限定
@RequestMapping(value="/show",headers="content-type=text/*")
public String test2(@RequestParam("userId") String userId){
...
}
params和headers分别通过请求参数及报文头属性进行映射,它们支持简单的表达式。
params表达式示例
- “param1”:表示请求必须包含名为param1的请求参数
- “!param1”:表示请求不能包含名为param1的请求参数
- “param1!=value1”:表示请求包含名为param1的请求参数,但其值不能为value1
- {“param1=value1”,”param2”} :请求必须包含名为param1和param2的两个请求参数,且param1参数的值必须为value1
通过注解绑定示例
@RequestMapping(value="/handle1")
public String handle1(@RequestParam("userName") String userName,
@RequestParam("password") String password,
@RequestParam("realName") String realName){
...
}
@RequestMapping(value="/handle2")
public String handle2(@CookieValue("JSESSIONID") String sessionId,
@RequestHeader("Accept-Language") String accpetLanguage){
...
}
@RequestParam有以下三个参数:
- value:参数名
- required:是否必须,默认为true,表示请求中必须包含对应的参数名,如果不存在将抛出异常
- defalutValue:默认参数名,设置该参数时,自动将required设为false。一般不需要设置。
使用命令/表单对象绑定
所谓命令/表单对象并不需要实现任何接口,仅是一个拥有若干属性的POJO。Spring MVC按:
”HTTP请求参数名=命令/表单对象的属性名“
的规则,自动绑定请求数据,支持级联属性名,自动进行基本类型数据转换。
@RequestMapping(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即可。
@RequestMapping(value = "/handle21")
public void handle21(HttpServletRequest request,HttpServletResponse response) {
String userName = WebUtils.findParameterValue(request, "userName");
response.addCookie(new Cookie("userName", userName));
}
public String handle23(HttpSession session) {
session.setAttribute("sessionId", 1234);
return "success";
}
public String handle24(HttpServletRequest request,
@RequestParam("userName")String userName) {
…
return "success";
}
使用Spring 的 Servlet API代理类
Spring MVC在org.springframework.web.context.request包中定
义了若干个可代理Servlet原生API类的接口,如WebRequest和NativeWebRequest,它们也允许作为处理类的入参,通过这些代理类可访问请求对象的任何信息。
@RequestMapping(value = "/handle25")
public String handle25(WebRequest request) {
String userName = request.getParameter("userName");
return "success";
}
使用IO对象作为入参
@RequestMapping(value = "/handle31")
public void handle31(OutputStream os) throws IOException{
Resource res = new ClassPathResource("/image.jpg");//读取类路径下的图片文件
FileCopyUtils.copy(res.getInputStream(), os);//将图片写到输出流中
}
其它类型的参数
- java.util.Locale
- java.security.Principal
使用@RequestBody/@ResponseBody
@RequestMapping(value = "/handle41")
public String handle41(@RequestBody String requestBody ) {
System.out.println(requestBody);
return "success";
}
@ResponseBody
@RequestMapping(value = "/handle42/{imageId}")
public byte[] handle42(@PathVariable("imageId") String imageId) throws IOException {
System.out.println("load image of "+imageId);
Resource res = new ClassPathResource("/image.jpg");
byte[] fileData =FileCopyUtils.copyToByteArray(res.getInputStream());
return fileData;
}
使用HttpEntity/ResponseEntity
@RequestMapping(value = "/handle43")
public String handle43(HttpEntity<String> httpEntity){
long contentLen = httpEntity.getHeaders().getContentLength();
System.out.println(httpEntity.getBody());
return "success";
}
@RequestMapping(params = "method=login")
public ResponseEntity<String> doFirst(){
HttpHeaders headers = new HttpHeaders();
MediaType mt=new MediaType("text","html",Charset.forName(“UTF-8"));
headers.setContentType(mt);
ResponseEntity<String> re=null;
String return = new String("test");
re=new ResponseEntity<String>(return,headers, HttpStatus.OK);
return re;
}
@RequestMapping(value = "/handle51")
public ResponseEntity<User> handle51(HttpEntity<User> requestEntity){
User user = requestEntity.getBody();
user.setUserId("1000");
return new ResponseEntity<User>(user,HttpStatus.OK);
}
控制输出XML/JSON
…