1 Spring MVC概述
- Spring MVC是Spring为表现层提供的基于MVC设计理念的优秀的web框架,是目前最主流的MVC框架之一。
- Spring3.0后全面超越Struts2,成为最优秀的MVC框架。
- Spring MVC通过一套MVC注解,让POJO成为处理请求的控制器,而无须实现任何的接口。
- 支持REST风格的URL请求。
- 采用了松耦合的可插拔组件结构,比其他MVC框架更具有扩展性和灵活性。
2 Spring MVC之HelloWorld
2.1 开发步骤
- 导入相应的jar包
- 在web.xml中配置DispatcherServlet
- 加入SpringMVC的配置文件
- 编写处理请求的处理器,并标识为处理器
- 编写视图
2.2 编码
2.2.1 导入jar包
2.2.2 在web.xml中配置前端控制器
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!-- 配置前端控制器 -->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring-mvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
2.2.3 在src下新建spring-mvc.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/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
<context:component-scan base-package="com.xuweiwei.handlers"/>
<!-- 配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
2.2.4 新建HelloWorld.java作为控制器
package com.xuweiwei.handlers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloWorld {
@RequestMapping(value = "/hello")
public String hello(){
System.out.println("hello");
return "success";
}
}
2.2.5 在WEB-INF下新建jsp目录,并在jsp目录下新建success.jsp文件
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
成功
</body>
</html>
2.2.6 在index.jsp文件中编写如下内容
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<a href="${pageContext.request.contextPath}/hello">helloworld</a>
</body>
</html>
2.2.7 访问
- 地址:http://localhost:8080/项目名/index.jsp
3 使用@RequestMapping映射请求
3.1 @RequestMapping使用请求头映射请求
- SpringMVC 使用@RequestMapping注解为控制器制定可以处理那些URL请求。
- 在控制器的类定义和方法定义处度可以标注,这可以从其定义知道。
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
String[] value() default {};
RequestMethod[] method() default {};
String[] params() default {};
String[] headers() default {};
String[] consumes() default {};
String[] produces() default {};
}
- 类定义处:提供初步的请求映射信息。相对于WEB应用的根目录。
- 方法处:提供进一步的细分映射信息。相对于类定义处的URL。如果类定义处没有标注@RequestMapping注解,则方法处标记的URL相对于WEB应用的根目录。
package com.xuweiwei.handlers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("test")
public class HelloWorld {
@RequestMapping(value = "/hello")
public String hello(){
System.out.println("hello");
return "success";
}
}
- 请求地址:http://localhost:8080/项目名/test/hello
package com.xuweiwei.handlers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloWorld {
@RequestMapping(value = "/hello")
public String hello(){
System.out.println("hello");
return "success";
}
}
- 请求地址:http://localhost:8080/项目名/hello
- DispatcherServlet拦截请求后,就通过控制器上的@RequestMapping提供的映射信息确定请求所对应的处理方法。
3.2 @RequestMapping使用请求方法来映射请求
- @RequestMapping不仅仅可以通过请求的URL来映射请求,还可以通过请求方法来映射请求,比如GET、POST等。
@RequestMapping(value = "/hello",method = RequestMethod.GET)
public String testGet(){
System.out.println("GET");
return "success";
}
@RequestMapping(value = "/hello",method = RequestMethod.POST)
public String testPost(){
System.out.println("POST");
return "success";
}
3.3 @RequestMapping使用请求参数来映射请求(不常用)
- @RequestMapping不仅仅可以通过请求的URL来映射请求,还可以通过请求参数来映射请求。
@RequestMapping(value = "/hello",params = {"name=aaa","password=bbb"})
public String testParams(){
System.out.println("testParams");
return "success";
}
- 请求地址:http://localhost:8080/项目名/hello?name=aaa&password=bbb
3.4 @RequestMapping使用请求头来映射请求(不常用)
- 和3.3类似,只不过将params替换成headers而已。
3.5 @PathVaiable映射URL绑定的占位符
- 带占位符的URL是Spring3.0新增的功能,该功能在SpringMVC向REST目标挺进发展过程中有着里程碑的作用。
- 通过@PathVarialbe可以将URL中占位符参数绑定到控制器处理方法的形参中
@RequestMapping(value = "/hello/{id}")
public String delete(@PathVariable(value = "id") Integer id){
return "success";
}
- 请求地址:http://localhost:8080/项目名/hello/2
3.6 REST
- REST的介绍网上一大堆,这边不多介绍,只大概介绍一下即可。
- 在HTTP协议中,四个标识操作方式的动词:GET、POST、PUT、DELETE。其中,GET用来获取资源,POST用来新建资源,PUT用来更新资源,DELETE用来删除资源。
- REST的四种URL格式如下:
- /order/1 GET 用来获取id=1的order
- /order/1 PUT 用来更新id=2的order
- /order/1 DELETE 用来删除id=1的order
- /order POST 用来新增order
- 我们知道,HTTP协议其实,只实现了GET和POST方式,那么SpringMVC是怎么实现REST风格的URL以及方法呢?其一,REST风格的URL就是通过@PathVariable注解(可以参考3.5);其二,PUT方法和DELETE方法呢,是通过HiddenHTTPMethodFilter这个过滤器实现的。
- HiddenMethodFilter的源代码如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.web.filter;
import java.io.IOException;
import java.util.Locale;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
public class HiddenHttpMethodFilter extends OncePerRequestFilter {
public static final String DEFAULT_METHOD_PARAM = "_method";
private String methodParam = "_method";
public HiddenHttpMethodFilter() {
}
public void setMethodParam(String methodParam) {
Assert.hasText(methodParam, "'methodParam' must not be empty");
this.methodParam = methodParam;
}
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String paramValue = request.getParameter(this.methodParam);
if ("POST".equals(request.getMethod()) && StringUtils.hasLength(paramValue)) {
String method = paramValue.toUpperCase(Locale.ENGLISH);
HttpServletRequest wrapper = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method);
filterChain.doFilter(wrapper, response);
} else {
filterChain.doFilter(request, response);
}
}
private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {
private final String method;
public HttpMethodRequestWrapper(HttpServletRequest request, String method) {
super(request);
this.method = method;
}
public String getMethod() {
return this.method;
}
}
}
- 通过HiddenMethodFilter的源代码,我们可以知道SpringMVC是将POST的请求包装了一下,当然你必须传递一个如{"_method":"DELETE"}这样的参数过去,当然,AJAX或传统的表单都是可以的。
- 准备工作:配置HiddenMethodFilter
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 配置前端控制器 -->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
package com.xuweiwei.handlers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class REST {
@RequestMapping(value = "/rest/{id}")
public String testGET(@PathVariable("id") Integer id){
System.out.println("获取到:"+id);
return "success";
}
}
- 地址:http://localhost:8080/项目名/rest/2
package com.xuweiwei.handlers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class REST {
@RequestMapping(value = "/rest")
public String testPOST(){
System.out.println("POST方法");
return "success";
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/rest" method="post">
<input type="submit" value="提交"/>
</form>
</body>
</html>
package com.xuweiwei.handlers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class REST {
@RequestMapping(value = "/rest/{id}", method = RequestMethod.PUT)
public String testPut(@PathVariable("id") Integer id) {
System.out.println("put方法" + id);
return "success";
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/rest/1" method="post">
<input type="hidden" name="_method" value="PUT"/>
<input type="submit" value="提交"/>
</form>
</body>
</html>
- 这个时候,会报错405,意思就是JSP只能发送GET或POST请求,为什么会出错?
- 我们可以想一下,我们平时写代码的时候,如果是新增的话,增加完毕之后,是重定向到查询页面的,而SpringMVC返回字符串默认的是转发,所以,我们可以将返回值改为重定向到查询方法,然后由查询方法转发到jsp页面。
- 当然,在上面的代码中,我们返回的是一个字符串,SpringMVC会通过视图解析器,拼凑返回的地址,会转发到success.jsp,这个时候,Spring会认为是一个JSP接口,而JSP接口仅仅能支持GET或POST方法,所以会报错,所以,需要加上@ResponseBody强制的让Spring认为返回值就是一个字符串。
- 还有一种解决方法,就是将Tomcat8将为Tomcat7
package com.xuweiwei.handlers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class REST {
@RequestMapping(value = "/rest/{id}", method = RequestMethod.PUT)
@ResponseBody
public String testPut(@PathVariable("id") Integer id) {
System.out.println("put方法" + id);
return "success";
}
}
package com.xuweiwei.handlers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class REST {
@RequestMapping(value = "/rest/{id}", method = RequestMethod.DELETE)
@ResponseBody
public String testDelete(@PathVariable("id") Integer id) {
System.out.println("delete方法" + id);
return "success";
}
}
<form action="${pageContext.request.contextPath}/rest/1" method="post">
<input type="hidden" name="_method" value="DELETE"/>
<input type="submit" value="提交"/>
</form>
4 请求处理方法的签名
4.1 @RequestParam
- 在处理方法形参中使用@RequestParam可以将请求参数传递给请求方法
- value:参数 名
- required:是否必须,默认为true,表示请求参数中欧必须包含对应的参数,如果不存在,就抛出异常
- defaultValue:默认值
package com.xuweiwei.handlers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class RequestParamTest {
@RequestMapping(value = "/testRequestParam")
public String testRequestParam(@RequestParam("username") String username){
System.out.println("username:"+username);
return "success";
}
}
- 地址:http://localhost:8080/项目名/testRequestParam?username=xuweiwei
package com.xuweiwei.handlers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class RequestParamTest {
@RequestMapping(value = "/testRequestParam")
public String testRequestParam(@RequestParam("username") String username,
@RequestParam(value = "age",required = false,defaultValue = "0") Integer age){
System.out.println("username:"+username);
System.out.println(age);
return "success";
}
}
- 地址:http://localhost:8080/项目名/testRequestParam?username=xuweiwei
4.2 使用POJO对象绑定请求参数值
- SpringMVC会按请求参数名和POJO属性名进行自动匹配,自动为该对象填充属性值,支持级联属性。
package com.xuweiwei.handlers;
public class User {
private String username;
private String password;
private Dept dept;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", dept=" + dept +
'}';
}
}
package com.xuweiwei.handlers;
public class Dept {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Dept{" +
"name='" + name + '\'' +
'}';
}
}
package com.xuweiwei.handlers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class POJO {
@RequestMapping("/pojo")
public String handler(User user){
System.out.println(user);
return "success";
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/pojo" method="post">
username:<input type="text" name="username"/><br/>
password:<input type="password" name="password"/><br/>
deptName:<input type="text" name="dept.name"/><br/>
<input type="submit" value="新增"/>
</form>
</body>
</html>
4.3 使用Servlet API作为参数
- 在SpringMVC中的方法可以接受如下的Servlet API类型的参数。
- HttpServletRequest
- HttpServletResponse
- HttpSession
- java.security.Principal
- Locale
- InputStream
- OutputStream
- Reader
- Writer
package com.xuweiwei.handlers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
@Controller
public class HttpServletRequestHandler {
@RequestMapping(value = "/testHttpServletRequest")
public String testHttpServletRequest(HttpServletRequest request){
String username = request.getParameter("username");
System.out.println(username);
return "success";
}
}
- 地址:http://localhost:8080/项目名/testHttpServletRequest?username=abc
5 处理模型数据
- SpringMVC提供了如下的途径输出模型数据:
- ModelAndView:处理方法返回值类型为ModelAndView时,方法体就可以通过该对象添加模型数据。
- Map或Model:入参为org.springframework.ui.Model、org.springframework.ui.ModelMap或java.util.Map时,处理方法返回时,Map中的数据会自动添加到模型中。
- @SessionAttributes:将模型中的某个属性暂时存放到HttpSession中,以便多个请求之间可以共享这个属性。
- @ModelAttribute:方法入参标注该注解,入参对象就会放到数据模型中。
- ModelAndView
- 控制器处理方法的返回值如果为ModelAndView,则既要包含视图信息。也包含模型数据信息。
- 添加模型数据的方法如下:
public ModelAndView addObject(String attributeName, Object attributeValue) {}
public ModelAndView addAllObjects(Map<String, ?> modelMap) {}
public void setView(View view){}
public void setViewName(String viewName) {}
public ModelAndView(String viewName, Map<String, ?> model) {}
package com.xuweiwei.handlers;
public class User {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
package com.xuweiwei.handlers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class ModelAndViewHandler {
@RequestMapping(value = "/testModelAndView")
public ModelAndView testModelAndView(){
ModelAndView modelAndView = new ModelAndView();
User user = new User();
user.setUsername("abc");
user.setPassword("123456");
modelAndView.addObject("user",user);
modelAndView.setViewName("success");
return modelAndView;
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${user.username}<br/>
${user.password}
</body>
</html>
- 地址:http://localhost:8080/项目名/testModelAndView
- Model及ModelMap
- SpringMVC在内部会使用一个org.springframework.ui.Model接口存储模型数据
- 示例:
package com.xuweiwei.handlers;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class ModelAndViewHandler {
@RequestMapping(value = "/testModelAndView")
public String testModelAndView(ModelMap model){
User user = new User();
user.setUsername("abc");
user.setPassword("123456");
model.addAttribute("user",user);
return "success";
}
}
6 视图
- 视图和视图解析器
- 请求处理方法执行完成后,最终返回一个ModelAndView对象,对于那些返回String、View或ModelMap等类型的处理方法,SpringMVC也会在内部将它们装配成一个ModelAndView对象,它包含了逻辑视图名和模型对象的视图。
- SpringMVC借助视图解析器(ViewResolver)得到最终的视图对象(View),最终的视图可以是JSP,也可以是Excel或JFreeChar等各种形式的视图。
- 对于最终究竟采取何种视图对象对模型数据进行渲染,处理器并不关系,处理器的工作重点在生产模型数据的工作上,从而实现MVC的充分解耦。
- 视图
- 视图的作用是渲染视图,将模型中的数据以某种形式呈现给用户。
- 为了实现视图模型和具体实现技术的解耦,Spring定义了一个高度抽象的View接口,详细代码如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.web.servlet;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface View {
String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() + ".responseStatus";
String PATH_VARIABLES = View.class.getName() + ".pathVariables";
String SELECTED_CONTENT_TYPE = View.class.getName() + ".selectedContentType";
String getContentType();
void render(Map<String, ?> var1, HttpServletRequest var2, HttpServletResponse var3) throws Exception;
}
- 视图对象由视图解析器负责示例化,由于视图是无状态的,所以是线程安全的。
- 视图解析器
- SpringMVC为逻辑视图名的解析提供了不同的策略,可以在SpringMVC中配置一种或多种解析策略,并且制定它们之间的顺序。每一种映射策略对应一个具体的视图解析器实现类。
- 视图解析器的作用比较单一,将逻辑视图解析为一个具体的视图对象。
- 所有的视图解析器必须实现ViewResolver接口,详细代码如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.web.servlet;
import java.util.Locale;
public interface ViewResolver {
View resolveViewName(String var1, Locale var2) throws Exception;
}
- 【温馨小提示】如果不希望通过handler转发到WEB-INF目录下的jsp页面,可以在spring-mvc.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/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
<context:component-scan base-package="com.xuweiwei.handlers"/>
<!-- 配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<mvc:view-controller path="/index" view-name="index"/>
<mvc:annotation-driven />
</beans>
- 重定向
- 一般情况下,控制器方法返回的字符串类型的值会被当成逻辑视图名处理。
- 如果返回的字符串中带有forward:或redirect:前缀的时候,SpringMVC会对它们进行特殊处理,将forward:和redirect:当成指示符,其后的字符串作为URL来处理。
- 例如:
- redirect:success.jsp 会完成一个到success.jsp的重定向的操作
- forward:success.jsp 会完成一个到success.jsp的转发的操作
7 处理静态资源
- 如果DispactherServlet请求映射配置为/,则SpringMVC将捕获web容器的所有请求,包括静态爱资源的请求,SpringMVC会将它们当成一个普通请求处理,因找不到对应的处理器而报错。
- 所以,需要在SpringMVC的配置文件中,配置一个<mvc:default-servlet-handler/>的方式解决静态资源的问题。
<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
<context:component-scan base-package="com.xuweiwei.handlers"/>
<!-- 配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<mvc:view-controller path="/index" view-name="index"/>
<mvc:default-servlet-handler/>
<mvc:annotation-driven />
</beans>
- <mvc:default-servlet-handler/>的详解
- 该注解将在SpringMVC的上下文定义一个DefaultServletHttpRequestHandler,它会对进入DispatcherServlet的请求进行过滤,如果发现没有经过映射的请求,就将该请求交由WEB服务器默认的Servlet处理,如果不是静态资源的请求,就由SpringMVC默认的前端控制器继续处理。
- 一般web服务器默认的Servlet的名称都是default,如果使用web服务器默认的Servlet的名称不是default,则需要通过default-servlet-name显示配置。
8 数据转换&数据格式化&数据校验
8.1 SpringMVC的数据绑定流程
- SpringMVC将ServletRequest对象及目标方法的入参实例化对象传递给WebDataBinderFactory示例,以创建DataBinder实例对象。
- DataBinder调用装配在SpringMVC上下文中的ConversionService组件进行数据类型转换和数据格式化。将Servlet中的请求信心填充到入参对象中。
- 调用Validator组件对已经绑定了请求消息的入参对象进行数据合法性的校验,并最终生成数据绑定结果BingdingData对象
- SpringMVC抽取BindingResult中的入参对象和校验错误对象,将它们赋给处理方法的响应入参。
8.2 数据绑定流程
8.3 自定义类型转换器(全局类型转换)
- ConversionService是Spring类型转换体系的核心接口。
- 可以利用ConversionServiceFactoryBean在Spring的IOC容器中注册一个ConversionService,Spring将自动识别出IOC容器中的ConversionService,并在Bean属性及SpringMVC处理方法入参绑定等场合使用它进行数据的转换。
- 可以通过ConversionServiceFactoryBean的converters属性注册自定义的类型转换器。
package com.xuweiwei.handlers;
import org.springframework.core.convert.converter.Converter;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateConverter implements Converter<String, Date> {
@Override
public Date convert(String s) {
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
try {
return dateFormat.parse(s);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
<mvc:annotation-driven conversion-service="conversionService" />
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.xuweiwei.handlers.DateConverter"/>
</set>
</property>
</bean>
<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
<context:component-scan base-package="com.xuweiwei.handlers"/>
<!-- 配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<mvc:view-controller path="/index" view-name="index"/>
<mvc:default-servlet-handler/>
<mvc:annotation-driven conversion-service="conversionService" />
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.xuweiwei.handlers.DateConverter"/>
</set>
</property>
</bean>
</beans>
8.4 <mvc:annotation-driven/>配置
- <mvc:annotation-driven/>会自动注册RequestMappingHandlerMapping、RequestMappingHandlerAdapter和ExceptionHandlerExceptionResolver三个Bean。
- 还提供如下的支持:
- 支持ConversionService实例对表单参数进行类型转换。
- 支持使用@NUmberFormat注解和@DateTimeFormat注解完成数据类型的格式化。
- 支持使用@Valid注解对JavaBean实例进行JSR 303 校验。
- 支持使用@ResponseBody和@RequestBody注解
8.5 @InitBinder注解
- 由@InitBinder标识的方法,可以对WebDataBinder对象进行初始化。WebDataBinder是DataBinder的子类,用于完成由表单字段到JavaBean属性的绑定。
- 注意:
- @InitBinder方法不能有返回值,必须声明为void。
- @InitBinder方法的参数通常是WebDataBinder
package com.xuweiwei.handlers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class POJO {
@InitBinder
public void initBinder(WebDataBinder webDataBinder){
webDataBinder.setDisallowedFields("username");
}
@RequestMapping("/pojo")
public String handler(User user){
System.out.println(user);
return "success";
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/pojo" method="post">
username:<input type="text" name="username" value="admin"/><br/>
password:<input type="password" name="password" value="123456"/><br/>
brith:<input type="text" name="brith" value="2011-11-11"/>
<input type="submit" value="新增"/>
</form>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${user.username}<br/>
${user.password}<br/>
${user.brith}
</body>
</html>
- 你会发现${user.username}没有显示出来。
8.6 数据格式化(局部类型转换)
- 对属性对象的输入和输出进行格式化,从其本质上讲依然是属于“类型转换”的范畴。
- Spring在格式化模块中定义了一个实现ConversionService接口的FormattingConversionService实现类,该实现类扩展了GenericConversionService,因此它既具有类型转换的功能,又具有格式化的功能。
- FormattingConversionService有一个FormattingConversionServiceFactoryBean工厂类,后者用于在Spring上下文中构造前者。
- FormattingConversionServiceFactoryBean在内部已经注册类:
- NumberFormatAnnotationFormatterFactory:支持对数字类型的属性使用@NumberFormat注解
- JodaDateTimeFormatAnnotaionFormatterFactory:支持对日期类型的数据使用@DateTimeFormat注解。
- 装配了FormattingConversionServiceFactoryBean后,就可以在SpringMVC入参绑定和模型数据输出的时候使用注解驱动了。
- <mvc:annotation-drivern/>默认创建的ConversionService实例就是FormattingConversionServiceFactoryBean。
package com.xuweiwei.handlers;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
public class User {
private String username;
private String password;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date brith;
public Date getBrith() {
return brith;
}
public void setBrith(Date brith) {
this.brith = brith;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", brith=" + brith +
'}';
}
}
package com.xuweiwei.handlers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class POJO {
@InitBinder
public void initBinder(WebDataBinder webDataBinder){
webDataBinder.setDisallowedFields("username");
}
@RequestMapping("/pojo")
public String handler(User user){
System.out.println(user);
return "success";
}
}
8.7 数据校验
- JSR 303
- JSR303 是java为Bean合法性校验提供的标准框架,它已经包含在JavaEE6.0中。
- JSR 303通过在Bean属性上标注类似于@NotNull、@Max等标准的注解指定校验规则,并通过标准的验证接口对Bean进行校验。
- Hibernate Validator
- Hibernate Validator是JSR 303的一个参考实现,除支持所有标准的校验注解外,还支持如下的扩展注解
<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
<!-- 配置自动扫描的包 -->
<context:component-scan base-package="com.xuweiwei"></context:component-scan>
<!-- 配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<mvc:view-controller path="/input" view-name="input"/>
<mvc:default-servlet-handler/>
<mvc:annotation-driven ></mvc:annotation-driven>
</beans>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<jsp:forward page="input"/>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/emp" method="post">
lastName<input type="text" name="lastName"/>${lastName}<br/>
email<input type="text" name="email"/><br/>
gender:<input type="text" name="gender" value="0"/>男
<input type="text" name="gender" value="1"/>女<br/>
birth:<input type="birth" name="birth" value=""/><br/>
<input type="submit" value="新增"/>
</form>
</body>
</html>
package com.xuweiwei;
import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.Past;
import java.util.Date;
public class Employee {
private Integer id;
@NotEmpty
private String lastName;
@Email
private String email;
private Integer gender;
@Past
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birth;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Integer getGender() {
return gender;
}
public void setGender(Integer gender) {
this.gender = gender;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
public Employee() {
}
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", lastName='" + lastName + '\'' +
", email='" + email + '\'' +
", gender=" + gender +
", birth=" + birth +
'}';
}
}
package com.xuweiwei;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.validation.Valid;
import java.util.Map;
@Controller
public class EmployeeHandler {
@RequestMapping(value="/emp", method=RequestMethod.POST)
public String save(@Valid @ModelAttribute Employee employee, BindingResult result,
Map<String, Object> map){
System.out.println("save: " + employee);
if(result.getErrorCount() > 0){
for(FieldError error:result.getFieldErrors()){
map.put(error.getField(), error.getDefaultMessage());
System.out.println(error.getField() + ":" + error.getDefaultMessage());
}
return "input";
}
return "success";
}
}
- 提示消息的国际化
- 每个属性在数据绑定和数据校验发生错误的时候,都会生出一个对应的FieldError对象。
- 当一个属性校验失败后,校验框架会为该属性生成4个消息代码,这些代码以校验注解类名为前缀,结合ModelAttribute、属性名和属性类型名生成多个对应的消息代码
- 例如:Employee类中的lastName属性有一个NotEmpty注解,如果该注解不满足@NotEmpty所定义的规则的时候,就会产生如下的错误代码:
- NotEmpty.employee.lastName
- NotEmpty.lastName
- NotEmpty.java.lang.String
- NotEmpty
- 当使用SpringMVC标签显示错误消息的时候,SpringMVC会查看web上下文是否装配了对应的国际化消息,如果没有,就会显示默认的错误消息,如果有,你懂的。
- 如果数据类型转换或数据格式转换发生错误,或该有的参数不存在,或调用处理方法时发生错误,都会在隐含模型中创建错误消息,其中错误代码前缀说明如下:
- required:必要的参数不存在。
- typeMismatch:在数据绑定的时候,发生数据类型不匹配的问题。
- methodInvocation:Spring MVC在调用处理方法的时候发生错误。
- 在springmvc.xml需要配置如下信息
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!-- 配置 SpringMVC 的 DispatcherServlet -->
<!-- The front controller of this Spring Web application, responsible for handling all application requests -->
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Map all requests to the DispatcherServlet for handling -->
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 配置 HiddenHttpMethodFilter: 把 POST 请求转为 DELETE、PUT 请求 -->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<jsp:forward page="input"/>
</body>
</html>
<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
<!-- 配置自动扫描的包 -->
<context:component-scan base-package="com.xuweiwei"></context:component-scan>
<!-- 配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<mvc:view-controller path="/input" view-name="input"/>
<mvc:default-servlet-handler/>
<mvc:annotation-driven ></mvc:annotation-driven>
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="i18n"/>
</bean>
</beans>
NotEmpty.employee.lastName=用户名不能为空
NotEmpty.employee.email=邮箱地址不能为空
Email.employee.email=邮箱地址错误
package com.xuweiwei;
import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.Past;
import java.util.Date;
public class Employee {
private Integer id;
@NotEmpty
private String lastName;
@NotEmpty
@Email
private String email;
private Integer gender;
@Past(message = "需要一个比当前时间小的日期")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birth;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Integer getGender() {
return gender;
}
public void setGender(Integer gender) {
this.gender = gender;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
public Employee() {
}
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", lastName='" + lastName + '\'' +
", email='" + email + '\'' +
", gender=" + gender +
", birth=" + birth +
'}';
}
}
- 在WEB-INF下新建一个jsp目录,然后在此目录下新建一个input.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/emp" method="post">
lastName<input type="text" name="lastName" value="${employee.lastName}"/>${lastName}<br/>
email<input type="text" name="email" value="${employee.email}"/>${email}<br/>
gender:<input type="text" name="gender" value="0"/>男
<input type="text" name="gender" value="1"/>女<br/>
birth:<input type="birth" name="birth" value="${employee.birth}"/>${birth}<br/>
<input type="submit" value="新增"/>
</form>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${employee}
</body>
</html>
package com.xuweiwei;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.validation.Valid;
import java.util.Map;
@Controller
public class EmployeeHandler {
@RequestMapping(value="/emp", method=RequestMethod.POST)
public String save(@Valid @ModelAttribute Employee employee, BindingResult result,
Map<String, Object> map){
System.out.println("save: " + employee);
if(result.getErrorCount() > 0){
map.put("employee", employee);
for(FieldError error:result.getFieldErrors()){
map.put(error.getField(), error.getDefaultMessage());
System.out.println(error.getField() + ":" + error.getDefaultMessage());
}
return "input";
}
return "success";
}
}
9 处理JSON
- 步骤:
- ①加入jackson的jar包
- ②编写目标方法,使其返回JSON对象的对象或集合
- ③在方法上添加@ResponseBody注解
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
<script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery-1.9.1.min.js"></script>
<script type="text/javascript">
$.ajax({
url:'${pageContext.request.contextPath}/emps',
success:function (data) {
console.log(data)
}
})
</script>
</head>
<body>
</body>
</html>
package com.xuweiwei;
import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.Past;
import java.util.Date;
public class Employee {
private Integer id;
@NotEmpty
private String lastName;
@NotEmpty
@Email
private String email;
private Integer gender;
@Past(message = "需要一个比当前时间小的日期")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birth;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Integer getGender() {
return gender;
}
public void setGender(Integer gender) {
this.gender = gender;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
public Employee() {
}
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", lastName='" + lastName + '\'' +
", email='" + email + '\'' +
", gender=" + gender +
", birth=" + birth +
'}';
}
}
package com.xuweiwei;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
@Controller
public class EmployeeHandler {
@RequestMapping(value = "/emps")
@ResponseBody
public List<Employee> employeeLists(){
List<Employee> employees = new ArrayList<>();
Employee e1 = new Employee();
e1.setId(1);
e1.setLastName("哈哈");
e1.setGender(1);
e1.setEmail("abc@163.com");
e1.setBirth(new Date());
Employee e2 = new Employee();
e2.setId(2);
e2.setLastName("呵呵");
e2.setGender(2);
e2.setEmail("bcd@163.com");
e2.setBirth(new Date());
employees.add(e1);
employees.add(e2);
return employees;
}
}
- 【注意】
- HttpMessageConverter<T>时Spring3.0新添加的一个接口,主要负责将请求信息转换为一个对象,或将对象输出为响应信息。
- DispatcherServlet默认装配RequestmappingHandlerAdapter,而RequestMappingHandlerAdapter默认装配的就是HttpMessageConverter。
- 而加入jacksonjar包后,HttpMessageConverter的实现类就是MappingJackson2HttpMessageConverter。
10 文件上传
- SpringMVC为文件上传提供了直接的支持,这种支持是通过即插即用的MultipartResolver实现的。Spring用Commons FileUpload技术实现了一个MultipartResolver实现类:CommonsMultipartResolver。
- SpringMVC上下文中默认没有装配MultipartResolver,因此默认情况下不能处理文件的上传,如果想使用SpringMVC的上传功能,需要在上下文中配置MultipartResolver。
<bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="UTF-8"/>
<property name="maxUploadSize" value="2422880"/>
</bean>
11 使用拦截器
- SpringMVC可以使用拦截器对请求进行拦截处理,用户可以自定义拦截器来实现特定的功能,自定义的拦截器必须实现HandlerInterceptor接口。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.web.servlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface HandlerInterceptor {
boolean preHandle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;
void postHandle(HttpServletRequest var1, HttpServletResponse var2, Object var3, ModelAndView var4) throws Exception;
void afterCompletion(HttpServletRequest var1, HttpServletResponse var2, Object var3, Exception var4) throws Exception;
}
package com.xuweiwei;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class FirstInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
System.out.println("FirstInterceptor");
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
package com.xuweiwei;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class SecondInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
System.out.println("SecondInterceptor");
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
<mvc:interceptors>
<!-- 拦截所有的请求 -->
<bean class="com.xuweiwei.FirstInterceptor">
</bean>
<!-- 拦截指定的资源 -->
<mvc:interceptor>
<mvc:mapping path="/emps"/>
<bean class="com.xuweiwei.SecondInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
12 异常处理
12.1 ExceptionHandlerException
- SpringMVC通过HandlerExceptionResolver处理程序的异常,包括Handler的映射、数据绑定以及目标方法的执行时发生的异常。
- 项目中配置了<mvc:annotation-driven/>的话,就是配置了如下的异常视图解析器
- ExceptionHandlerExceptionResolver
- ResponseStatusExceptionResolver
- DefaultHandlerExceptionResolver
- ExceptionHandlerExceptionResolver
- 主要处理Handler中用@ExceptionHandler注解定义的方法
- @ExceptionHandler注解定义的方法有优先级的问题,例如:发生的时NullPointerException,但是声明的异常有RuntimeException和Exception,那么此时会根据异常的最近集成关系找到继承关系最浅的那个@ExceptionHandler注解方法,即标记了RuntimeException的方法
- 如果ExceptionHandlerExceptionResolver内部找不到@ExceptionHandler的注解的话,就会找@ControllerAdvice中的@ExceptionHandler的注解方法。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<a href="${pageContext.request.contextPath}/emps">emps</a>
</body>
</html>
package com.xuweiwei;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class EmployeeHandler {
@ExceptionHandler(value = {ArithmeticException.class})
public String handlerException(Exception e){
System.out.println(e.getMessage());
return "error";
}
@RequestMapping(value = "/emps")
public String employeeLists(){
int i = 10 /0 ;
return "success";
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
出错了
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<a href="${pageContext.request.contextPath}/emps">emps</a>
</body>
</html>
- ExceptionHandlerTest.java
package com.xuweiwei;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import javax.servlet.http.HttpServletRequest;
@ControllerAdvice
public class ExceptionHandlerTest {
@ExceptionHandler(value = {ArithmeticException.class})
public String handlerException(Exception e, HttpServletRequest request){
System.out.println(e.getMessage());
request.setAttribute("e", e);
return "error";
}
}
package com.xuweiwei;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class EmployeeHandler {
@RequestMapping(value = "/emps")
public String employeeLists(){
int i = 10 /0 ;
return "success";
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
出错了
</body>
</html>
12.2 ResponseStatusExceptionResolver
- 在异常及异常的父类中找到@ResponseStatus注解,然后使用这个注解的属性进行处理。
- 示例:
- 定义一个@ResponseStatus注解修改的异常类
package com.xuweiwei;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value = HttpStatus.FORBIDDEN,reason = "禁止访问")
public class UsernameAndPasswordException extends RuntimeException {
}
- 由于ExceptionHandlerExceptionResolver不解析上述异常,因为上述异常是由@ResponseStatus注解修改的异常,因此会被ResponseStatusExceptionResolver解析到,然后响应状态码给客户端。
package com.xuweiwei;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class EmployeeHandler {
@RequestMapping(value = "/emps")
public String employeeLists(){
if(true){
throw new UsernameAndPasswordException();
}
return "success";
}
}
package com.xuweiwei;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import javax.servlet.http.HttpServletRequest;
@ControllerAdvice
public class ExceptionHandlerTest {
@ExceptionHandler(value = {ArithmeticException.class})
public String handlerException(Exception e){
System.out.println(e.getMessage());
return "error";
}
}
12.3 SimpleMappingExceptionResolver
- 如果希望对所有的异常进行统一的处理,可以使用SimpleMappingExceptionResolver,它将异常类名映射为视图名,即发生异常时使用相应的视图报告异常。
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.Exception">
error
</prop>
</props>
</property>
</bean>
13 SpringMVC对比Struts2
- ①SpringMVC的入口是Servlet,而Struts2的入口是Filter。
- ②SpringMVC的速度比Struts2快些。SpringMVC是基于方法设计的,而Struts2是基于类设计的,每次请求都会创建一个Action实例。
- ③SpringMVC使用更加简洁,开发效率高些,并且支持JSR303,处理AJAX的请求更加方便些。