本文最后更新于 2024-04-01,欢迎来到我的Blog! https://www.zpeng.site/

java过滤器拦截器

1.介绍

过滤器

  • 过滤器是Tomcat中的组件

  • 过滤器是过滤Servlet请求的

  • 过滤的执行时机

  • 1)Servlet执行之前

  • 2)Servlet执行之后

拦截器

  • 拦截器是SpringMVC中的组件

  • 拦截器是拦截Controller请求的

  • 拦截器的执行时机

  • 1)在DispatcherServlet执行之后,Controller执行之前执行

  • 2)在Controller执行之后,DispatcherServlet执行之前执行

  • 3)在DispatcherServlet执行之后,视图渲染完成之后执行

2.过滤器

2.1简介

  • 过滤器是Servlet规范中的一部分,它在Spring框架之外,由Servlet容器(如Tomcat)管理。

  • 过滤器在请求到达Servlet之前执行,并且可以在响应返回给客户端之前执行。

  • 过滤器通常用于执行一些跨多个请求和响应的任务,如日志记录、身份验证、编码转换等。

  • 过滤器通过实现javax.servlet.Filter接口来定义。

2.2Filter原理

Filter(过滤器)在 Web 应用程序中具有生命周期,包括初始化和销毁两个阶段。其生命周期由 Web 容器负责管理。

  1. 初始化阶段:

    • 当 Web 容器启动时,会检测并创建配置文件(如 web.xml)中定义的 Filter。

    • 创建 Filter 实例,并调用 init() 方法进行初始化。

    • 在 init() 方法中,可以进行一些一次性的初始化操作,如加载配置文件、创建资源等。

  2. 请求处理阶段:

    • 在 Web 应用程序启动后,Filter 可以拦截、处理和修改来自客户端的请求和服务器的响应。

    • 当有请求到达时,Web 容器会调用 Filter 的 doFilter() 方法,并传递请求和响应对象。

    • 在 doFilter() 方法中,可以编写过滤逻辑,检查请求、修改请求或响应的内容,以及将请求传递给下一个 Filter 或目标资源。

    • 如果存在多个 Filter,它们会按照 Filter 链的顺序依次执行。

  3. 销毁阶段:

    • 当 Web 容器关闭或重启时,会销毁已创建的 Filter 实例。

    • 调用 Filter 的 destroy() 方法进行销毁。

    • 在 destroy() 方法中,可以执行一些清理操作,如释放资源、关闭连接等。

需要注意的是,在 Filter 的生命周期中,init()和destroy()方法仅在 Filter 实例创建和销毁时调用一次,而doFilter()方法每次请求被拦截时都会被调用。

可以在 Filter 的init()方法中进行一些初始化配置的操作,如读取配置文件、初始化资源等。在destroy()方法中可以释放资源,做一些清理工作。

利用 Filter 的生命周期,开发人员可以在请求处理过程中实现自定义的逻辑,并对请求和响应进行处理、修改或增强。

Filter 有如下几个方法:

  • void init(FilterConfig filterConfig) 用于完成过滤器的初始化。

  • doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) 实现过滤功能,将该方法对每个请求增加额外的处理。

  • void destroy() 用于过滤器销毁前,完成某些资源的回收。

2.3使用

测试用controller

package com.example.useragent.demos.web;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class BasicController {

    // http://127.0.0.1:8080/hello?name=lisi
    @RequestMapping("/hello")
    @ResponseBody
    public String hello(@RequestParam(name = "name", defaultValue = "unknown user") String name) {
        System.out.println("BasicController.hello");
        System.out.println("BasicController.hello");
        System.out.println("BasicController.hello");
        System.out.println("BasicController.hello");
        return "Hello " + name;
    }
}

FirstFilter

package com.example.useragent.demos.web.filter;


import javax.servlet.*;
import java.io.IOException;

public class FirstFilter implements Filter {


    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
        System.out.println("FirstFilter 初始化完成");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("------对 First request 进行过滤 --------");

        //下面这行代码就是放行
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("------对 First response 进行过滤 --------");

    }

    @Override
    public void destroy() {
        Filter.super.destroy();
        System.out.println("firstFilter 已销毁");
    }
}

SecondFilter

package com.example.useragent.demos.web.filter;



import javax.servlet.*;
import java.io.IOException;

//@WebFilter(urlPatterns="/*")
public class SecondFilter implements Filter {


    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("Second Filter 初始化完成");
        Filter.super.init(filterConfig);
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("------对 Second request 进行过滤 --------");

        //下面这行代码就是放行
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("------对 Second response 进行过滤 --------");

    }

    @Override
    public void destroy() {
        System.out.println("Second Filter 已销毁");
        Filter.super.destroy();
    }
}

WebConfig 配置文件方式

也可用@WebFilter注解方式

package com.example.useragent.demos.web.filter;


import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;

@Configuration
public class WebConfig {
    @Bean
    public FilterRegistrationBean firstFilterRegistrationBean() {
        FilterRegistrationBean<Filter> filterFilterRegistrationBean = new FilterRegistrationBean<>();
        // 对哪些路径进行过滤
        filterFilterRegistrationBean.addUrlPatterns("/*");
        filterFilterRegistrationBean.setOrder(1); // 设置优先级 ,数字越小,优先级越高
        FirstFilter filter = new FirstFilter(); // 绑定过滤器
        filterFilterRegistrationBean.setFilter(filter);
        return filterFilterRegistrationBean;
    }

    @Bean
    public FilterRegistrationBean secondFilterRegistrationBean() {
        FilterRegistrationBean<Filter> filterFilterRegistrationBean = new FilterRegistrationBean<>();
        filterFilterRegistrationBean.addUrlPatterns("/hello");
        filterFilterRegistrationBean.setOrder(2);  // 设置优先级
        // 绑定过滤器
        SecondFilter filter = new SecondFilter();
        filterFilterRegistrationBean.setFilter(filter);
        return filterFilterRegistrationBean;
    }

}

在每个过滤器的doFilter()方法中,可以调用 FilterChain 的doFilter()方法来传递请求和响应给下一个过滤器,或者传递给目标资源。如果没有调用doFilter()方法,过滤器链将会被中断,请求将不会继续传递。

2.4测试

http://127.0.0.1:8080/hello?name=zpeng

2.5执行顺序

我们可以看见 FirstFilter 先进行过滤,然后交给 SecondFilter ,然后访问资源,然后 SecondFilter 对响应进行过滤,然后 FirstFilter 对响应进行过滤。图示如下:  

3.拦截器

3.1简介

  • 拦截器是Spring框架的一部分,它依赖于Spring的AOP模块。

  • 拦截器在Spring的DispatcherServlet中工作,用于拦截请求并在控制器之前或之后执行。

  • 拦截器通常用于记录日志、执行安全检查、数据绑定等操作。

  • 拦截器通过实现org.springframework.web.servlet.HandlerInterceptor接口或扩展org.springframework.web.servlet.handler.HandlerInterceptorAdapter类来定义。

它依赖于web框架,在SpringMVC中就是依赖于SpringMVC框架。在实现上,基于Java的反射机制,属于面向切面编程(AOP)的一种运用,就是在service或者一个方法前,调用一个方法,或者在方法后,调用一个方法,比如动态代理就是拦截器的简单实现,在调用方法前打印出字符串(或者做其它业务逻辑的操作),也可以在调用方法后打印出字符串,甚至在抛出异常的时候做业务逻辑的操作。由于拦截器是基于web框架的调用,因此可以使用Spring的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个controller生命周期之内可以多次调用。但是缺点是只能对controller请求进行拦截,对其他的一些比如直接访问静态资源的请求则没办法进行拦截处理。

3.2Interceptor 原理

preHandle()

这个方法在Controller方法执行之前执行,在该方法中可以做一些权限校验的操作。如果希望该拦截器对请求进行拦截后,还要调用其他的拦截器或者控制器去处理业务,则返回true;如果希望该拦截器拦截请求后,不需要再调用其他拦截器或控制器去处理业务,则返回false。

postHandle()

这个方法在 Controller 方法执行之后,渲染视图之前执行,可以对Controller方法的返回值进行处理。

afterCompletion()

这个方法在 Controller方法执行之后,视图渲染完毕之后执行,也即DispatcherServlet完全处理完请求之后执行,可以做一些资源清理的操作。

  • 服务器接收到浏览器发出的请求

  • 执行拦截器的preHandle()方法

  • 执行Controller处理请求

  • 执行拦截器的postHandle()方法

  • DispatcherServlet渲染视图

  • 执行拦截器的afterCompletion()方法

3.3使用

FirstInterceptor

package com.example.useragent.demos.web.interceptor;

import org.springframework.context.annotation.Bean;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Service
public class FirstInterceptor implements HandlerInterceptor {
    /**
     * 该方法在DispatcherServlet执行之后,Controller执行之前执行
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("First Interceptor --> preHandler()!");
        // 如果返回false,则停止流程,api不会被调用
        return true;
    }
    /**
     * 该方法在Controller执行之后,DispatcherServlet执行之前执行
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
        System.out.println("First Interceptor --> postHandle()!");
    }
    /**
     * 该方法在DispatcherServlet渲染视图之后,执行
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
        System.out.println("First Interceptor --> afterCompletion()!");
    }
}

SecondInterceptor

package com.example.useragent.demos.web.interceptor;

import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Service
public class SecondInterceptor implements HandlerInterceptor {
    /**
     * 该方法在DispatcherServlet执行之后,Controller执行之前执行
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("Second Interceptor --> preHandler()!");
        // 如果返回false,则停止流程,api不会被调用
        return true;
    }
    /**
     * 该方法在Controller执行之后,DispatcherServlet执行之前执行
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
        System.out.println("Second Interceptor --> postHandle()!");
    }
    /**
     * 该方法在DispatcherServlet渲染视图之后,执行
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
        System.out.println("Second Interceptor --> afterCompletion()!");
    }
}

InterceptorConfig

package com.example.useragent.demos.web.interceptor;

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 InterceptorConfig implements WebMvcConfigurer {

    @Autowired
    private FirstInterceptor firstInterceptor;
    @Autowired
    private SecondInterceptor secondInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 添加拦截器
        registry.addInterceptor(firstInterceptor)
                .addPathPatterns("/**"); // 拦截所有请求路径
        registry.addInterceptor(secondInterceptor)
                .addPathPatterns("/**"); // 拦截所有请求路径
        //.excludePathPatterns("/login", "/error"); // 排除某些路径
    }
}

3.4测试

http://127.0.0.1:8080/hello?name=zpeng

3.5执行顺序

First Interceptor --> preHandler()!
Second Interceptor --> preHandler()!
BasicController.hello
BasicController.hello
BasicController.hello
BasicController.hello
Second Interceptor --> postHandle()!
First Interceptor --> postHandle()!
Second Interceptor --> afterCompletion()!
First Interceptor --> afterCompletion()!

服务器接收到浏览器发出的请求

  • 执行第一个拦截器的preHandle()方法

  • 执行第二个拦截器的preHandle()方法

  • 调用Controller处理请求

  • 执行第二个拦截器的postHandle()方法

  • 执行第一个拦截器的postHandle()方法

  • DispatcherServlet渲染视图

  • 执行第二个拦截器的afterCompletion()方法

  • 执行第一个拦截器的afterCompletion()方法

  • 当第一个拦截器的preHandle()方法返回false时,不会继续执行,请求结束。

  • 当第一个拦截器的preHandle()方法返回true,第二个拦截器的preHandle()方法返回false时,会执行第一个拦截器和第二个拦截器的preHandle(),以及第一个拦截器的afterCompletion()方法,之后请求结束。

  • 当前拦截器的preHandle()方法返回false,会执行之前拦截器的preHandle()、当前拦截器的preHandle(),以及之前拦截器的afterCompletion()

4.过滤器拦截器一起

4.1介绍

拦截器:对请求在handler【Controller】前后进行处理,属于应用上下文。

过滤器:对请求在进入后Servlet之前或之后进行处理,允许交换请求和响应对象。

拦截器和过滤器执行顺序:

1、Filter.init();

2、Filter.doFilter(); before doFilter

3、HandlerInterceptor.preHandle();

4、Controller方法执行

5、HandlerInterceptor.postHandle();

6、DispatcherServlet视图渲染

7、HandlerInterceptor.afterCompletion();

8、Filter.doFilter(); after doFilter

9、Filter.destroy();

4.2测试

都打开看结果