本文共 9170 字,大约阅读时间需要 30 分钟。
拦截器是指通过统一拦截浏览器发往服务器的请求来完成功能的增强。
其使用场景一般是为了解决请求的共性问题,如:乱码问题、权限验证问题等。
拦截器和过滤器的工作原理非常相似,最后再总结。
拦截器有两种实现方式
1、 通过实现Spring的HandlerInterceptor接口,或者集成实现HandlerInterceptor接口的类,如该接口的抽象类HandlerInterceptorAdapter.
2、 通过实现Spring的WebRequestInterceptor接口,或者集成WebRequestInterceptor的类。
1、 编写拦截器类实现HandlerInterceptor接口。
2、 将拦截器注册进SpringMVC框架中。
3、 配置拦截器的拦截规则。
此处以Maven管理SpringMVC的项目
@@@@Pom.xml配置,添加依赖项
……
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>${spring.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
……
上述spring-framework-bom是为了提供springMVC框架,servlet-api是为了提供页面请求HttpServletRequest和页面返回HttpServletResponse等对象,但是Tomcat包含有这些对象,所以,使用Tomcat服务器的可以不用加入后面的servlet-api依赖,使用Jetty容器的则需要加入servlet-api依赖。
@@@@公共上下文applicationContext配置
<?xmlversion="1.0"encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<!--启动基于Annotation的DI管理 -->
<context:annotation-config/>
<context:component-scanbase-package="com.terence.mvcdemo">
<context:exclude-filtertype="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
</beans>
@@@@DispatcherServlet.xml文件配置
<?xmlversion="1.0"encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<context:annotation-config/>
<context:component-scanbase-package="com.terence.mvcdemo.controller">
<context:include-filtertype="annotation"expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<mvc:annotation-driven/>
<mvc:interceptors>
<mvc:interceptor>
<!--拦截规则:只拦截viewAll.form结尾的请求 -->
<mvc:mappingpath="/viewAll.form"/>
<beanclass="com.terence.mvcdemo.interceptor.Test1Interceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
<beanclass="org.springframework.web.servlet.view.InternalResourceViewResolver">
<propertyname="viewClass"value="org.springframework.web.servlet.view.JstlView"/>
<propertyname="prefix"value="/WEB-INF/jsps/"/>
<propertyname="suffix"value=".jsp"/>
</bean>
<!-- 注册拦截器 -->
</beans>
package com.terence.mvcdemo.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class Test1Interceptor implements HandlerInterceptor {
public void afterCompletion(HttpServletRequest arg0,
HttpServletResponse arg1,Object arg2,
Exception arg3) throws Exception {
System.out.println("执行到了afterCompletion1()");
}
public void postHandle(HttpServletRequest arg0,
HttpServletResponse arg1,Object arg2,
ModelAndView arg3) throws Exception {
arg3.addObject("msg","这是拦截器postHandle方法修改之后的消息");
//更改传回的参数值。
arg3.setViewName("hello2");//更改请求页面,类似于重定向
System.out.println("执行到了postHandle1()");
}
public boolean preHandle(HttpServletRequest arg0,
HttpServletResponse arg1,Object arg2)
throws Exception {
arg0.setCharacterEncoding("utf-8");
System.out.println("执行到了preHandle1()");
//如果用户没有登录,则终止请求,并返回到登录页面
if(arg0.getSession().getAttribute("user")==null)
{
arg0.getRequestDispatcher("/login").forward(arg0, arg1);
//return false;
}
return true;
}
}
在请求之前进行调用
public boolean preHandle(HttpServletRequest arg0,
HttpServletResponse arg1,Object arg2)
throws Exception {
System.out.println("preHandle");
return true;
}
此方法含有布尔型返回值,表示是否将当前请求拦截下来,如果返回false,表示请求将被终止,如果返回true,表示当前请求将继续运行。
HttpServletRequest:表示所有请求内容。
HttpServletResponse:表示所有返回内容。
Object:表示的是被拦截的请求的目标,表示的是控制器Controller下的某个方法,可以通过一些参数获取拦截对象中的一些内容。
在请求之后调用。
public void postHandle(HttpServletRequest arg0,
HttpServletResponse arg1,Object arg2,
ModelAndView arg3) throws Exception {
System.out.println("postHandle");
}
ModelAndView:通过该参数可以改变显示的视图,或修改发往视图的方法。
比如,如果想要在jsp页面中显示一个后台返回的消息,则在后台可以通过ModelAndView传回一个消息,在jsp页面中写${“msg”}即可,msg是ModelAndView对象里面要显示的消息名。
视图显示步骤完成之后要调用的方法,主要用于资源销毁,用于关闭一些资源或流。
<mvc:interceptor>
<!--拦截规则:只拦截viewAll.form结尾的请求 -->
<mvc:mappingpath="/viewAll.form"/>
<beanclass="com.terence.mvcdemo.interceptor.Test1Interceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
……
<mvc:mappingpath="/viewAll.form"/>
……
表示只拦截viewAll.form结尾的请求。
拦截器1:
public class Test1Interceptor implements HandlerInterceptor {
public void afterCompletion(HttpServletRequest arg0,
HttpServletResponse arg1,Object arg2,
Exception arg3) throws Exception {
System.out.println("执行到了afterCompletion1()");
}
public void postHandle(HttpServletRequest arg0,
HttpServletResponse arg1,Object arg2,
ModelAndView arg3) throws Exception {
arg3.addObject("msg","这是拦截器postHandle方法修改之后的消息");//更改传回的参数值。
arg3.setViewName("hello2");//更改请求页面,类似于重定向
System.out.println("执行到了postHandle1()");
}
public boolean preHandle(HttpServletRequest arg0,
HttpServletResponse arg1,Object arg2)
throws Exception {
System.out.println("执行到了preHandle1()");
return true;
}
}
拦截器2:
public class Test2Interceptor implements HandlerInterceptor {
public void afterCompletion(HttpServletRequest arg0,
HttpServletResponse arg1,Object arg2,
Exception arg3) throws Exception {
System.out.println("执行到了afterCompletion2()");
}
public void postHandle(HttpServletRequest arg0,
HttpServletResponse arg1,Object arg2,
ModelAndView arg3) throws Exception {
System.out.println("执行到了postHandle2()");
}
public boolean preHandle(HttpServletRequest arg0,
HttpServletResponse arg1,Object arg2)
throws Exception {
System.out.println("执行到了preHandle2()");
return true;
}
}
@@@@@viewSpace-servlet.xml配置文件(DispatcherServlet文件)
…………
<mvc:interceptors>
<mvc:interceptor>
<!--拦截规则:只拦截viewAll.form结尾的请求 -->
<mvc:mappingpath="/viewAll.form"/>
<beanclass="com.terence.mvcdemo.interceptor.Test1Interceptor"></bean>
</mvc:interceptor>
<mvc:interceptor>
<!--拦截规则:只拦截viewAll.form结尾的请求 -->
<mvc:mappingpath="/viewAll.form"/>
<beanclass="com.terence.mvcdemo.interceptor.Test2Interceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
…………
然后可以调拦截器注册的先后顺序,会发现,执行结果和上述拦截器的反应相反。得知拦截器是根据注册的先后顺序实行多拦截器的拦截,那么拦截器的具体执行过程是怎样的呢?可以表示为下图所示:
拦截器的第二种实现方式就是实现WebRequestInterceptor接口
这两种方法的方法名一样,只有参数有稍微区别,不过大同小异;另外,后者preHandle方法没有返回值,不能终止请求。
通过实现WebRequestInterceptor接口来实现拦截器,其注册方法不变,唯一的缺点就是不能终止请求。
使用原则:处理所有请求中的共性问题
可以在preHandle()方法中对请求内容进行统一编码:
public boolean preHandle(HttpServletRequest arg0,
HttpServletResponse arg1,Object arg2)
throws Exception {
arg0.setCharacterEncoding(“utf-8”);
System.out.println("执行到了preHandle2()");
return true;
}
很多内容都是针对登录用户才能使用,需要统一验证用户
……
public boolean preHandle(HttpServletRequest arg0,
HttpServletResponsearg1, Object arg2)
throws Exception {
arg0.setCharacterEncoding("utf-8");
System.out.println("执行到了preHandle1()");
//如果用户没有登录,则终止请求,并返回到登录页面
if(arg0.getSession().getAttribute("user")==null)
{
arg0.getRequestDispatcher("/login").forward(arg0, arg1);
//return false;
}
return true;
}
……
1) 拦截器Interceptor依赖于框架容器,基于java的反射机制,只过滤请求。 过滤器依赖于servlet容器,基 于回调函数,过滤范围较大。2) 拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。3) 拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。4) 在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。5) 拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个service,可以 调用业务逻辑6) 拦截器可以处理web应用中请求的一些通用问题,将拦截单独编写,独立于Controller,可以处理一些共性的 问题,减少了代码服用,便于维护。
Reference Demo: