Spring MVC (JDK8+Tomcat8)
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中配置前端控制器
- 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文件
- 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作为控制器
- 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文件
- 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文件中编写如下内容
- 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等。
- GET请求:
@RequestMapping(value = "/hello",method = RequestMethod.GET) public String testGet(){ System.out.println("GET"); return "success"; }
- POST请求:
@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>
- 示例:REST风格的GET方法
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
- 示例:REST风格的POST方法
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>
- 示例:REST风格的PUT方法
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"; } }
- 示例:REST风格的DELETE方法
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) {}
- 返回ModelAndView对象
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 视图
- SpringMVC会按照如下的步骤去解析视图
- 视图和视图解析器
- 请求处理方法执行完成后,最终返回一个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的一个参考实现,除支持所有标准的校验注解外,还支持如下的扩展注解
- 示例:
- springmvc.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"></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>
- index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>$Title$</title> </head> <body> <jsp:forward page="input"/> </body> </html>
- 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"/>${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>
- Employee.java
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 + '}'; } }
- EmployeeHandler.java
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
- 例如:Employee类中的lastName属性有一个NotEmpty注解,如果该注解不满足@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>
- 示例:
- index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>$Title$</title> </head> <body> <jsp:forward page="input"/> </body> </html>
- springmvc.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"></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>
- i18n.properties
NotEmpty.employee.lastName=用户名不能为空 NotEmpty.employee.email=邮箱地址不能为空 Email.employee.email=邮箱地址错误
- Employee.java
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>
- 同上,新建一个success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> ${employee} </body> </html>
- EmployeeHandler.java
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注解
- 示例:
- index.jsp
<%@ 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>
- Employee.java
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 + '}'; } }
- EmployeeHandler.java
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。
- 配置springmvc.xml
<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的注解方法。
- 示例:
- index.jsp
<%@ 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>
- ExceptionHandler.java
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"; } }
- error.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> 出错了 </body> </html>
- 示例:
- index.jsp
<%@ 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"; } }
- EmployeeHandler.java
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"; } }
- error.jsp
<%@ 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的请求更加方便些。
Spring MVC (JDK8+Tomcat8)的更多相关文章
- spring MVC (学习笔记)
web.xml 相关配置 <?xml version="1.0" encoding="UTF-8"?><web-app xmlns=" ...
- Spring MVC(七篇)
(一)Spring MVC简介 (二)SpringMVC核心控制器 (三)Spring MVC Controller接口控制器详解(一) (三)Spring MVC Controller接口控制器详解 ...
- spring mvc(前置控制器)(转载)
(此文转载:http://www.cnblogs.com/brolanda/p/4265749.html) 一.前置控制器配置与讲解 上篇中理解了IOC容器的初始化时机,并理解了webApplicat ...
- Spring7:基于注解的Spring MVC(下篇)
Model 上一篇文章<Spring6:基于注解的Spring MVC(上篇)>,讲了Spring MVC环境搭建.@RequestMapping以及参数绑定,这是Spring MVC中最 ...
- 浅谈spring——spring MVC(十一)
springMVC框架主要是围绕DispatcherServlet这个核心展开,它负责拦截请求并将其分派给相应的的处理器处理,然后将结果响应给用户.包括注解驱动控制器.请求及响应信息处理.视图解析.本 ...
- Spring MVC(一)Servlet 2.x 规范在 Spring MVC 中的应用
Spring MVC(一)Servlet 2.x 规范在 Spring MVC 中的应用 Spring 系列目录(https://www.cnblogs.com/binarylei/p/1019869 ...
- 通用后台管理系统(ExtJS 4.2 + Spring MVC 3.2 + Hibernate)
通用后台管理系统(ExtJS 4.2 +Spring MVC 3.2 + Hibernate) 开发语言JAVA 成品成品 前端技术extjs 数据库mysql,sql server,oracle 系 ...
- 调试SPRING MVC(或者整合SSH)的时候遇到了org/objectweb/asm/Type
调试SPRING MVC(或者整合SSH)的时候遇到了org/objectweb/asm/Type 解决方法1: 原因是Spring中的cglib-nodep-2.x.x.jar与Hibernate中 ...
- mybatis与spring整合(基于配置文件)
本文主要介绍了如何将mybatis和spring整合在一起使用,本人使用的是mybatis3.05 + spring3.1.0M2 ,使用dbcp作为数据库连接池. 1.编写数据访问接口(UserDa ...
随机推荐
- 模拟退火算法实例(c++ 与 c# 实现)
此片文章主要参考CSDN博主里头的一篇文章, 将自己的理解写下来,以方便后期的查阅. 一.C++ 实现 1. 已知平面上若干点坐标(xi, yi), 求平面上一点p(x, y) , 到这些点的总距离最 ...
- file中private_data
这个是Linux下连接VFS文件系统框架和不同文件/文件系统底层实现之间的一个核心数据结构,虽然它只是一个指针,但是一个指针可以解决所有问题,有了它,妈妈再也不用担心我的学习.我们回想一下用户态线程的 ...
- uboot引导linux内核过程详解【转】
http://blog.chinaunix.net/uid-7828352-id-4472376.html 写的不错,尤其是uboot向linux内核传递参数的过程写的比较详细.
- 关于python使用threadpool中的函数单个参数和多个参数用法举例
1.对单个元素的函数使用线程池: # encoding:utf-8 __author__='xijun.gong' import threadpool def func(name): print 'h ...
- 开发高性能JAVA应用程序基础(内存篇)
虽然Java的垃圾回收和当前高配置的服务器可以让程序员大部分时间忘掉OutOfMemoryError的存在,但是访问量增大后频繁的GC会额外消耗CPU (使用top查看结果为us值高),系统响应速度下 ...
- BZOJ1758: [Wc2010]重建计划(01分数规划+点分治+单调队列)
题目:http://www.lydsy.com/JudgeOnline/problem.php?id=1758 01分数规划,所以我们对每个重心进行二分.于是问题转化为Σw[e]-mid>=0, ...
- Ultra-QuickSort(树状数组求逆序对数)
Ultra-QuickSort 题目链接:http://poj.org/problem?id=2299 Time Limit: 7000MS Memory Limit: 65536K Total ...
- linux 内核提权
不经意间找到了大牛总结的一些Linux提权exp 我直接借花献佛分享给大家 #CVE #Description #Kernels CVE-2017-1000367 [Sudo] (Sudo 1.8.6 ...
- TI-RTOS 之 PWM
TI-RTOS 之 PWM CC1310 有4个定时器,8个PWM通道,在TI-RTOS它的驱动是写好的,引用时需要包含 PWM.h头文件即可. 一般是任务主体之前,或者主函数进行初始化. Board ...
- 布隆(Bloom)过滤器 JAVA实现
前言 Bloom过滤器,通过将字符串映射为信息指纹从而节省了空间.Bloom过滤器的原理为,将一个字符串通过一定算法映射为八个Hash值,将八个Hash值对应位置的Bitset位进行填充.在进行校验的 ...