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界面略

相关推荐
fqq323 分钟前
算法入门----排序(选择,冒泡,插排)
java·数据结构·算法
小小鸭程序员1 小时前
Spring Boot事务管理详解(附银行转账案例)
java·spring boot·spring·github·intellij-idea
kill bert1 小时前
第30周Java分布式入门 docker
java·分布式·docker
云之渺1 小时前
java115
java
林川的邹1 小时前
如何根据场景判断是使用ArrayList还是LinkedList?
java·后端
阿绵1 小时前
拦截器和过滤器详解
java·spring·过滤器·拦截器
十六ᵛᵃᵉ2 小时前
day3_Flink基础
android·java·flink
黄雪超2 小时前
Java多线程与高并发专题——Condition 和 wait/notify的关系
java·开发语言·并发编程
茶本无香2 小时前
Optional的stream方法,flatMap, filter应用
java·stream·filter·optional·flatmap
罗婕斯特2 小时前
Scala中while和for循环
java·开发语言·前端