Web组件之 Filter(过滤器)& HandlerInterceptor

发布于:2024-11-02 ⋅ 阅读:(149) ⋅ 点赞:(0)

在这里插入图片描述


JavaWeb的三大组件

组件 作用 实现接口
Servlet Server Applet小应用程序,在JavaWeb中主要做为控制器来使用 可以处理用户的请求并且做出响应 javax.servlet.Servlet
Filter 过滤器,对用户发送的请求或响应进行集中处理,实现请求的拦截 javax.servlet.Filter
Listener 监听器,在Web执行过程中,监听一些事件,当相应事件发生时, 进行处理 javax.servlet.XxxListener 每个事件有一个接口

Filter 概述

过滤器——Filter,它是 JavaWeb 三大组件之一。另外两个是ServletListener
它是在2000年发布的Servlet2.3规范中加入的一个接口。是Servlet规范中非常实用的技术。
它可以对web应用中的所有资源进行拦截,并且在拦截之后进行一些特殊的操作。
常见应用场景:URL级别的权限控制;过滤敏感词汇;中文乱码问题等等。

过滤器的编写

@WebFilter(filterName = "FilterDemo2", urlPatterns = "/*")
public class FilterDemo1 implements Filter {
    /**
     * 过滤器的核心方法
     * @param request
     * @param response
     * @param chain
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
    throws IOException, ServletException {
        /**
         * 如果不写此段代码,控制台会输出两次:FilterDemo1拦截到了请求。
         */
        HttpServletRequest req = (HttpServletRequest) request;
        String requestURI = req.getRequestURI();
        if (requestURI.contains("favicon.ico")) {
            return;
        }
        System.out.println("FilterDemo1拦截到了请求");
    }
}

过滤器 API

Filter

方法 说明
void init(FilterConfig filterConfig) 过滤器对象创建的时候调用的方法
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 执行过滤的方法,每次访问被拦截的资源都会执行该方法
void destory() 过滤器销毁的时候调用的方法

注意:doFilter第三个参数:FilterChain 表示过滤器链接口。

​ 放行:使用FilterChain 对象调用 FilterChain 中的方法:chain.doFilter(request,response);即可以让浏览器访问服务器资源

​ 不放行,那么不写上述代码,即不让浏览器访问服务器资源


返回类型 方法 简介

  • void init(FilterConfig conf) 用于执行过滤器的初始化工作,web容器会在web项目启动时自动调用该方法

  • void doFilter(ServletRequest request,SerlvetResponse response,FilterChain chain) 当请求和响应被过滤器拦截后,都会交给 doFilter 来处理:其中两个参数分别是被拦截 request 和 response 对象,可以使用 chaindoFliter 方法来放行。

  • void destroy() 用于释放关闭Filter对象打开的资源,在web项目关闭时,由web容器自动调用该方法。

Filter_API.png
Filter_API2.png

FilterConfig

FilterConfig_API.png

FilterChain

FilterChain_API.png

生命周期

出生当应用加载的时候执行实例化和初始化方法。

活着只要应用一直提供服务,对象就一直存在。

死亡当应用卸载时,或者服务器宕机时,对象消亡。

Filter 的实例对象在内存中也只有一份。所以也是单例的。


拦截路径(重点)

在开发时,我们可以指定过滤器的拦截路径来定义拦截目标资源的范围

  • 精准匹配 用户访问指定目标资源(/demo01.html)时,过滤器进行拦截

  • 目录匹配 用户访问指定目录下(/user/*)所有资源时,过滤器进行拦截

  • 后缀匹配 用户访问指定后缀名(*.html)的资源时,过滤器进行拦截,不能加/

  • 匹配所有 用户访问该网站所有资源(/*)时,过滤器进行拦截 掌握

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


//@WebFilter("/xx")
//@WebFilter({"/xx","/yy"})
@WebFilter("/xx") // 精准匹配
//@WebFilter("/user/*") // 目录匹配
//@WebFilter("*.do") // 后缀匹配
//@WebFilter("/*") // 匹配所有
public class MyFilter3 implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("doFilter");
    }

    @Override
    public void destroy() {

    }
}
1.精确匹配:拦截哪个资源就书写哪个资源 /demo01.html /demo02Servlet 掌握
2.目录匹配:/xxx/*  拦截以xxx开始的所有资源
    举例:/user/* 只要浏览器访问的资源路径以user开始都会执行该过滤器
3.后缀名匹配:*.xxx 拦截以xxx结尾的资源,这里不能加/ 。      
		举例:*.html:拦截所有以html结尾的资源
4.匹配所有路径:/* 掌握

过滤器链


在一次请求中,若我们请求匹配到了多个filter,通过请求就相当于把这些filter串起来了,形成了过滤器链。

问题:如果多个过滤器都对相同路径进行匹配,执行顺序该是什么?

  1. 如果同一个包下多个过滤器拦截同一个资源,那么按照过滤器类的名字字母升序来依次执行(针对注解开发), 例如: AFilter ---- BFilter 前提:多个过滤器过滤同一个资源,并且多个过滤器是在同一包下。

  2. 执行完拦截资源后依次在按照相反顺序执行

  3. 如果是 xml 配置方式,那么按照从上往下配置的顺序执行

  4. 如果是 Spring 环境, 可以使用注解 @Order(1) 指定顺序. (@Order 注解的值越小,过滤器的执行顺序越靠前。)

下面演示同一包下的情况:

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebFilter("/apple")
public class AFilter implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;
        System.out.println("AFilter....放行前");
        chain.doFilter(request, response);
        System.out.println("AFilter....放行后");
    }

    public void init(FilterConfig config) throws ServletException {

    }
}
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebFilter("/apple")
public class BFilter implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;
        System.out.println("BFilter....放行前");
        chain.doFilter(request, response);
        System.out.println("BFilter....放行后");
    }

    public void init(FilterConfig config) throws ServletException {

    }
}
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/apple")
public class AppleServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("AppleServlet....");
    }
}

在这里插入图片描述
在这里插入图片描述

小结

  1. 如果同一个包下多个过滤器拦截同一个资源,那么按照过滤器类的名字字母升序来依次执行(针对注解开发), 例如: AFilter ---- BFilter

  2. 执行完拦截资源后依次在按照相反顺序执行

  3. 如果是 xml 配置方式,那么按照从上往下配置的顺序执行

  4. 如果是 Spring 环境, 可以使用注解 @Order(1) 指定顺序. (@Order 注解的值越小,过滤器的执行顺序越靠前。)


过滤器核心方法的细节

/**
 * 过滤器的核心方法
 */
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    /**
     * 如果不写此段代码,控制台会输出两次:FilterDemo1拦截到了请求。
    HttpServletRequest req = (HttpServletRequest) request;
    String requestURI = req.getRequestURI();
    if (requestURI.contains("favicon.ico")) {
        return;
    }*/
    System.out.println("FilterDemo1拦截到了请求");
    //过滤器放行
    chain.doFilter(request, response);
    System.out.println("FilterDemo1放行之后,又回到了doFilter方法");
}

测试运行结果,我们发现过滤器放行之后执行完目标资源,仍会回到过滤器中:
filter_demo8.png


多个过滤器执行顺序

运行结果

filter_multi_demo.png



Filter 在 SpringBoot 使用

Filter 在 SpringBoot 使用演示

定义 n 个过滤器

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

public class FilterDemo1 implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
    throws IOException, ServletException {
        System.out.println("FilterDemo1...请求进来了");
        chain.doFilter(request, response);
        System.out.println("FilterDemo1...请求走了");
    }
}
import javax.servlet.*;
import java.io.IOException;

public class FilterDemo2 implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        System.out.println("FilterDemo2...请求进来了");
        chain.doFilter(request, response);
        System.out.println("FilterDemo2...请求走了");
    }
}

配置类加如下配置 :

@Bean
public FilterRegistrationBean<FilterDemo1> filterDemo1() {
    FilterRegistrationBean<FilterDemo1> registrationBean = new FilterRegistrationBean<>();
    registrationBean.setFilter(new FilterDemo1());
    registrationBean.addUrlPatterns("/*");
    registrationBean.setOrder(1); // 设置顺序
    return registrationBean;
}

@Bean
public FilterRegistrationBean<FilterDemo2> filterDemo2() {
    FilterRegistrationBean<FilterDemo2> registrationBean = new FilterRegistrationBean<>();
    registrationBean.setFilter(new FilterDemo2());
    registrationBean.addUrlPatterns("/*");
    registrationBean.setOrder(2); // 设置顺序
    return registrationBean;
}

就 OK 了!


但是 在 SpringBoot 项目中有自己的 拦截器实现方式, HandlerInterceptor: HandlerInterceptor 是 Spring MVC 框架的一部分,它工作在 Spring MVC 层。它主要用于拦截 Spring MVC 控制器的请求,可以在控制器处理请求之前、之后以及视图渲染之前执行操作。


SpringBoot 项目拦截器使用演示

在 Spring Boot 项目中,自定义拦截器可以通过实现 HandlerInterceptor 接口并在 Spring 容器中注册该拦截器来完成。以下步骤将指导你如何创建和配置一个自定义拦截器:

步骤 1:创建拦截器类
首先,创建一个实现 HandlerInterceptor 接口的类。这个类需要重写三个方法:

preHandle: 在请求处理之前执行
postHandle: 在请求处理之后执行,但在视图渲染之前
afterCompletion: 在请求完成之后执行
示例代码如下:

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

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

@Component
public class MyInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("前处理方法:请求路径 = " + request.getRequestURI());
        // 返回 true 继续处理,返回 false 中断请求
        return 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("请求完成后执行");
    }
}

步骤 2:注册拦截器
接下来,需要在 Spring Boot 应用中注册这个拦截器。你可以通过实现 WebMvcConfigurer 接口的配置类来完成注册。

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

    @Autowired
    private MyInterceptor myInterceptor;

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

步骤 3:测试拦截器
启动 Spring Boot 应用并发送请求。你应该能在控制台看到拦截器的输出信息。根据 preHandle 的返回值,可以控制请求的继续或中断。

总结
通过上述步骤,你可以在 Spring Boot 项目中自定义和配置拦截器来处理请求。在实际应用中,可以根据需要对拦截器逻辑进行扩展,比如添加日志记录、请求鉴权等功能。


HandlerInterceptor 与 Filter 是什么关系

在 Java Web 开发中,HandlerInterceptor 和 Filter 都是用于处理请求的拦截器,但它们的作用范围和使用方式有所不同。以下是它们的区别和关系:

1.作用范围

  • Filter: Filter 是 Servlet 规范的一部分,它工作在 Servlet 容器层,用于拦截所有进入 Servlet 容器的请求。它可以在请求到达 Servlet 或控制器之前或之后执行一些操作。

  • HandlerInterceptor: HandlerInterceptor 是 Spring MVC 框架的一部分,它工作在 Spring MVC 层。它主要用于拦截 Spring MVC 控制器的请求,可以在控制器处理请求之前、之后以及视图渲染之前执行操作。

  1. 执行顺序
  • Filter: Filter 的执行顺序早于 HandlerInterceptor。当一个请求到达时,Filter 会先被调用,然后请求才会被传递到 Spring MVC 层。

  • HandlerInterceptor: HandlerInterceptor 的执行顺序晚于 Filter。它会在请求到达 Spring MVC 控制器之前被调用。

  1. 使用场景
  • Filter:

    • 用于处理一些与 Web 容器相关的操作,例如编码转换、日志记录、权限验证等。
    • 适用于非 Spring 应用或希望在进入 Spring 层之前执行的操作。
  • HandlerInterceptor:

    • 用于处理一些与 Spring MVC 相关的操作,例如身份验证、日志记录、性能监控、请求预处理等。
    • 适用于 Spring 应用中,特别是在需要访问 Spring 上下文(如 ApplicationContext)或 Spring 管理的 Bean 时。





网站公告

今日签到

点亮在社区的每一天
去签到