一、拦截器
1、拦截器概述
2、拦截器执行
二、自定义拦截器
1、实现 HandlerInterceptor 或者 HandlerInterceptorAdapter
public class MyFirstInterceptor implements HandlerInterceptor {
/**
* 目标方法运行之前运行
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyFirstInterceptor... preHandle...");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyFirstInterceptor... postHandle...");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyFirstInterceptor... afterCompletion...");
}
}
2、在配置文件中注册配置拦截器
在 SpringMVC 的配置文件中注册这个拦截器,配置这个拦截器来拦截哪些请求的目标方法
<!-- 配置拦截器 -->
<mvc:interceptors>
<!--方式一:默认拦截所有请求-->
<!--配置某个拦截器,默认是拦截所有请求的;-->
<bean class="com.njf.interceptor.MyFirstInterceptor"></bean>
<!-- 方式二:此方式要求拦截器类上必须加注解 @Componentent -->
<!-- <ref bean="myFirstInterceptor" /> -->
<!-- 方式三:设置自定义拦截方式 -->
<!--配置某个拦截器更详情的信息-->
<mvc:interceptor>
<!--只来拦截 /test01 请求 -->
<mvc:mapping path="/test01"/> <!--设置所有的请求都拦截-->
<!--<mvc:exclude-mapping path="/"/> 去除指定的请求不拦截-->
<bean class="com.njf.interceptor.MySecondInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
三、单个拦截器运行流程
正常流程:
按配置顺序依次执行preHandle(),再执行目标方法的适配器和处理器;
然后再按一开始倒序来执行postHandle(),再渲染视图页面;
然后再倒序执行afterCompletion()
单个拦截器的正常运行流程:
断点调试拦截器执行流程:
其他流程:
四、多拦截器运行流程
配置两个拦截器,看一下是如何运行的:
拦截器一:
public class MyFirstInterceptor implements HandlerInterceptor {
/**
* 目标方法运行之前运行
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyFirstInterceptor... preHandle...");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyFirstInterceptor... postHandle...");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyFirstInterceptor... afterCompletion...");
}
}
拦截器二:
public class MySecondInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MySecondInterceptor... preHandle...");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MySecondInterceptor... postHandle...");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MySecondInterceptor... afterCompletion...");
}
}
配置拦截器:
<!-- 配置拦截器 -->
<mvc:interceptors>
<!--方式一:默认拦截所有请求-->
<!--配置某个拦截器,默认是拦截所有请求的;-->
<bean class="com.njf.interceptor.MyFirstInterceptor"></bean>
<!-- 方式二:此方式要求拦截器类上必须加注解 @Componentent -->
<!-- <ref bean="myFirstInterceptor" /> -->
<!-- 方式三:设置自定义拦截方式 -->
<!--配置某个拦截器更详情的信息-->
<mvc:interceptor>
<!--只来拦截 /test01 请求 -->
<mvc:mapping path="/test01"/> <!--设置所有的请求都拦截-->
<!--<mvc:exclude-mapping path="/"/> 去除指定的请求不拦截-->
<bean class="com.njf.interceptor.MySecondInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
运行结果:
(1)两个都返回 true
MyFirstInterceptor... preHandle...
MySecondInterceptor... preHandle...
test01被调用了
MySecondInterceptor... postHandle...
MyFirstInterceptor... postHandle...
success.jsp显示了
MySecondInterceptor... afterCompletion...
MyFirstInterceptor... afterCompletion...
(2)第一个返回 true,第二个返回 false
MyFirstInterceptor... preHandle...
MySecondInterceptor... preHandle...
MyFirstInterceptor... afterCompletion...
(3)第一个返回false,第二个返回true
MyFirstInterceptor... preHandle...
(4)两个都返回 false
MyFirstInterceptor... preHandle...
多个拦截器的流程:与 Filter 流程类似
(1)拦截器的preHandler:是按照顺序执行;
(2)拦截器的 postHandler:是按照逆序执行;
(3)拦截器的 afterCompletion:是按照逆序执行;
(4)已经放行了的拦截器的 afterCompletion 还是会执行;
多个拦截器的流程:
(1)全部返回 true(正常流程)
(2)第一个返回 false(出现拦截情况)
多个拦截器执行顺序总结:
1、当有多个拦截器时,且都返回为 true时
preHandle: 按照拦截器数组的正向顺序执行
postHandle:按照拦截器数组的反向顺序执行
afterCompletion:按照拦截器数组的反向顺序执行
2、当多个拦截器的 preHandle 有不同的值时
五、源码分析
从源码的执行角度分析流程:
(1)DispatcherServlet 中 doDispatch 方法:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request. 拿到方法的执行链,包含拦截器,目标方法 HandlerExecutionChain
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request. 拿到方法的适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//拦截器 preHandler 执行位置,有一个拦截器返回false目标方法以后都不会执行;直接跳到 triggerAfterCompletion
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler. 适配器执行目标方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(request, mv);
//目标方法只要正常,就会走到 postHandle,任何期间有异常就会到 afterCompletion
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
//页面渲染,如果异常也是直接跳到 triggerAfterCompletion
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
//页面异常,也会执行 afterCompletion,afterCompletion 总会执行
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Error err) {
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
(2)HandlerExecutionChain 的 applyPreHandler 方法
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (getInterceptors() != null) {
for (int i = 0; i < getInterceptors().length; i++) {
HandlerInterceptor interceptor = getInterceptors()[i];
//preHandle() 返回 true 就放行,返回false就拦截
if (!interceptor.preHandle(request, response, this.handler)) {
//执行完 triggerAfterCompletion()
triggerAfterCompletion(request, response, null);
//返回一个falsefalse
return false;
}
//记录一下拦截器的索引
this.interceptorIndex = i;
}
}
return true;
}
(3)HandlerExecutionChain 的 applyPostHandler 方法
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
if (getInterceptors() == null) {
return;
}
//逆向执行每个拦截器的 postHandle
for (int i = getInterceptors().length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = getInterceptors()[i];
interceptor.postHandle(request, response, this.handler, mv);
}
}
(4)页面渲染
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
//页面渲染
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
"': assuming HandlerAdapter completed request handling");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
if (mappedHandler != null) {
//页面正常执行 afterCompletion
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
(5)HandlerExecutionChain 的 triggerAfterCompletion 方法
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
throws Exception {
if (getInterceptors() == null) {
return;
}
//之前保存的拦截器的索引,有记录最后一个放行拦截器的索引,从他开始把之前所有放行的拦截器的 afterCompletion 都执行
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = getInterceptors()[i];
try {
interceptor.afterCompletion(request, response, this.handler, ex);
}
catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
七、拦截器与过滤器
1、拦截器与过滤器的区别?
2、什么用过滤器(Filter),什么时候用拦截器?
(1)如果某些功能比较复杂,需要其他组件配合完成,就使用拦截器,可以使用 @AutoWired 注入其他组件;需要依赖于Spring环境;
(2)Filter 比较适用于功能简单,如设置字符编码等,而且脱离Spring也可以运行,是由Tomcat来创建的;