栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

0010-Spring MVC 拦截器及其源码解析

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

0010-Spring MVC 拦截器及其源码解析

HandlerInterceptor

Spring MVC 中拦截器的顶层接口如下:

package org.springframework.web.servlet;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.lang.Nullable;

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 {
    }
}
拦截器的执行

在Spring MVC流程解析一章的最后我们知道,拦截器是封装在HandlerExecutionChain对象中的,而且是一个集合。意味着我们可以有多个拦截器同时作用。
我们再来看看DispatcherServlet的doService方法,其最终调用的是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 {
            try {
                ModelAndView mv = null;
                Object dispatchException = null;

                try {
                    processedRequest = this.checkMultipart(request);
                    multipartRequestParsed = processedRequest != request;
                    mappedHandler = this.getHandler(processedRequest);
                    if (mappedHandler == null) {
                        this.noHandlerFound(processedRequest, response);
                        return;
                    }

                    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                    String method = request.getMethod();
                    boolean isGet = "GET".equals(method);
                    if (isGet || "HEAD".equals(method)) {
                        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                        if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                            return;
                        }
                    }

                    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                        return;
                    }

                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }

                    this.applyDefaultViewName(processedRequest, mv);
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                } catch (Exception var20) {
                    dispatchException = var20;
                } catch (Throwable var21) {
                    dispatchException = new NestedServletException("Handler dispatch failed", var21);
                }

                this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
            } catch (Exception var22) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
            } catch (Throwable var23) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
            }

        } finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            } else if (multipartRequestParsed) {
                this.cleanupMultipart(processedRequest);
            }

        }
    }

太长了,我们截取出关键代码如下:

.....
// mappedHandler就是HandlerExecutionChain对象
// 该方法就是通过HandlerExecutionChain对象的handler对象获取对应的HandlerAdapter
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
......
// 调用HandlerExecutionChain的applyPreHandle方法,如果返回false,则直接return
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    return;
}
......
// 执行HandlerAdapter方法的handle方法(就是执行我们Controller的实际逻辑)
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
......
// 进行一些页面渲染的操作
this.applyDefaultViewName(processedRequest, mv);
// 调用HandlerExecutionChain的applyPostHandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
.......

其中只有两个方法与我们的拦截器有关,applyPreHandle、applyPostHandle,这两个方法都是HandlerExecutionChain对象的方法

applyPreHandle方法
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HandlerInterceptor[] interceptors = this.getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) {
                HandlerInterceptor interceptor = interceptors[i];
                if (!interceptor.preHandle(request, response, this.handler)) {
                    this.triggerAfterCompletion(request, response, (Exception)null);
                    return false;
                }
            }
        }

        return true;
    }

方法内直接调用了所有配置的拦截器的preHandle方法,只要有一个拦截器的preHandle方法返回false,则调用triggerAfterCompletion方法,并直接返回false。之后的拦截器不再执行。

如DispatcherServlet的代码applyPreHandle如果返回false则当前的请求将直接return;

triggerAfterCompletion方法
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) throws Exception {
        HandlerInterceptor[] interceptors = this.getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for(int i = this.interceptorIndex; i >= 0; --i) {
                HandlerInterceptor interceptor = interceptors[i];

                try {
                    interceptor.afterCompletion(request, response, this.handler, ex);
                } catch (Throwable var8) {
                    logger.error("HandlerInterceptor.afterCompletion threw exception", var8);
                }
            }
        }

    }

triggerAfterCompletion方法依次调用了所有拦截器的afterCompletion方法。

applyPostHandle方法
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
        HandlerInterceptor[] interceptors = this.getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for(int i = interceptors.length - 1; i >= 0; --i) {
                HandlerInterceptor interceptor = interceptors[i];
                interceptor.postHandle(request, response, this.handler, mv);
            }
        }

    }

applyPostHandle方法依次调用了所有拦截器的postHandle方法。

总结
  • 如果我们的添加了拦截器,在执行请求之前会先执行preHandle方法,如果返回false,则执行所有拦截器的afterCompletion方法,然后请求直接return。
  • 在执行请求之后会执行所有拦截器的postHandle方法
  • 如果请求过程中出现异常(见DispatcherServlet源码),在catch异常后调用了triggerAfterCompletion方法。意味着,如果出现异常,则会调用拦截器的afterCompletion方法。
拦截器各个方法的作用

preHandle方法:在请求处理之前执行,可用于请求前的权限认证等操作

postHandle方法:在请求之后执行。可用于请求执行之后的日志记录等操作

afterCompletion方法:主要用于释放当前拦截器使用的资源等操作。

自定义拦截器

自定义拦截器一般通过如下方式来实现

  • 直接实现HandlerInterceptor接口。
  • 继承HandlerInterceptorAdapter抽象类
    HandlerInterceptorAdapter其实就是一个适配器,jdk1.8之后接口可以定义default方法,HandlerInterceptor的接口定义默认就是一个适配器。所以我们通过直接实现HandlerInterceptor接口只重写其中一个方法也是可以的。
继承HandlerInterceptorAdapter来实现
package com.yyoo.springmvc.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("拦截器 preHandle");
        return true;
    }

    @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");
    }
}

配置拦截器

还记得我们前面实现WebMvcConfigurer的配置类吗?在该类中重写如下代码来配置拦截器。

    @Bean
    public MyInterceptor myInterceptor(){
        return new MyInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(myInterceptor())
                .addPathPatterns("/**") // 拦截器作用的url
                .order(1);// 拦截器的注册顺序
    }

上一篇:009-Spring MVC Json返回
下一篇:待续

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/389714.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号