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

    }

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

相关推荐
MgArcher2 小时前
Python高级特性:高阶函数完全指南
后端·面试
橘子编程2 小时前
MindOS:你的AI第二大脑知识库
java·开发语言·人工智能·计算机网络·ai
XWalnut2 小时前
LeetCode刷题 day9
java·算法·leetcode
忧郁的Mr.Li2 小时前
JAVA工具类---PDF电子签章工具类
java·pdf
零二年的冬2 小时前
epoll详解
java·linux·开发语言·c++·链表
凭君语未可2 小时前
Java 中的接口是什么
java·开发语言
XiYang-DING2 小时前
【Java】二叉树
java·开发语言·数据结构
databook2 小时前
逃离SQL丛林:实用主义的数据救赎
后端·sql·数据分析
凌冰_2 小时前
Servlet+Thymeleaf + Fetch 实现无刷新异步请求
java·servlet