Spring MVC拦截器

一、什么是拦截器

拦截器是 SpringMVC 提供的一种可以在请求处理过程中对请求进行预处理或后处理的机制。简单来说,拦截器就像是一位"守门员",它拦住所有进来的请求,根据设定的规则决定是否放行或者进行某些操作。

拦截器可以:

  • 在请求进入 Controller 之前做一些操作(如权限检查、日志记录、性能监控等)。
  • 在请求返回前做一些操作(如对返回数据进行修改、记录日志等)。

二、拦截器和过滤器的区别

拦截器与过滤器(Filter)有许多相似之处,它们都可以对 HTTP 请求和响应进行拦截和处理。但是,它们的工作原理和应用场景有所不同。

|----------------------|---------------------------|--------------------------|
| 特性 | 拦截器 (Interceptor) | 过滤器 (Filter) |
| 生命周期 | 只在 SpringMVC 生命周期内起作用 | 可以跨多个应用,通常与 Servlet 容器相关 |
| 适用范围 | 只适用于 SpringMVC 控制器的请求处理过程 | 可以处理所有类型的请求和响应 |
| 执行时机 | 在 Controller 方法调用之前和之后 | 在请求到达 Servlet 之前和响应返回之前 |
| 是否能够访问 SpringMVC 的功能 | 可以访问 SpringMVC 提供的各种功能 | 不能直接访问 SpringMVC 的功能 |
| 配置方式 | 配置在 Spring 配置文件中 | 配置在 web.xml 文件中 |

拦截器更贴近 SpringMVC 的请求处理机制,能与 Spring 提供的功能更加紧密地集成,而过滤器更多的是一种通用的 Web 组件,适用于不同类型的 Web 应用。

过滤器依赖于servlet,而拦截器技术属于SpringMVC

过滤器可对所有请求起作用,拦截器只对访问controller层的请求起作用。

过滤器会比拦截器先执行。拦截器(Interceptor)是在Servlet和Controller控制器之间执行;而过滤器(Filter)是在请求进入Tomcat容器之后 但是在请求进入Servlet之前执行。

三、拦截器的工作时机

1、三个阶段

预处理阶段:在请求到达 Controller 之前,拦截器的 preHandle() 方法会被执行。

后处理阶段:在 Controller 方法执行后,视图渲染之前,拦截器的 postHandle() 方法会被调用。

最终处理阶段:无论请求成功与否,拦截器的 afterCompletion() 方法都会在请求结束时执行。

2、三个常用方法

都在接口HandlerInterceptor中声明。

1.preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)

在 Controller 方法执行之前调用。返回值是 boolean 类型,true 表示继续执行,false 表示请求被中止,后续的拦截器和 Controller 不会执行。

2. postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)

在 Controller 方法执行后,视图渲染前调用。可以对 ModelAndView 进行修改。

3. afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)

在请求处理完成后(视图渲染后)调用,用于清理资源。

3、多个拦截器执行

1. 请求进入多个拦截器的 preHandle() 方法

请求会按照拦截器的配置顺序进入每个拦截器的 preHandle() 方法。

如果某个拦截器的 preHandle() 返回 false,那么请求会被中断,后续的拦截器和 Controller 方法都不会被执行。

2. 请求进入 Controller

如果所有拦截器的 preHandle() 都返回 true,请求会最终进入 Controller 的目标方法。

3. 多个拦截器的 postHandle() 方法

当 Controller 方法执行完毕后,拦截器的 postHandle() 方法会按配置顺序从后往前被调用。即,最后配置的拦截器的 postHandle() 会最先执行。

postHandle() 方法中的 ModelAndView 还没有被渲染,可以对它进行修改。

4. 多个拦截器的 afterCompletion() 方法

最后,拦截器的 afterCompletion() 方法会按配置顺序从前往后被调用,即,第一个配置的拦截器的 afterCompletion() 会最先执行。这个方法通常用于清理工作,比如日志记录或释放资源。

4、单个拦截器和多个拦截器执行顺序对比

|-------------------|----------------------|-------------------------|
| 阶段 | 单个拦截器执行顺序 | 多个拦截器执行顺序 |
| preHandle() | 按顺序执行,遇到 false 中断请求 | 按顺序执行,遇到 false 中断请求 |
| Controller | 执行 Controller 方法 | 执行 Controller 方法 |
| postHandle() | 按顺序执行(如果有多个拦截器,从前到后) | 按配置顺序反向执行(从最后一个拦截器到第一个) |
| afterCompletion() | 按顺序执行(从前到后) | 按配置顺序正向执行(从第一个拦截器到最后一个) |

四、单个拦截器示例

1、创建拦截器

java 复制代码
package com.goose.config;

import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 获取请求url
        String url = request.getRequestURI();
        // 请求是否是登录相关的请求
        if(url.contains("login")){
            return true;
        }else{// 判断是否登录过
            if(request.getSession().getAttribute("user")!=null){
                return true;
            }else{
                // 重定向到login页面
                response.sendRedirect("/html/login.html");
                return false;
            }
        }
    }
}

2、配置拦截器

XML 复制代码
<!-- 配置拦截器 -->
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**"/> <!--表示拦截所有请求-->
        <bean class="com.goose.config.LoginInterceptor" ></bean>
    </mvc:interceptor>
</mvc:interceptors>

3、在Controller 中处理登录

java 复制代码
package com.goose.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.http.HttpSession;

@Controller
@RequestMapping("/login")
public class LoginController {
    @RequestMapping("/toLogin")
    public String toLogin(){
        return "login";
    }

    @RequestMapping(value = "/login")
    public String login(String username, String password, HttpSession session){
        if(username.equals("张三") && password.equals("123")){
            System.out.println("用户登录成功");
            session.setAttribute("user","张三");
            return "A";
        }
        return "B";
    }
}

html界面略

相关推荐
ZZHow10243 小时前
JavaWeb开发_Day05
java·笔记·web
CHEN5_023 小时前
【Java虚拟机】垃圾回收机制
java·开发语言·jvm
Warren983 小时前
Lua 脚本在 Redis 中的应用
java·前端·网络·vue.js·redis·junit·lua
艾伦~耶格尔7 小时前
【数据结构进阶】
java·开发语言·数据结构·学习·面试
爪洼传承人7 小时前
18- 网络编程
java·网络编程
smileNicky7 小时前
SpringBoot系列之从繁琐配置到一键启动之旅
java·spring boot·后端
祈祷苍天赐我java之术8 小时前
Java 迭代器(Iterator)详解
java·开发语言
David爱编程8 小时前
为什么必须学并发编程?一文带你看懂从单线程到多线程的演进史
java·后端
我命由我123458 小时前
软件开发 - 避免过多的 if-else 语句(使用策略模式、使用映射表、使用枚举、使用函数式编程)
java·开发语言·javascript·设计模式·java-ee·策略模式·js
long3168 小时前
java 策略模式 demo
java·开发语言·后端·spring·设计模式