一:拦截器的应用
在Web应用中,拦截器可以用来处理异常、记录日志、登录拦截或权限认证等操作。在SpringMVC中,定义一个拦截器也十分简单,Spring提供了一个拦截器的接口HandlerInterceptor,实现该接口即可自定义一个拦截器。我们还是拿上篇博客创建的工程为例,对其进行改造。
1、创建一个类,实现接口HandlerInterceptor:
在src下新建一个包“cn.jingpengchong.hello.interceptor”,创建一个类MyInterceptor,让它实现接口HandlerInterceptor。
package cn.jingpengchong.hello.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 MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("preHandle");
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("afterCompletion");
}
}
2、在配置文件中配置该拦截器:
拦截所有请求:
<mvc:interceptors>
<bean class="cn.jingpengchong.hello.interceptor.MyInterceptor"/>
</mvc:interceptors>
拦截特定请求:
<mvc:interceptors>
<mvc:interceptor>
<!-- 拦截所有的请求,这个必须写在前面,也就是写在【不拦截】的上面 -->
<mvc:mapping path="/**" />
<!-- 但是排除下面这些,也就是不拦截请求 -->
<mvc:exclude-mapping path="/login.html" />
<mvc:exclude-mapping path="/account/login.do" />
<bean class="cn.jingpengchong.hello.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
在这里我们配置拦截所有请求。
3、测试:
控制器HelloController中的方法如下:
@RequestMapping("hello")
public String hello() {
System.out.println("hello");
return "hello";
}
发布该项目,在浏览器发送请求后结果如下:
控制台信息如下:
将拦截器中的preHandle()方法的返回值设置为true后,再次发布项目,发送请求后结果如下:
控制台打印的信息如下:
从结果来看,我们知道,第一次的请求被拦截了,而第二次被放行了;并且我们直观的发现了拦截其中三个方法和handler的执行顺序为:preHandle->handler->postHandle->afterCompletion。那么其内部是如何执行的呢?下面就来分析一下。
二:拦截器的源码分析
之前的流程就不再说了,可以参考博客:springmvc一个请求的执行过程。
这次我们直接从DispatcherServlet类中的doDispatch()方法开始分析,我们知道在doDispatch()方法的此处会根据映射器获得与请求相匹配的handler方法:
在下面的代码中,又将该handler方法传给了适配器:
映射处理器会在此处调用执行applyPreHandle()方法:
在applyPreHandle()方法内,会获得该程序的所有拦截器,其中就有一个我们自定义的拦截器MyInterceptor:
接着便对拦截器进行遍历,逐个调用其preHandle()方法:
在 ConversionServiceExposingInterceptor的preHandle()方法中,只设置了一个属性,并返回了true:
由于该方法返回了true,所以if判断语句为false,跳过if代码块不执行:
下面就遍历到了自定义的拦截器MyInterCeptor,并调用自定义拦截器的preHandle()方法:
在MyInterCeptor的preHandle()方法中执行了一条打印语句,并返回了false:
由于该方法返回了false,所以if的判断语句为true,所以执行了if代码块并返回了false:
由于applyPreHandle()方法返回了false,因此这里为true,方法直接就结束了,而并没有执行下面的processDispatchResult()方法,当然就没有执行render()方法,也当然没有执行getRequestDispatcher()方法,最终也没有进行请求转发,因此页面的响应结果就如我们第一次执行所显示的那样,什么都没有:
但是如果MyInterceptor的preHandle()方法返回了true,则上图的if判断为false,因此程序接着向下执行,并在此处执行了handler方法:
上面的方法执行完毕后获得了一个ModelAndView对象mv:
接着向下执行,又执行到了applyPostHandle()方法:
在applyPostHandle()方法中,获得了所有拦截器组成的数组后在遍历的时候从后向前遍历,因此先执行了自定义拦截器MyInterceptor的postHandle()方法:
在MyInterceptor的postHandle()方法中,我们只输出了一条语句:
在applyPostHandle()方法执行完毕后,就执行到了processDispatchResult()方法:
在processDispatchResult()方法中执行了render()方法,在该方法内解析视图,并调用解析后的视图的render()方法,并在视图的render()方法中执行getRequestDispatcher()方法,并接着调用forward()方法进行请求转发操作。这些在之前已经做了论述,此处不再介绍:
当执行了render()方法后,接着向下执行,由于mappedHandler不为null,所以就执行到了triggerAfterCompletion()方法:
在triggerAfterCompletion()方法方法中按照同样的套路获得拦截器数组,并遍历,逐个调用其afterCompletion()方法:
由于是倒着遍历,所以第一次就执行到了自定义的拦截器MyInterceptor的afterCompletion()方法,在该方法中,我们只打印了一条语句:
当该方法执行完毕后triggerAfterCompletion()又调用了HandlerInterceptorAdapter中的afterCompletion()方法,但该方法是个空方法:
该方法结束后triggerAfterCompletion()方法也就结束了,triggerAfterCompletion()方法结束后processDispatchResult()方法也就结束了,我们的分析也到此结束吧,呼~