Spring的拦截器和过滤器的区别

拦截器和过滤器的区别

1. 定义与归属

维度 Filter(过滤器) Interceptor(拦截器)
属于 Servlet 规范(JavaEE) SpringMVC 框架机制
管理 由 Servlet 容器(如 Tomcat)管理 由 Spring 容器管理
本质 是一种基于函数回调的请求预处理机制 是基于 Java 反射机制的请求拦截器
依赖注入 不支持直接注入 Spring Bean(需特殊配置) 支持直接注入 Spring Bean

2. 拦截范围

维度 Filter Interceptor
作用范围 所有请求(包括静态资源,如 HTML、JS、CSS、图片) 只拦截经过 DispatcherServlet 的请求,即控制器 Controller 相关请求
典型请求 包括 API、静态资源、文件上传下载等 仅 Controller 层及之后流程

3. 生命周期和调用时机

Filter 生命周期:

  • 服务器启动时初始化。

  • 请求到达 Servlet 之前执行。

  • 请求完成后也可进行后处理(通过 FilterChain.doFilter 前后分别处理)

工作流程:

请求 → Filter  → DispatcherServlet → Interceptor → Controller

Interceptor 生命周期:

  • DispatcherServlet 初始化时注册拦截器。

  • 在 Controller 方法调用前/后执行,更靠近业务逻辑层。

具有 3 个关键回调方法:

  • preHandle():请求处理前调用(可以中断请求)

  • postHandle():请求处理后,视图渲染前调用

  • afterCompletion():整个请求完成后调用(用于资源清理)

工作流程:

请求 → Filter → DispatcherServlet → preHandle() → Controller → postHandle() → 渲染 → afterCompletion()

4. 作用和功能定位

维度 Filter 的主要作用 Interceptor 的主要作用
常用于 编码设置、跨域处理(CORS)、日志记录、安全校验、统一异常处理 登录鉴权、权限控制、业务日志记录、参数加工
粒度 粗粒度(请求级别处理) 细粒度(控制器方法级别处理)
静态资源处理 可以直接处理(比如压缩、缓存控制) 默认不处理(静态资源跳过 DispatcherServlet)

总结

flowchart TD A(请求进入服务器) --> B(Filter链处理) B --> C(DispatcherServlet) C --> D(Interceptor.preHandle) D --> E(Controller方法执行) E --> F(Interceptor.postHandle) F --> G(View解析) G --> H(Interceptor.afterCompletion) H --> I(响应返回客户端)

Demo

1. 自定义 Filter - LogFilter.java

import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component
@Order(1)  // 可以指定顺序,数字越小优先执行
public class LogFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        System.out.println("[LogFilter] 请求路径: " + req.getRequestURI());
        chain.doFilter(request, response);
    }
}

2. 自定义 Interceptor - LoginInterceptor.java

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

public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        String token = request.getHeader("Authorization");
        if (!"valid-token".equals(token)) {
            System.out.println("[LoginInterceptor] 未登录,拦截请求!");
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return false;
        }
        System.out.println("[LoginInterceptor] 登录验证通过");
        return true;
    }
}

3. 配置注册 - WebConfig

import org.example.springmvclearn.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/api/**") // 只拦截 /api 开头的路径
                .excludePathPatterns("/api/public/**"); // 排除不需要登录的接口
    }
}

4. 测试 Controller 示例

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class TestController {

    @GetMapping("/public/hello")
    public String publicHello() {
        return "这是公开接口,不需要登录";
    }

    @GetMapping("/private/hello")
    public String privateHello() {
        return "这是私有接口,需要登录验证";
    }
}

5. 测试

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
class SpringMvcLearnApplicationTests {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void testPublicHello() throws Exception {
        mockMvc.perform(get("/api/public/hello"))
                .andExpect(status().isOk())
                .andExpect(content().string("这是公开接口,不需要登录"));
    }

    @Test
    public void testPrivateHelloWithoutToken() throws Exception {
        mockMvc.perform(get("/api/private/hello"))
                .andExpect(status().isUnauthorized());
    }

    @Test
    public void testPrivateHelloWithValidToken() throws Exception {
        mockMvc.perform(get("/api/private/hello")
                        .header("Authorization", "valid-token"))
                .andExpect(status().isOk())
                .andExpect(content().string("这是私有接口,需要登录验证"));
    }

}

6.结果

[LogFilter] 请求路径: /api/public/hello

[LogFilter] 请求路径: /api/private/hello
[LoginInterceptor] 未登录,拦截请求!

[LogFilter] 请求路径: /api/private/hello
[LoginInterceptor] 登录验证通过
posted @ 2025-04-27 21:42  Eiffelzero  阅读(87)  评论(0)    收藏  举报