一、SpringMVC拦截器简介
Spring MVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。在springmvc中,定义拦截器要实现HandlerInterceptor接口
二、SpringMVC拦截器方法
- preHandle
预处理回调方法,实现处理器的预处理(如登录检查),第三个参数为响应的处理器(如具体的Controller实现);
返回值:true表示继续流程(如调用下一个拦截器或处理器);false表示流程中断(如登录检查失败),不会继续调用其他的拦截器或处理器,此时我们需要通过response来产生响应;
- postHandle
后处理回调方法,实现处理器的后处理(但在渲染视图之前),此时我们可以通过modelAndView(模型和视图对象)对模型数据进行处理或对视图进行处理,modelAndView也可能为null
- afterCompletion
整个请求处理完毕回调方法,即在视图渲染完毕时回调,如性能监控中我们可以在此记录结束时间并输出消耗时间,还可以进行一些资源清理,类似于try-catch-finally中的finally,
但仅调用处理器执行链中preHandle返回true的拦截器的afterCompletion
三、SpringMVC拦截器实例
1)创建工程
Eclipse下新建一个web项目,File>New>Dynamic Web Project
2)配置web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <display-name>springmvc_6_interceptor</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <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:spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springDispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
3)添加首页面
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <form action="springmvc/testFileUpload" method="POST" enctype="multipart/form-data"> File: <input type="file" name="file" /><br> <br> Desc: <input type="text" name="desc" /> <input type="submit" value="Submit" /> </form> <br> <br> </body> </html>
4)添加控制器Controller
package com.yyx.handlers; import java.io.IOException; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.multipart.MultipartFile; @RequestMapping("/springmvc") @Controller /** * * @author yyx 2018年7月19日 */ public class SpringMVCTest { private static final String SUCCESS = "success"; /** * 常用:单个文件上传 * 注意:需要在配置文件配置支持文件上传的bean * @param desc * @param file * @return */ @RequestMapping("/testFileUpload") public String testFileUpload( @RequestParam(value = "desc", required = false) String desc, @RequestParam("file") MultipartFile file) { System.out.println("desc:" + desc); System.out.println("OriginalFilename:" + file.getOriginalFilename()); try { System.out.println("InputStream:" + file.getInputStream()); } catch (IOException e) { e.printStackTrace(); } return SUCCESS; } }
5)添加拦截器
package com.yyx.interceptors; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; /** * 拦截器一 * @author yyx 2018年7月19日 */ public class FirstInterceptor implements HandlerInterceptor { /** * 渲染视图之后被调用. 释放资源 */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object obj, Exception ex) throws Exception { System.out.println("[FirstInterceptor] afterCompletion"); } /** * 调用目标方法之后, 但渲染视图之前. 可以对请求域中的属性或视图做出修改. */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object obj, ModelAndView mv) throws Exception { System.out.println("[FirstInterceptor] postHandle"); } /** * 该方法在目标方法之前被调用. 若返回值为 true, 则继续调用后续的拦截器和目标方法. 若返回值为 false, * 则不会再调用后续的拦截器和目标方法. * * 可以考虑做权限. 日志, 事务等. */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object obj) throws Exception { System.out.println("[FirstInterceptor] preHandle"); return true; } }
package com.yyx.interceptors; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; /** * 拦截器二 * @author yyx 2018年7月19日 */ public class SecondInterceptor implements HandlerInterceptor { /** * 渲染视图之后被调用. 释放资源 */ @Override public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception { System.out.println("[SecondInterceptor] afterCompletion"); } /** * 调用目标方法之后, 但渲染视图之前. 可以对请求域中的属性或视图做出修改. */ @Override public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception { System.out.println("[SecondInterceptor] postHandle"); } /** * 该方法在目标方法之前被调用. 若返回值为 true, 则继续调用后续的拦截器和目标方法. 若返回值为 false, * 则不会再调用后续的拦截器和目标方法. * * 可以考虑做权限. 日志, 事务等. */ @Override public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception { System.out.println("[SecondInterceptor] preHandle"); return true; } }
6)配置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:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <mvc:annotation-driven /> <!-- 配置自动扫描的包 --> <context:component-scan base-package="com.yyx.handlers"></context:component-scan> <!-- 配置视图解析器 :如何把 handlers 方法返回值解析为实际的物理视图 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/views/"></property> <property name="suffix" value=".jsp"></property> </bean> <!-- 配置拦截器 --> <mvc:interceptors> <!-- 配置自定义的拦截器 --> <!-- <bean class="com.yyx.interceptors.FirstInterceptor"></bean> --> <!-- 配置拦截器(不)作用的路径 --> <mvc:interceptor> <mvc:mapping path="/springmvc/testFileUpload" /> <!-- 拦截所有的请求,这个必须写在前面,也就是写在【不拦截】的上面 --> <!-- <mvc:mapping path="/**"/> --> <!-- 但是排除下面这些,也就是不拦截请求 --> <!-- <mvc:exclude-mapping path="/springmvc/testFileUpload"/> --> <bean class="com.yyx.interceptors.SecondInterceptor"></bean> </mvc:interceptor> </mvc:interceptors> <!-- 配置 MultipartResolver 文件上传 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="defaultEncoding" value="UTF-8"></property> <property name="maxUploadSize" value="1024000"></property> </bean> </beans>
7)添加目标页面
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h2>Success Page</h2>
</body>
</html>
常见应用场景
- 日志记录:记录请求信息的日志,以便进行信息监控、信息统计、计算PV(Page View)等
- 权限检查:如登录检测,进入处理器检测检测是否登录,如果没有直接返回到登录页面
- 性能监控:有时候系统在某段时间莫名其妙的慢,可以通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间(如果有反向代理,如apache可以自动记录)
- 通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取Locale、Theme信息等,只要是多个处理器都需要的即可使用拦截器实现
- OpenSessionInView:如Hibernate,在进入处理器打开Session,在完成后关闭Session