Spring MVC拦截器

Spring MVC拦截器

一、概述

二、简介

SpringMVC 的拦截器 Interceptor 规范,主要是对 Controller 资源访问时进行拦截操作的技术,当然拦截后可以进行权限控制,功能增强等都是可以的。拦截器有点类似 Javaweb 开发中的 Filter,拦截器与 Filter 的区别如下图:

2.1Filtery与Interceptor对比:

2.2拦截器Interceptor简介:

实现了 HandlerInterceptor 接口,且被 Spring 管理的 Bean 都是拦截器,接口定义如下:

java 复制代码
package com.itheima.interceptors;

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

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

public class MyInterceptor1 implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("MyInterceptor1...preHandle");
        return true; //是否放行  true放行
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("MyInterceptor1...postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("MyInterceptor1...afterCompletion");
    }
}

实际上Controller层的方法也就是所谓的Handler处理器(目标方法),preHandle方法在目标方法之前执行,返回true目标方法才有机会执行, postHandle在目标方法之后执行,不一定执行,afterCompletion是在视图渲染完毕,在最后执行(有点类似于try-catch方法中的finally方法)。(全部执行完之后才执行),下面是三个方法的一些参数:

三、HandlerInterceptor拦截器快速入门

3.1先实现HandlerInterceptor接口定义自己的拦截器
java 复制代码
package com.itheima.interceptors;

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

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

public class MyInterceptorm1 implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("HandlerInterceptor...preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("HandlerInterceptor...postHandle");
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("HandlerInterceptor...afterCompletion");
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

preHandle方法是最重要的,直接关乎到资源是否能够访问。

3.2在SpringMVC容器中配置拦截器
java 复制代码
<!--    配置拦截器-->
    <mvc:interceptors>
        <mvc:interceptor>
<!--            这里实际上是配置拦截的路径-->
<!--            /*是只能拦截下一层目录的所有,/**是拦截多层目录的-->
            <mvc:mapping path="/**"/>
            <bean class="com.itheima.interceptors.MyInterceptor1"/>
        </mvc:interceptor>
    </mvc:interceptors>
3.3实验测试

1、正常访问,preHandle返回true(代表可以访问目标资源):

2、当让preHandle返回false时,只有preHandle,目标方法无法执行, postHandle、afterCompletion又是跟随preHandle:

四、拦截器执行顺序

4.1总述

4.2Interceptor本身执行顺序、preHandle全部返回true


只与配置顺序有关,这种情况又恰好是preHandle全部返回true的情况,所以直接借这个实验说明了:

4.3preHandle有返回false的

两个拦截器,第2个返回false:

五、拦截器执行原理

从前段控制器的源码来分析原理:

java 复制代码
 HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            try {
                ModelAndView mv = null;
                Exception dispatchException = null;

                try {
                    processedRequest = this.checkMultipart(request);
                    multipartRequestParsed = processedRequest != request;
                    mappedHandler = this.getHandler(processedRequest);

HandlerExecutionChain:

java 复制代码
public class HandlerExecutionChain {
    private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);
    private final Object handler;
    private final List<HandlerInterceptor> interceptorList;
    private int interceptorIndex;

主要是一些目标资源和拦截器,和上面的流程图对应了。

java 复制代码
  if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                        return;
                    }
java 复制代码
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex = i++) {
            HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);
            if (!interceptor.preHandle(request, response, this.handler)) {
                this.triggerAfterCompletion(request, response, (Exception)null);
                return false;
            }
        }

        return true;
    }

执行前置方法由小到大遍历。

java 复制代码
 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

这就是执行目标方法。

java 复制代码
 mappedHandler.applyPostHandle(processedRequest, response, mv);
java 复制代码
 void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
        for(int i = this.interceptorList.size() - 1; i >= 0; --i) {
            HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);
            interceptor.postHandle(request, response, this.handler, mv);
        }

    }

执行后置方法,倒序地遍历。

java 复制代码
this.processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
java 复制代码
 private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception {
        boolean errorView = false;
        if (exception != null) {
            if (exception instanceof ModelAndViewDefiningException) {
                this.logger.debug("ModelAndViewDefiningException encountered", exception);
                mv = ((ModelAndViewDefiningException)exception).getModelAndView();
            } else {
                Object handler = mappedHandler != null ? mappedHandler.getHandler() : null;
                mv = this.processHandlerException(request, response, handler, exception);
                errorView = mv != null;
            }
        }

        if (mv != null && !mv.wasCleared()) {
            this.render(mv, request, response);
            if (errorView) {
                WebUtils.clearErrorRequestAttributes(request);
            }
        } else if (this.logger.isTraceEnabled()) {
            this.logger.trace("No view rendering, null ModelAndView returned.");
        }

        if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
            if (mappedHandler != null) {
                mappedHandler.triggerAfterCompletion(request, response, (Exception)null);
            }

        }
    }

mappedHandler.triggerAfterCompletion(request, response, (Exception)null); ,是执行最终方法了:

java 复制代码
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
        for(int i = this.interceptorIndex; i >= 0; --i) {
            HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);

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

    }

顺序是倒序了,这段原理分析同时也验证了拦截器的执行顺序。

相关推荐
Interview Aid1122 小时前
TikTok OA 四题分享|半小时内 AC,题目基本都是实现题
java·开发语言·算法·面试·职场和发展
mldong2 小时前
换了工作流引擎,前端一行代码没改
java·架构
东风破_9 小时前
danci 2:创建的单词书到底存在哪里?从 Supabase 一路理解 ORM、Drizzle 和 RLS
数据库·后端·node.js
微尘寒风10 小时前
【Git】的安装和使用
java·git
东风破_10 小时前
danci 项目(三):从 JSON 数据到 AI Coding,真实项目里的数据清洗、Prompt 和工程规范
前端·后端·node.js
y = xⁿ10 小时前
DeepSeek Harness 学习日记:关于Agent接口,工具调用的底层实现
android·java·学习
东风破_10 小时前
danci 项目(一):从需求到架构,一个单词学习系统为什么会这样设计
前端·后端·node.js
侧耳倾听11111 小时前
jwt使用简介
java·jwt
顶点多余11 小时前
那些在算法中适合巩固的知识点---1
java·前端·算法
AI人工智能+电脑小能手11 小时前
大白话说Java设计模式-46-责任链模式(业务实战篇)
java·设计模式·责任链模式·风控校验·servlet filter·spring interceptor·链式处理