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

    }

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

相关推荐
摇滚侠8 分钟前
DBeaver 导入数据库 导入 SQL 文件 MySQL 备份恢复
java·数据库·mysql
古城小栈8 分钟前
从 cargo-whero 库中,找到提升 rust 的契机
开发语言·后端·rust
keep one's resolveY31 分钟前
SpringBoot实现重试机制的四种方案
java·spring boot·后端
天空属于哈夫克31 小时前
企业微信API常见的错误和解决方案
java·数据库·企业微信
摇滚侠2 小时前
VMvare 虚拟机 Oracle19c 安装步骤,远程连接 Oracle19c,百度网盘安装包
java·oracle
梁萌2 小时前
idea报错找不到XX包的解决方法
java·intellij-idea·启动报错·缺少包
Agent产品评测局2 小时前
生产排期与MES/ERP系统打通,实操方法详解 —— 2026企业级智能体自动化选型与实战指南
java·运维·人工智能·ai·chatgpt·自动化
阿丰资源2 小时前
基于Spring Boot的电影城管理系统(直接运行)
java·spring boot·后端
IT_陈寒2 小时前
SpringBoot自动配置的坑差点让我加班到天亮
前端·人工智能·后端
呱牛do it3 小时前
企业级门户网站设计与实现:基于SpringBoot + Vue3的全栈解决方案(Day 8)
java