目录
Spring MVC拦截器使用
拦截器简介
Spring Web MVC的拦截器,类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理;Spring MVC拦截器是可插拔式的设计,在配置文件中配置或取消即可。
拦截器:通常指通过拦截从浏览器发往服务器的一些请求来完成某些功能的一段程序代码,一般在一个请求发生之前,发生时,发生后都可以对请求进行拦截。
拦截器与过滤器区别
过滤器是依赖于Servlet容器,基于回调函数;Interceptor依赖于框架,基于反射机制。
过滤器的过滤范围更大,还可以过滤一些静态资源。
1.拦截器是基于Java的反射机制,而过滤器是基于函数回调
2.拦截器不依赖于Servlet容器,过滤器依赖于Servlet容器
3.拦截器只能对Action请求起作用,而过滤器则可以对几乎所有的请求起作用
4.拦截器可以访问Action上下文、值栈里的对象,而过滤器不能访问
5.在Action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次
6.拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个Service,可以调用业务逻辑
拦截器只拦截请求
1.日志记录
记录请求信息的日志,以便进行信息监控、信息统计、计算PV(Page View)等
2.权限检查
如登录检测,进入处理器检测检测是否登录,如果没有直接返回到登录页面
3.性能监控
有时候系统在某段时间莫名其妙的慢,可以通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间(如果有反向代理,如apache可以自动记录)
4.通用行为
读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取Locale、Theme信息等,只要是多个处理器都需要的即可使用拦截器实现
5.OpenSessionInView
如Hibernate,在进入处理器打开Session,在完成后关闭Session
本质也是AOP(面向切面编程),也就是说符合横切关注点的所有功能都可以放入拦截器实现
拦截器接口
org.springframework.web.servlet.HandlerInterceptor
package org.springframework.web.servlet;
public interface HandlerInterceptor {
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
void postHandle( HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndViewmodelAndView) throws Exception;
void afterCompletion( HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception;
}
拦截器三个方法
方法 | 描述 |
---|---|
preHandle | 在控制器的处理请求方法之前执行,其返回值表示是否中断后续操作。返回true表示继续向下执行,就会继续调用下一个Interceptor的preHandle方法,若已经是最后一个Interceptor,就会调用当前请求的Controller。返回false表示中断后续操作,后续的Interceptor和Controller都不会再执行,此时需要通过response来产生响应 |
postHandle | 在控制器的处理请求方法调用之后、解析视图之前执行,可以通过此方法对请求域中的模型和视图做进一步的修改。只能在当前所属的interceptor的preHandle方法返回true之后才能被调用。在Controller方法之后DispatherServlet进行视图返回渲染之前执行,可以对Controller方法返回的ModelAndView(模型和视图对象)对模型数据进行处理或对视图进行处理,modelAndView也可能为null。先声明该方法的Interceptor会后执行 |
afterCompletion | 在控制器的处理请求方法执行完成后执行,即视图渲染结束后执行,可以通过此方法实现一些资源清理、记录日志信息等工作。在当前所属Interceptor的prehandle()方法返回true时执行。该方法在DispatcherServlet渲染了对应的视图之后执行。在视图渲染完毕时回调,如性能监控中可以在此记录结束时间并输出消耗时间,还可以进行一些资源清理,类似于try-catch-finally中的finally,但仅调用处理器执行链中preHandle返回true的拦截器的afterCompletion |
拦截器链执行顺序
preHandle按顺序执行,postHandle、afterCompletion则是逆向执行
第一步:preHandle 1 --> preHandle 2 --> preHandle 3
第二步:postHandle 3 --> postHandle 2 --> postHandle 1
第三步:afterCompletion 3 --> afterCompletion 2 --> afterCompletion 1
自定义拦截器
定义一个拦截器需要对拦截器进行定义和配置,定义一个拦截器可以通过两种方式:
(1)一种是通过实现 HandlerInterceptor 接口或继承 HandlerInterceptor 接口的实现类(HandlerInterceptorAdapter)来定义
(2)通过实现 WebRequestInterceptor 接口或继承 WebRequestInterceptor 接口的实现类()来定义
1.实现接口
①实现HandlerInterceptor接口
public interface HandlerInterceptor {
// 在控制器的处理请求方法调用之前
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
// 在控制器的处理请求方法调用之后,解析视图之前执行
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
}
// 在控制器的处理请求方法执行完成后,视图渲染结束之后执行
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
}
}
/**
*
* 该方法进行处理器拦截,该方法将在Controller处理之前调用。该方法返回true拦截器才会继续往下执行,返回false整个请求结束
* /
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("AuthorizationInterceptor preHandle -->");
//flag判断用户是否登录
boolean flag = false;
//获取请求的路径进行判断
String servletPath= request.getServletPath();
//判断是否需要拦截请求
for (String s : IGNORE_URI) {
if(servletPath.contains(s)) {
flag = true;
break;
}
}
//拦截请求
if(!flag) {
//获取session总的用户
User user = (User) request.getSession().getAttribute("user");
//判断是否登录
if (user == null) {
//用户没登录
System.out.println("AuthorizationInterceptor拦截请求:");
request.setAttribute("message", "请先登录!");
request.getRequestDispatcher("login").forward(request, response);
} else {
//已登录
System.out.println("AuthorizationInterceptor放行请求:");
flag = true;
}
}
return flag;
}
②实现WebRequestInterceptor接口
WebRequestInterceptor的入参WebRequest是包装了HttpServletRequest和HttpServletResponse,通过WebRequest获取Request中的信息更简便。
WebRequestInterceptor的preHandle是没有返回值的,说明该方法中的逻辑并不影响后续的方法执行,所以这个接口实现就是为了获取Request中的信息,或者预设一些参数供后续流程使用。
HandlerInterceptor的功能更强大也更基础,可以在preHandle方法中就直接拒绝请求进入controller方法
public class OneInterceptor implements WebRequestInterceptor {
@Override
public void preHandle(WebRequest request) throws Exception {
}
@Override
public void postHandle(WebRequest request, ModelMap model) throws Exception {
}
@Override
public void afterCompletion(WebRequest request, Exception ex) throws Exception {
}
}
2.配置文件(springmvc-servlet.xml)
<!-- 配置拦截器 -->
<mvc:interceptors>
<!--如果在interceptors中配置bean,那么所有定义在这里的bean都会被拦截-->
<!--配置一个全局拦截器:拦截所有-->
<bean class="com.my.AllIntercepttor"/>
<mvc:interceptor>
<!--配置拦截器作用路径-->
<mvc:mapping path="/gotoTest" />"
<!--配置不需要拦截作用路径-->
<mvc:exclude-mapping path="" />
<!--过滤登陆死循环情况-->
<!--<mvc:exclude-mapping path="/manage/login.do"/>-->
<!--定义在<mvc:interceptor>元素中,表示匹配指定路径的请求才能拦截-->
<bean class="com.my.TestIntercepttor"></bean>
</mvc:interceptor>
</mvc:interceptors>
如上述示例代码中,path的属性值“/**”表示拦截所有路径,“/gotoTest”表示拦截所有以“/gotoTest”结尾的路径。如果在请求路径中包含不需要拦截的内容,可以通过 <mvc:exclude-mapping> 子元素进行配置
元素节点 | 描述 |
---|---|
interceptors节点 | Spring MVC的拦截器集配置节点,在这个节点里面可以声明多个interceptor |
interceptor节点 | 配置拦截路径以及拦截器实现类的节点 |
mapping节点 | 符合mapping路径匹配的请求都会经过拦截器(拦截) |
exclude-mapping节点 | 符合exclude-mapping路径匹配的请求都不会经过拦截器(不拦截) |
bean节点 | 配置我们自己的实现类,实现类要实现HandlerInterceptor接口 |
当定义了exclude-mapping时,Spring MVC将优先判断一个请求是否在execlude-mapping定义的范围内,如果在则不进行拦截。 一个interceptor下面定义的mapping和exclude-mapping都是可以有多个的
另外,exclude-mapping的定义规则和mapping的定义规则是一样的,也可以使用一个星号表示任意字符,使用两个星号表示任意层次的任意字符
注意:<mvc:interceptor>元素的子元素必须按照<mvc:mapping…/>、<mvc:exclude-mapping…/>、<bean…/>的顺序配置
拦截方式
可以利用mvc:interceptors标签声明一系列的拦截器,然后它们就可以形成一个拦截器链,拦截器的执行顺序是按声明的先后顺序执行的,先声明的拦截器中的preHandle方法会先执行,然而它的postHandle方法和afterCompletion方法会后执行。
方式一:总拦截器,拦截所有url
直接定义一个Interceptor实现类的bean对象。使用这种方式声明的Interceptor拦截器将会对所有的请求进行拦截。
<mvc:interceptors>
<bean class="com.app.mvc.MyInteceptor" />
</mvc:interceptors>
方式二:局部拦截器, 拦截匹配的URL
使用mvc:interceptor标签进行声明。使用这种方式进行声明的Interceptor可以通过mvc:mapping子标签来定义需要进行拦截的请求路径
<mvc:interceptors >
<mvc:interceptor>
<mvc:mapping path="/user/*" /> <!-- /user/* -->
<mvc:exclude-mapping path="/user/b" /><!-- 不匹配的 -->
<bean class="com.mvc.MyInteceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
方案三:HandlerMapping上的拦截器
如果基于xml配置使用Spring MVC,可以利用SimpleUrlHandlerMapping、BeanNameUrlHandlerMapping进行Url映射(相当于struts的path映射)和拦截请求(注入interceptors)
如果基于注解使用Spring MVC,可以使用DefaultAnnotationHandlerMapping注入interceptors。
注意:无论基于xml还是基于注解,Interceptor bean都是需要在xml中配置的
<bean class="…….servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors">
<list>
<bean class="com.mvc.MyInteceptor"></bean>
</list>
</property>
</bean>