过滤器(Filter)与拦截器(interceptor)的区别和使用方式

一、所属技术栈与依赖

过滤器:

                是 Servlet 规范的一部分,属于 Java EE 标准 ,依赖于 Servlet 容器。这意味着它主要在 Web 项目的 Servlet 环境中发挥作用,若脱离 Servlet 容器则无法正常运行。

拦截器:

                是 Spring MVC 框架提供的功能,属于 Spring 生态的一部分。它不依赖 Servlet 容器,只要存在 Spring 的环境就能使用,并且可以获取 IOC 容器中的各个 bean,方便调用业务逻辑。

二、执行位置与时机

过滤器:

                在 Servlet 容器层面工作。在请求进入容器后、到达 Servlet 之前进行预处理;请求结束返回时,在 Servlet 处理完后、返回给前端之前进行后处理 。例如在处理用户请求时,先经过过滤器进行诸如字符编码设置等预处理,再由 Servlet 处理业务逻辑,最后在响应返回前过滤器还可进行响应数据压缩等操作。

拦截器:

                在 Spring MVC 的 DispatcherServlet 处理请求的过程中工作。在请求进入 DispatcherServlet 之后、进入具体的处理方法之前进行预处理;在处理方法执行后、视图渲染之前可进行一些操作(如 postHandle 方法) ;在视图渲染完成后还能进行最终处理(如 afterCompletion 方法),并且可以获取到异常信息 。比如在用户请求访问某个 Controller 方法时,拦截器可以先进行权限判断,Controller 方法执行后可以对返回结果做一些调整,视图渲染后进行日志记录等。

三、实现原理

过滤器:

                基于回调函数实现。通过实现javax.servlet.Filter接口,重写init(初始化)、doFilter(处理过滤逻辑)、destroy(销毁)方法。在doFilter方法中,通过调用FilterChain对象的doFilter方法来将请求传递给下一个过滤器或目标资源,是一种基于函数调用链的回调机制 。

拦截器:

                基于反射(动态代理)实现。通常实现HandlerInterceptor接口,重写preHandle(方法执行前调用)、postHandle(方法执行后调用)、afterCompletion(视图页面渲染后调用) 方法。Spring 通过动态代理技术在运行时创建代理对象来调用这些方法,实现对目标方法的拦截和处理 。

四、功能能力与应用场景

过滤器:

  • 功能:可以访问原始的 HTTP 请求和响应,也能够修改请求和响应内容 。例如进行敏感词过滤、设置字符集编码、实现 URL 级别的权限访问控制、压缩响应信息等通用功能。
  • 应用场景:常用于解决跨域问题(CORS)、统一处理请求的字符编码、对所有请求进行安全相关的前置检查(如防 SQL 注入、防 XSS 攻击等)。

    拦截器:

    • 功能:能够访问 action 请求上下文、值栈里的对象 ,可以在方法执行的不同阶段进行干预。
    • 应用场景:更侧重于业务层面的判断和处理,如登录判断(判断用户是否登录)、权限判断(校验用户是否有权限访问资源)、日志记录(记录请求操作日志,包括用户 IP、访问时间等) 、性能监控(监控请求处理时长)等 。

    五、执行顺序与生命周期管理

    • 执行顺序:当过滤器和拦截器同时存在时,一般过滤器会先执行,然后才执行拦截器,最后进入真正要调用的方法 。不过具体顺序也可通过各自的配置方式进行调整。
    • 生命周期管理:过滤器的生命周期由 Servlet 容器管理,在容器初始化时调用一次初始化方法,后续每次请求按规则调用过滤方法,容器销毁时调用销毁方法 。拦截器则可以通过 IoC 容器来管理,可通过注入等方式获取其他 Bean 的实例,使用相对更灵活 。

    六、代码实现

    过滤器

    过滤器的简单实现有两个步骤

    1.创建过滤器,实现filter接口,重写方法,再过滤器类上添加@WebFilter注解,指定要拦截的url

    2.开启注解扫描,在启动类上加@ServletComponentScan注解,扫描被@WebFilter标记的类
     

    简单写个测试,controller层

    package com.dong.filterandinterceptor.controller;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @Slf4j
    @RestController
    public class FilterTest {
    
        @RequestMapping("/testFilter")
        public String test(){
            log.info("执行业务代码....");
            return "success";
        }
    }
    

    创建过滤器,拦截所有请求

    package com.dong.filterandinterceptor.filter;
    
    import jakarta.servlet.*;
    import jakarta.servlet.annotation.WebFilter;
    
    import java.io.IOException;
    
    @WebFilter(value = {"/*"})
    public class MyFilter implements Filter {
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            //过滤前执行
            System.out.println("MyFilter before...");
    
            //执行业务逻辑,决定是否放行
            chain.doFilter(request, response);
    
            //过滤后执行
            System.out.println("MyFilter after...");
        }
    }
    

    启动类上别忘了加@ServletComponentScan注解

    package com.dong.filterandinterceptor;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.web.servlet.ServletComponentScan;
    
    @ServletComponentScan
    @SpringBootApplication
    public class FilterAndInterceptorApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(FilterAndInterceptorApplication.class, args);
        }
    
    }
    

    访问localhost:8088/testFilter,可以看到在进入controller层先进入过滤器中

    拦截器

    拦截器要比过滤器步骤多一点

    1.创建拦截器类,实现HandlerInterceptor接口,在该类上标记@Component注解,纳入IOC容器

    2.创建配置类,实现WebMvcConfigurer接口,在该类上加上@Configuration注解,标记配置类

    拦截器,在过滤器放行后执行到达拦截器的preHandle方法,返回true,才到达Controller层

    业务执行完之后,执行拦截器的postHandle方法,然后再是afterCompletion方法,配置了过滤器的话,最后就执行过滤器doFilter方法后的代码

    package com.dong.filterandinterceptor.interceptor;
    
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    import org.springframework.stereotype.Component;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    @Component
    public class MyInterceptor implements HandlerInterceptor{
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("MyInterceptor before...");
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("MyInterceptor after...");
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("MyInterceptor afterCompletion...");
        }
    }
    

    配置类,将创建的拦截器加入拦截器链中,拦截所有请求,这里与过滤器不同,是 /**

    package com.dong.filterandinterceptor.config;
    
    import com.dong.filterandinterceptor.interceptor.MyInterceptor;
    import org.springframework.beans.factory.annotation.Autowired;
    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 MyConfig implements WebMvcConfigurer {
    
        @Autowired
        private MyInterceptor myInterceptor;
    
        //配置拦截器
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(myInterceptor).addPathPatterns("/**");
        }
    }
    

    执行结果,这里我过滤器和拦截器都配置了

    这是过滤器和拦截器同时存在的流程图

    还有更复杂的用法,配置了多个拦截器,他们之间的执行顺序又会有所不同

    小总结:过滤器的拦截范围要比拦截器大,过滤器拦截所有浏览器输入的所有请求,而拦截器仅针对controller上拦截的url资源。

    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值