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

    }

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

相关推荐
油丶酸萝卜别吃11 小时前
Java 集合类全景介绍
java·开发语言
神奇小汤圆11 小时前
解放双手!SpringBoot 6种自动填充公共字的手段,这些代码真没必要手写!
后端
钱栈up11 小时前
"Flowable 工作流引擎进阶实战(高级篇):任务分配、流程变量与监听器"
java
鱼人11 小时前
多态底层原理:向上转型、动态绑定,一文彻底搞懂
后端
豆沙沙包?11 小时前
c++中引用(P7-P11)
java·c++·算法
神奇小汤圆11 小时前
吃透Redis缓存核心:淘汰策略、LRU/LFU区别与四大缓存问题全解
后端
wuminyu12 小时前
虚拟线程底层ForkJoinPool的工作窃取算法机制
java·linux·c语言·jvm·c++
无责任此方_修行中12 小时前
搓了一个国产大模型与 AI Agent 比价工具
前端·后端·ai编程
掉鱼的猫12 小时前
Solon 的日志设计哲学:不只是用 SLF4J 做统一门面
java
szephyr12 小时前
腾讯云 ADP 智能体的 Skills 配置变更后没有走灰度,直接全量替换出了问题怎么定位?
java·前端·腾讯云·腾讯云adp