Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3223883
  • 博文数量: 269
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1988
  • 用 户 组: 普通用户
  • 注册时间: 2019-10-28 13:40
文章分类

全部博文(269)

文章存档

2022年(42)

2021年(155)

2020年(50)

2019年(22)

我的朋友

分类: Java

2019-11-05 11:35:18

一、使用场景

SpringMVC 中的Interceptor 拦截器也是相当重要和相当有用的,它的主要作用是拦截用户的请求并进行相应的处理。比如通过它来进行权限验证,或者是来判断用户是否登陆,或者是像12306 那样子判断当前时间是否是购票时间。我在项目里面主要是统一对http请求进行打日志。

二、Interceptor介绍

SpringMVC拦截器(Interceptor)实现对每一个请求处理前后进行相关的业务处理,类似与servlet中的Filter

SpringMVC 中的Interceptor 拦截请求是通过HandlerInterceptor来实现的。

SpringMVC中定义一个Interceptor非常简单,主要有4种方式:

1)实现SpringHandlerInterceptor接口;

2)继承实现了HandlerInterceptor接口的类,比如Spring 已经提供的实现了HandlerInterceptor 接口的抽象类HandlerInterceptorAdapter

3)实现SpringWebRequestInterceptor接口;

4)继承实现了WebRequestInterceptor的类;

实现了拦截器之后,我们可以通过重写WebMvcConfigurerAdapteraddInterceptors方法来注册自己的拦截器。

我们这里只通过实现HandlerInterceptor接口的方式给出实例。实例中使用拦截器实现两个功能

1)计算每一次请求的处理时间;

2)并对特定时间和特定用户(数据在codis)的请求进行拒绝;

环境:jdk 1.8 spring-webmvc 5.1.5.RELEASE

SpringMVC 中定义一个Interceptor是比较非常简单,主要有两种方式:

第一种:实现HandlerInterceptor 接口,或者是继承实现了HandlerInterceptor 接口的类,例如HandlerInterceptorAdapter

第二种:实现SpringWebRequestInterceptor接口,或者是继承实现了WebRequestInterceptor的类。

现在主要结合一个例子说一下第一种方式:实现HandlerInterceptor接口。

三、实现HandlerInterceptor接口

HandlerInterceptor接口主要定义了三个方法:

boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handle)方法:该方法将在请求处理之前进行调用,只有该方法返回true,才会继续执行后续的InterceptorController,当返回值为true时就会继续下一个InterceptorpreHandle方法,如果已经是最后一个Interceptor的时候就会是调用当前请求的Controller方法;

void postHandle (HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView)方法:该方法将在请求处理之后,DispatcherServlet进行视图返回渲染之前进行调用,可以在这个方法中对Controller 处理之后的ModelAndView 对象进行操作。

void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex)方法:该方法也是需要当前对应的InterceptorpreHandle方法的返回值为true时才会执行,该方法将在整个请求结束之后,也就是在DispatcherServlet 渲染了对应的视图之后执行。用于进行资源清理。

下面是一个简单的代码说明:

import javax.servlet.http.HttpServletRequest;  

import javax.servlet.http.HttpServletResponse;    

import org.springframework.web.servlet.HandlerInterceptor;  

import org.springframework.web.servlet.ModelAndView;    

public class SpringMVCInterceptor implements HandlerInterceptor {  

    /**

     * preHandle方法是进行处理器拦截用的,顾名思义,该方法将在Controller处理之前进行调用,SpringMVC中的Interceptor拦截器是链式的,可以同时存在

     * 多个Interceptor,然后SpringMVC会根据声明的前后顺序一个接一个的执行,而且所有的Interceptor中的preHandle方法都会在

     * Controller方法调用之前调用。SpringMVC的这种Interceptor链式结构也是可以进行中断的,这种中断方式是令preHandle的返

     * 回值为false,当preHandle的返回值为false的时候整个请求就结束了。

     */  

    @Override  

    public boolean preHandle(HttpServletRequest request,  

            HttpServletResponse response, Object handler) throws Exception {  

        // TODO Auto-generated method stub  

        return false;  

    }  

    /**

     * 这个方法只会在当前这个InterceptorpreHandle方法返回值为true的时候才会执行。postHandle是进行处理器拦截用的,它的执行时间是在处理器进行处理之

     * 后,也就是在Controller的方法调用之后执行,但是它会在DispatcherServlet进行视图的渲染之前执行,也就是说在这个方法中你可以对ModelAndView进行操

     * 作。这个方法的链式结构跟正常访问的方向是相反的,也就是说先声明的Interceptor拦截器该方法反而会后调用,这跟Struts2里面的拦截器的执行过程有点像,

     * 只是Struts2里面的intercept方法中要手动的调用ActionInvocationinvoke方法,Struts2中调用ActionInvocationinvoke方法就是调用下一个Interceptor

     * 或者是调用action,然后要在Interceptor之前调用的内容都写在调用invoke之前,要在Interceptor之后调用的内容都写在调用invoke方法之后。

     */  

    @Override  

    public void postHandle(HttpServletRequest request,  

            HttpServletResponse response, Object handler,  

            ModelAndView modelAndView) throws Exception {  

        // TODO Auto-generated method stub  

    }  

    /**

     * 该方法也是需要当前对应的InterceptorpreHandle方法的返回值为true时才会执行。该方法将在整个请求完成之后,也就是DispatcherServlet渲染了视图执行,

     * 这个方法的主要作用是用于清理资源的,当然这个方法也只能在当前这个InterceptorpreHandle方法的返回值为true时才会执行。

     */  

    @Override  

    public void afterCompletion(HttpServletRequest request,  

            HttpServletResponse response, Object handler, Exception ex)  

    throws Exception {  

        // TODO Auto-generated method stub  

       }  

   

}  

四、实现WebRequestInterceptor 接口

WebRequestInterceptor 中也定义了三个方法,我们也是通过这三个方法来实现拦截的。这三个方法都传递了同一个参数WebRequest ,那么这个WebRequest 是什么呢?这个WebRequest Spring 定义的一个接口,它里面的方法定义都基本跟HttpServletRequest 一样,在WebRequestInterceptor 中对WebRequest 进行的所有操作都将同步到HttpServletRequest 中,然后在当前请求中一直传递。

preHandle(WebRequest request) 方法。该方法将在请求处理之前进行调用,也就是说会在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 范围内可以访问。

postHandle(WebRequest request, ModelMap model) 方法。该方法将在请求处理之后,也就是在Controller 方法调用之后被调用,但是会在视图返回被渲染之前被调用,所以可以在这个方法里面通过改变数据模型ModelMap 来改变数据的展示。该方法有两个参数,WebRequest 对象是用于传递整个请求数据的,比如在preHandle 中准备的数据都可以通过WebRequest 来传递和访问;ModelMap 就是Controller 处理之后返回的Model 对象,我们可以通过改变它的属性来改变返回的Model 模型。

afterCompletion(WebRequest request, Exception ex) 方法。该方法会在整个请求处理完成,也就是在视图返回并被渲染之后执行。所以在该方法中可以进行资源的释放操作。而WebRequest 参数就可以把我们在preHandle 中准备的资源传递到这里进行释放。Exception 参数表示的是当前请求的异常对象,如果在Controller 中抛出的异常已经被Spring 的异常处理器给处理了的话,那么这个异常对象就是是null

下面是一个简单的代码说明:

import org.springframework.ui.ModelMap;  

import org.springframework.web.context.request.WebRequest;  

import org.springframework.web.context.request.WebRequestInterceptor;  

  

public class AllInterceptor implements WebRequestInterceptor {  

          /**

     * 在请求处理之前执行,该方法主要是用于准备资源数据的,然后可以把它们当做请求属性放到WebRequest

     */  

    @Override  

    public void preHandle(WebRequest request) throws Exception {  

        // TODO Auto-generated method stub  

        System.out.println("AllInterceptor...............................");  

        request.setAttribute("request", "request", WebRequest.SCOPE_REQUEST);//这个是放到request范围内的,所以只能在当前请求中的request中获取到  

        request.setAttribute("session", "session", WebRequest.SCOPE_SESSION);//这个是放到session范围内的,如果环境允许的话它只能在局部的隔离的会话中访问,否则就是在普通的当前会话中可以访问  

        request.setAttribute("globalSession", "globalSession", WebRequest.SCOPE_GLOBAL_SESSION);//如果环境允许的话,它能在全局共享的会话中访问,否则就是在普通的当前会话中访问  

    }  

    /**

     * 该方法将在Controller执行之后,返回视图之前执行,ModelMap表示请求Controller处理之后返回的Model对象,所以可以在

     * 这个方法中修改ModelMap的属性,从而达到改变返回的模型的效果。

     */  

    @Override  

    public void postHandle(WebRequest request, ModelMap map) throws Exception {  

        // TODO Auto-generated method stub  

        for (String key:map.keySet())  

            System.out.println(key + "-------------------------");;  

        map.put("name3", "value3");  

        map.put("name1", "name1");  

    }  

    /**

     * 该方法将在整个请求完成之后,也就是说在视图渲染之后进行调用,主要用于进行一些资源的释放

     */  

    @Override  

    public void afterCompletion(WebRequest request, Exception exception)  

    throws Exception {  

        // TODO Auto-generated method stub  

        System.out.println(exception + "-=-=--=--=-=-=-=-=-=-=-=-==-=--=-=-=-=");  

    }  

      }  

五、把定义的拦截器类加到SpringMVC的拦截体系中

5.1 SpringMVC的配置文件中加上支持MVCschema

Xml代码

   xmlns:mvc=""  

    xsi:schemaLocation="  

        /spring-mvc--4.1.xsd"  

下面是我的声明示例:

       xmlns:xsi=""

       xmlns:context=""

       xmlns:mvc=""

       xsi:schemaLocation="

/spring-beans-4.1.xsd

/spring-context-4.1.xsd

/spring-mvc-4.1.xsd">

这样在SpringMVC的配置文件中就可以使用mvc标签了,mvc标签中有一个mvc:interceptors是用于声明SpringMVC的拦截器的。

5.2 使用mvc:interceptors标签来声明需要加入到SpringMVC拦截器链中的拦截器

Xml代码

 

      

      

      

          

          

          

      

 

由上面的示例可以看出可以利用mvc:interceptors标签声明一系列的拦截器,然后它们就可以形成一个拦截器链,拦截器的执行顺序是按声明的先后顺序执行的,先声明的拦截器中的preHandle方法会先执行,然而它的postHandle方法和afterCompletion方法却会后执行。

mvc:interceptors标签下声明interceptor主要有两种方式:

1)直接定义一个Interceptor实现类的bean对象。使用这种方式声明的Interceptor拦截器将会对所有的请求进行拦截。

2)使用mvc:interceptor标签进行声明。使用这种方式进行声明的Interceptor可以通过mvc:mapping子标签来定义需要进行拦截的请求路径。

经过上述两步之后,定义的拦截器就会发生作用对特定的请求进行拦截了。

六、基于Spring4.1.0补充:

不知道从哪个版本开始,Spring MVCmvc命名空间开始支持exclude-mapping。反正笔者最开始写这篇博文的时候是基于Spring3.1.0所写,那时候的mvc命名空间下是没有execlude-mapping定义的。但是笔者现在使用的Spring4.1.0是有exclude-mapping定义的。所以基于该版本补充Spring MVC拦截器的exclude-mapping的用法。mapping只能映射某些需要拦截的请求,而exclude-mapping用来排除某些特定的请求映射。当我们需要拦截的请求映射是比较通用的,但是其中又包含了某个特殊的请求是不需要使用该拦截器的时候我们就可以把它定义为exclude-mapping了。比如像下面示例这样,我们定义的拦截器将拦截所有匹配/interceptor/**模式的请求,但是不能拦截请求“/interceptor/b”,因为它定义为了exclude-mapping。当定义了exclude-mapping时,Spring MVC将优先判断一个请求是否在execlude-mapping定义的范围内,如果在则不进行拦截。

 

      

          

          

          

      

 

虽然笔者的示例中interceptor下面定义的mapping只有一个,但实际上一个interceptor下面定义的mappingexclude-mapping都是可以有多个的。

另外,exclude-mapping的定义规则和mapping的定义规则是一样的,我们也可以使用一个星号表示任意字符,使用两个星号表示任意层次的任意字符。比如下面这样。

Xml代码

 

      

          

          

          

      

  

阅读(4623) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~