拦截器和过滤器(原理&区别)

目录

一、拦截器

拦截器是什么

拦截器的使用

拦截器的实现

导入依赖

实现HandlerInterceptor接口

注册拦截器

拦截器的生命周期

拦截器的执行顺序

拦截器的生命周期

多个拦截器的执行流程

拦截器的实际使用

拦截器实现日志记录

实现接口幂等性校验

拦截器的性能优化

二、过滤器

过滤器是什么

过滤器的使用

过滤器的实现

创建过滤器

开启URL过滤

过滤器的生命周期

多个Filter的执行顺序

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

相同

底层原理

使用范围不同

触发时机不同

控制执行的顺序不同


一、拦截器

拦截器是什么

拦截器(Interceptor)是一种特殊的组件,它是基于反射进行实现。它可以在请求处理的过程中对请求和响应进行拦截和处理。拦截的是servlet 和 controller 之间的请求和响应。

拦截器的使用

  • 权限控制:拦截器可以在请求到达处理器之前进行权限验证,从而实现对不同用户的访问控制。

  • 日志记录:拦截器可以在请求处理过程中记录请求和响应的详细信息,便于后期分析和调试。

  • 接口幂等性校验:拦截器可以在请求到达处理器之前进行幂等性校验,防止重复提交。

  • 数据校验:拦截器可以在请求到达处理器之前对请求数据进行校验,确保数据的合法性。

  • 缓存处理:拦截器可以在请求处理之后对响应数据进行缓存,提高系统性能。

拦截器的实现

导入依赖

如果是使用的是springboot项目的话,直接导入这个依赖,spring项目需要导入 web 依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

实现HandlerInterceptor接口

要在SpringBoot中实现拦截器,首先需要创建一个类并实现HandlerInterceptor接口。HandlerInterceptor接口包含以下三个方法:

preHandle:在请求到达处理器之前执行,可以用于权限验证、数据校验等操作。如果返回true,则继续执行后续操作;如果返回false,则中断请求处理。

postHandle:在处理器处理请求之后执行,可以用于日志记录、缓存处理等操作。

afterCompletion:在视图渲染之后执行,可以用于资源清理等操作。

public class MainInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("我是处理之前!");
        return true;   //只有返回true才会继续,否则直接结束
    }
​
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("我是处理之后!");
    }
​
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("我是完成之后!");
    }
}

注册拦截器

要让拦截器生效,需要将其注册到InterceptorRegistry中。这可以通过实现WebMvcConfigurer接口并重写addInterceptors方法来实现。注册成功以后,我们还可以设置拦截的规则,拦截的路径,和不拦截的路径

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Autowired
    private MainInterceptor customInterceptor;
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 添加拦截器,并设置拦截路径
        registry.addInterceptor(customInterceptor)
                .addPathPatterns("/**").excludePathPatterns("/exclude/**");
    }
}

拦截器的生命周期

拦截器的执行顺序

当有多个拦截器时,它们的执行顺序取决于注册顺序。先注册的拦截器先执行,后注册的拦截器后执行。在请求处理过程中,拦截器的preHandle方法按注册顺序执行,而postHandle和afterCompletion方法按注册顺序的逆序执行。

拦截器的生命周期

拦截器的生命周期由Spring容器管理。当Spring容器启动时,拦截器会被实例化并初始化;当Spring容器关闭时,拦截器会被销毁。

多个拦截器的执行流程

当有多个拦截器时,它们的执行流程如下:

  1. 执行所有拦截器的preHandle方法,按注册顺序执行。如果某个拦截器的preHandle方法返回false,则中断请求处理,直接执行已执

  2. 拦截器的afterCompletion方法。

  3. 执行处理器的处理方法。

  4. 执行所有拦截器的postHandle方法,按注册顺序的逆序执行。

  5. 渲染视图。

  6. 执行所有拦截器的afterCompletion方法,按注册顺序的逆序执行。

拦截器的实际使用

拦截器实现日志记录

使用拦截器记录接口访问的记录,记录访问该接口的IP地址

@Component
public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("request.getRemoteHost() = " + request.getRemoteHost());
        System.out.println("request.getParameter(\"id\") = " + request.getParameter("id"));
        return true;   //只有返回true才会继续,否则直接结束
    }
​
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        request.getParameter("id");
        System.out.println("我是处理之后!");
    }
​
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("我是完成之后!");
    }
}
​

实现接口幂等性校验

拦截器可以在请求到达处理器之前进行幂等性校验,防止重复提交。以下是一个简单的幂等性校验示例:

public class IdempotentInterceptor implements HandlerInterceptor {
​
    private static final String IDEMPOTENT_TOKEN = "idempotentToken";
​
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String token = request.getHeader(IDEMPOTENT_TOKEN);
        if (StringUtils.isEmpty(token)) {
            throw new RuntimeException("Idempotent token is missing");
        }
        if (!checkIdempotentToken(token)) {
            throw new RuntimeException("Duplicate request");
        }
        return true;
    }
​
    private boolean checkIdempotentToken(String token) {
        // Check the token in the cache or database
        // Return true if the token is valid, false otherwise
    }
}
​
//在上述示例中,我们在preHandle方法中检查请求头中的幂等性令牌,如果令牌无效,则抛出异常并中断请求处理。

拦截器的性能优化

拦截器在请求处理过程中可能会影响系统性能,以下是一些性能优化策略:

  • 减少拦截器数量:尽量将相关功能集中到一个拦截器中,避免创建过多的拦截器。

  • 精确配置拦截规则:通过addPathPatterns和excludePathPatterns方法精确配置拦截规则,避免不必要的拦截。

  • 使用异步处理:在拦截器中使用异步处理,避免阻塞请求处理过程。

  • 使用缓存:在拦截器中使用缓存,减少对数据库或其他资源的访问。

二、过滤器

过滤器是什么

过滤器顾名思义就是对事物进行过滤的,在Web中的过滤器,当然就是对请求进行过滤,我们使用过滤器,就可以对请求进行拦截,然后做相应的处理,实现许多特殊功能。过滤器的实现是通过函数回调进行实现。

函数回调:它指的是将一个函数作为参数传递给另一个函数,并在特定事件发生时被调用执行的函数。这种方式使得在异步编程中,当某个操作完成后,系统能够调用预先定义好的回调函数来处理结果,从而避免阻塞程序的执行,提高程序的效率和响应速度。

过滤器的使用

  • 如登录控制

  • 权限管理

  • 过滤敏感词汇等

过滤器的实现

过滤器的配置比较简单,直接实现Filter 接口即可,也可以通过@WebFilter注解实现对特定URL拦截,看到Filter 接口中定义了三个方法。

创建过滤器

init() :该方法在容器启动初始化过滤器时被调用,它在 Filter 的整个生命周期只会被调用一次。注意:这个方法必须执行成功,否则过滤器会不起作用。 FilterConfig可以获取配置信息

doFilter() :容器中的每一次请求都会调用该方法, FilterChain 用来调用下一个过滤器 Filter。

默认就会传入Request和Response,这个参数封装了请求和响应,我们直接使用就行。ServletResquest和ServletResponse可以直接强转成HttpServletRequest和HttpServletResponse,然后使用相应的方法。

destroy(): 当容器销毁 过滤器实例时调用该方法,一般在方法中销毁或关闭资源,在过滤器 Filter 的整个生命周期也只会被调用一次

@Component
@WebFilter("/*") // 定义的过滤规则,只过滤对应的url请求
public class MyFilter implements Filter {
    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
​
        System.out.println("Filter 前置");
    }
​
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
​
        System.out.println("Filter 处理中");
        filterChain.doFilter(servletRequest, servletResponse);
    }
​
    @Override
    public void destroy() {
​
        System.out.println("Filter 后置");
    }
}
开启URL过滤

开启专门的Url过滤需要在启动类添加一个注解 @ServletComponentScan

@SpringBootApplication
@ServletComponentScan
public class InterceptorApplication {
    public static void main(String[] args) {
        SpringApplication.run(InterceptorApplication.class, args);
    }
}

过滤器的生命周期

  1. 初始化阶段(Initialization): 在 Servlet 容器启动时,会实例化所有的过滤器并调用其 init 方法进行初始化。在 init 方法中,过滤器可以进行一些初始化操作,例如读取配置文件、建立数据库连接等。该方法只会在过滤器实例化时调用一次。

  2. 请求处理阶段(Request Processing): 在请求到达 Servlet 前,过滤器可以对请求进行预处理。当请求与过滤器匹配时,Servlet 容器会调用过滤器的 doFilter 方法来处理请求。在 doFilter 方法中,过滤器可以执行一些操作,例如修改请求或响应内容、记录日志、验证权限等。如果请求不符合过滤器的条件,过滤器可以选择放行请求,将请求传递给下一个过滤器或目标 Servlet。

  3. 销毁阶段(Destruction): 在 Servlet 容器关闭时,会销毁所有的过滤器并调用其 destroy 方法进行清理。在 destroy 方法中,过滤器可以进行一些清理操作,例如释放资源、关闭连接等。该方法只会在过滤器销毁时调用一次。

多个Filter的执行顺序
  • 如果我们是在web.xml中配置的过滤器,那么过滤器的执行顺序就是<filter-mapping>在web配置的顺序,配置在上面那么就会先执行。

  • 如果我们是使用@WebFilter进行配置的,那么执行顺序就是字符比较顺序来执行,例如有2个过滤器,一个是AFilter,一个是BFilter,那么AFilter就会先执行。

  • 如果注解和xml混用,那么在web.xml中配置的会先执行。

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

相同

二者都是体现了AOP的思想,都可以实现诸如日志记录、登录鉴权等功能,但二者的不同点也是比较多的,接下来一一说明。

底层原理

拦截器是使用反射进行实现,过滤器是基于函数回调进行实现

使用范围不同

过滤器 实现的是 javax.servlet.Filter 接口,而这个接口是在Servlet规范中定义的,也就是说过滤器Filter 的使用要依赖于Tomcat等容器,导致它只能在web程序中使用。

拦截器(Interceptor) 它是一个Spring组件,并由Spring容器管理,并不依赖Tomcat等容器,是可以单独使用的。不仅能应用在web程序中,也可以用于ApplicationSwing等程序中。

触发时机不同

过滤器拦截器的触发时机也不同,我们看下边这张图

过滤器Filter是在请求进入容器后,但在进入servlet之前进行预处理,请求结束是在servlet处理完以后。

拦截器 Interceptor 是在请求进入servlet后,在进入Controller之前进行预处理的,Controller 中渲染了对应的视图之后请求结束。·

控制执行的顺序不同

拦截器:是先声明的拦截器 preHandle() 方法先执行,而postHandle()方法反而会后执行。

为什么?

两个方法中在调用拦截器数组 HandlerInterceptor[] 时,循环的顺序竟然是相反的。导致postHandle()preHandle() 方法执行的顺序相反。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值