SpringMVC中的Interceptor拦截器也是相当重要和相当有用的,它的主要作用是拦截用户的请求并进行相应的处理。比如通过它来进行权限验证,或者是来判断用户是否登陆,或者是像12306那样子判断当前时间是否是购票时间。
一、定义Interceptor实现类
SpringMVC中的Interceptor拦截请求是通过HandlerInterceptor来实现的。在SpringMVC中定义一个Interceptor非常简单,主要有两种方式,第一种方式是要定义的Interceptor类要实现了Spring的HandlerInterceptor 接口,或者是这个类继承实现了HandlerInterceptor接口的类,比如Spring已经提供的实现了HandlerInterceptor接口的抽象类HandlerInterceptorAdapter ;第二种方式是实现Spring的WebRequestInterceptor接口,或者是继承实现了WebRequestInterceptor的类。
(一)实现HandlerInterceptor接口
HandlerInterceptor接口中定义了三个方法,我们就是通过这三个方法来对用户的请求进行拦截处理的。
(1)preHandle(HttpServletRequest request, HttpServletResponse response, Object handle)方法,顾名思义,该方法将在请求处理之前进行调用。SpringMVC中的Interceptor是链式的调用的,在一个应用中或者说是在一个请求中可以同时存在多个Interceptor。每个Interceptor的调用会依据它的声明顺序依次执行,而且最先执行的都是Interceptor中的preHandle方法,所以可以在这个方法中进行一些前置初始化操作或者是对当前请求的一个预处理,也可以在这个方法中进行一些判断来决定请求是否要继续进行下去。该方法的返回值是布尔值Boolean类型的,当它返回为false时,表示请求结束,后续的Interceptor和Controller都不会再执行;当返回值为true时就会继续调用下一个Interceptor的preHandle方法,如果已经是最后一个Interceptor的时候就会是调用当前请求的Controller方法。
(2)postHandle(HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView)方法,由preHandle方法的解释我们知道这个方法包括后面要说到的afterCompletion方法都只能是在当前所属的Interceptor的preHandle方法的返回值为true时才能被调用。postHandle方法,顾名思义就是在当前请求进行处理之后,也就是Controller方法调用之后执行,但是它会在DispatcherServlet进行视图返回渲染之前被调用,所以我们可以在这个方法中对Controller处理之后的ModelAndView对象进行操作。postHandle方法被调用的方向跟preHandle是相反的,也就是说先声明的Interceptor的postHandle方法反而会后执行,这和Struts2里面的Interceptor的执行过程有点类型。Struts2里面的Interceptor的执行过程也是链式的,只是在Struts2里面需要手动调用ActionInvocation的invoke方法来触发对下一个Interceptor或者是Action的调用,然后每一个Interceptor中在invoke方法调用之前的内容都是按照声明顺序执行的,而invoke方法之后的内容就是反向的。
(3)afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex)方法,该方法也是需要当前对应的Interceptor的preHandle方法的返回值为true时才会执行。顾名思义,该方法将在整个请求结束之后,也就是在DispatcherServlet渲染了对应的视图之后执行。这个方法的主要作用是用于进行资源清理工作的。
(二)实现WebRequestInterceptor接口
WebRequestInterceptor中也定义了三个方法,我们也是通过这三个方法来实现拦截的。这三个方法都传递了同一个参数WebRequest,那么这个WebRequest是什么呢?这个WebRequest是Spring定义的一个接口,它里面的方法定义都基本跟HttpServletRequest一样,在WebRequestInterceptor中对WebRequest进行的所有操作都将同步到HttpServletRequest中,然后在当前请求中一直传递。
(1)preHandle(WebRequestrequest)方法。该方法将在请求处理之前进行调用,也就是说会在Controller方法调用之前被调用。这个方法跟HandlerInterceptor中的preHandle是不同的,主要区别在于该方法的返回值是void,也就是没有返回值,所以我们一般主要用它来进行资源的准备工作,比如我们在使用Hibernate的时候可以在这个方法中准备一个Hibernate的Session对象,然后利用WebRequest的setAttribute(name, value, scope)把它放到WebRequest的属性中。这里可以说说这个setAttribute方法的第三个参数scope,该参数是一个Integer类型的。在WebRequest的父层接口RequestAttributes中对它定义了三个常量:
SCOPE_REQUEST:它的值是0,代表只有在request中可以访问。
SCOPE_SESSION:它的值是1,如果环境允许的话它代表的是一个局部的隔离的session,否则就代表普通的session,并且在该session范围内可以访问。
SCOPE_GLOBAL_SESSION:它的值是2,如果环境允许的话,它代表的是一个全局共享的session,否则就代表普通的session,并且在该session范围内可以访问。
(2)postHandle(WebRequestrequest, ModelMap model)方法。该方法将在请求处理之后,也就是在Controller方法调用之后被调用,但是会在视图返回被渲染之前被调用,所以可以在这个方法里面通过改变数据模型ModelMap来改变数据的展示。该方法有两个参数,WebRequest对象是用于传递整个请求数据的,比如在preHandle中准备的数据都可以通过WebRequest来传递和访问;ModelMap就是Controller处理之后返回的Model对象,我们可以通过改变它的属性来改变返回的Model模型。
(3)afterCompletion(WebRequestrequest, Exception ex)方法。该方法会在整个请求处理完成,也就是在视图返回并被渲染之后执行。所以在该方法中可以进行资源的释放操作。而WebRequest参数就可以把我们在preHandle中准备的资源传递到这里进行释放。Exception参数表示的是当前请求的异常对象,如果在Controller中抛出的异常已经被Spring的异常处理器给处理了的话,那么这个异常对象就是是null。
二、拦截器示例:
MyInterceptor.java
1 package com.tgb.interceptor; 2 3 import java.util.List; 4 5 import javax.servlet.http.HttpServletRequest; 6 import javax.servlet.http.HttpServletResponse; 7 8 9 import org.springframework.web.servlet.HandlerInterceptor; 10 import org.springframework.web.servlet.ModelAndView; 11 12 import com.tgb.entity.Student; 13 /** 14 * 自定义springmvc的拦截器 15 * @ClassName: MyInterceptor 16 * @Description: TODO(这里用一句话描述这个类的作用) 17 * @author 尚晓飞 18 * @date 2014-11-3 上午11:07:07 19 * 20 */ 21 public class MyInterceptor implements HandlerInterceptor{ 22 23 private List<String> allowsUrl;//可以再xml配置的时候进行赋值 24 25 // afterCompletion()方法在DispatcherServlet完全处理完请求后被调用 26 //afterCompletion():这个方法在DispatcherServlet完全处理完请求后被调用, 27 //可以在该方法中进行一些资源清理的操作。 28 @Override 29 public void afterCompletion(HttpServletRequest arg0, 30 HttpServletResponse arg1, Object arg2, Exception arg3) 31 throws Exception { 32 System.out.println("MyInterceptor.afterCompletion(DispatcherServlet完全处理完请求后被调用)"); 33 34 35 } 36 37 //业务处理请求之后被调用(preHandle此方法返回false,该方法不执行) 38 //postHandle():这个方法在业务处理器处理完请求后,被调用。该方法执行是,jsp等视图还没有渲染。 39 //在该方法中对用户请求request进行处理。 40 @Override 41 public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, 42 Object arg2, ModelAndView arg3) throws Exception { 43 System.out.println("MyInterceptor.postHandle(业务处理请求之后被调用)"); 44 45 } 46 47 //业务处理请求之前被调用 48 //preHandle():这个方法在业务处理器处理请求之前被调用,在该方法中对用户请求request进行处理。 49 //如果程序员决定该拦截器对请求进行拦截处理后还要调用其他的拦截器,或者是业务处理器去进行处理,则返回true; 50 //如果程序员决定不需要再调用其他的组件去处理请求,则返回false。 51 @Override 52 public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, 53 Object arg2) throws Exception { 54 String uri=arg0.getRequestURI(); 55 String url=arg0.getRequestURL().toString(); 56 System.out.println("MyInterceptor.preHandle(业务处理请求之前被调用)"); 57 System.out.println("MyInterceptor.preHandle(第一个拦截器:uri)"+uri); 58 System.out.println("MyInterceptor.preHandle(第一个拦截器:url)"+url); 59 60 //进行拦截的地址,可以对url进行重定向 61 //获取session 62 Student student=(Student) arg0.getSession().getAttribute("student"); 63 if(student==null){ 64 //用户未登录或者正在登录。但拦截器已经过滤了登录url的请求。因此为虽已经登录,但session死亡 65 arg0.getSession().setAttribute("msg", "长时间未操作,请重新登录"); 66 //重定向 67 arg1.sendRedirect("/test_ssh/login.jsp"); 68 //请求转发 69 //arg0.getRequestDispatcher("/test_ssh/login.jsp").forward(arg0, arg1); 70 71 } 72 return true; 73 } 74 75 public List<String> getAllowsUrl() { 76 return allowsUrl; 77 } 78 79 public void setAllowsUrl(List<String> allowsUrl) { 80 this.allowsUrl = allowsUrl; 81 } 82 83 84 85 }
拦截器的配置。在配置控制器的xml中配置。spring-mvc.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xmlns:mvc="http://www.springframework.org/schema/mvc" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans 7 http://www.springframework.org/schema/beans/spring-beans.xsd 8 http://www.springframework.org/schema/context 9 http://www.springframework.org/schema/context/spring-context-3.2.xsd 10 http://www.springframework.org/schema/mvc 11 http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd"> 12 13 <!--此xml是springMvc是启动控制器和视图解析工具的 --> 14 15 <!-- 注解扫描包 pojo处定义@Controller,此配置扫描相应的类包,将有注解的pojo变成一个可以分发http请求的控制器--> 16 <!-- @Controller将pojo变成可以分发http请求的控制器。 --> 17 <!-- @RequestMapping确定不同请求,调用不同控制器中的某个控制器,然后调用某个控制器中的某个方法 --> 18 <!-- 类头定义@RequestMapping是提供初步的请求映射信息(相对于项目部署),方法头定义@RequestMapping(相对于类头部署)是确定请求所要调用的方法。请求规则包含:url,请求参数,请求方法,请求头 --> 19 <context:component-scan base-package="com.tgb.web" /> 20 21 <!-- <mvc:annotation-driven /> 是一种简写形式,完全可以手动配置替代这种简写形式,简写形式可以让初学都快速应用默认配置方案。<mvc:annotation-driven /> 22 会自动注册DefaultAnnotationHandlerMapping与AnnotationMethodHandlerAdapter 两个bean,是spring MVC为@Controllers分发请求所必须的。 23 并提供了:数据绑定支持,@NumberFormatannotation支持,@DateTimeFormat支持,@Valid支持,读写XML的支持(JAXB),读写JSON的支持(Jackson)。 24 后面,我们处理响应ajax请求时,就使用到了对json的支持。后面,对action写JUnit单元测试时,要从spring IOC容器中取DefaultAnnotationHandlerMapping 25 与AnnotationMethodHandlerAdapter 两个bean,来完成测试,取的时候要知道是<mvc:annotation-driven />这一句注册的这两个bean。--> 26 <mvc:annotation-driven /> 27 28 29 <!-- 静态资源(js/image)的访问 --> 30 <mvc:resources location="/js/" mapping="/js/**"/> 31 32 33 <!-- 定义视图解析器 --> 34 <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> 35 <property name="prefix" value="/"></property> 36 <property name="suffix" value=".jsp"></property> 37 </bean> 38 39 <!-- 配置springMVC拦截器 。内部可以配置多个拦截器。一个拦截器是一个<mvc:interceptor></mvc:interceptor>节点。执行顺序,根据配置的上下顺序--> 40 <mvc:interceptors> 41 <!-- 第一个拦截器 --> 42 <mvc:interceptor> 43 <!-- 凡是请求路径符合/*/*都被该拦截器拦截 --> 44 <mvc:mapping path="/*/*"/> 45 <!-- 可以指定对某些请求地址不进行拦截 --> 46 <mvc:exclude-mapping path="/student/findAll"/> 47 <!-- 自定义拦截器的java类。 --> 48 <bean class="com.tgb.interceptor.MyInterceptor"> 49 <property name="allowsUrl"><!-- 自定义拦截器类中的一个list属性。以下为该list属性赋值 。此处做判断,表示不拦截的url--> 50 <list> 51 <value>/js</value> 52 <value>/css</value> 53 <value>/images</value> 54 </list> 55 </property> 56 </bean> 57 </mvc:interceptor> 58 </mvc:interceptors> 59 </beans>