Java Filter 过滤器详解

一、Filter过滤器概述

Filter(过滤器) 是 Java Servlet 规范中的核心组件之一,用于在请求到达目标资源(如 Servlet、JSP)之前或响应返回客户端之后执行预处理/后处理操作。它类似于现实生活中的「筛子」,可以对 HTTP 请求和响应进行拦截和加工。

Filter的主要作用是实现通用功能的集中化处理,例如:

  • 身份验证与权限控制:检查用户是否登录或是否有权限访问资源。
  • 日志记录与监控:记录请求信息(如URL、参数、时间等)。
  • 数据编码处理:统一设置请求和响应的字符编码(如UTF-8)。
  • 数据压缩与优化:对响应内容进行压缩,减少传输数据量。
  • 敏感词过滤:过滤请求中的敏感内容。
  • 异常处理:捕获并处理请求处理过程中的异常。

二、Filter的核心概念

1. Filter的工作流程

Filter的拦截过程分为以下步骤:

  1. 拦截请求:当客户端请求到达服务器时,Servlet容器(如Tomcat)会检查该请求是否匹配已配置的Filter。
  2. 预处理请求:Filter通过doFilter()方法对请求进行预处理(如修改请求参数、验证身份)。
  3. 传递请求:调用FilterChain.doFilter()将请求传递给下一个Filter或目标资源(Servlet/JSP)。
  4. 处理响应:目标资源处理完成后,Filter有机会对响应进行后处理(如压缩响应内容、添加响应头)。
  5. 返回响应:最终响应返回客户端。

2. Filter的生命周期

  • init():Filter实例化后,Servlet容器调用此方法进行初始化,通常在此配置资源(如数据库连接、日志初始化)。
  • doFilter():每次请求被拦截时调用,核心逻辑在此实现。
  • destroy():Filter销毁前调用,用于释放资源(如关闭连接)。

三、Filter 开发步骤

1. 创建 Filter 类

实现 javax.servlet.Filter 接口,重写三个方法:

public class MyFilter implements Filter {
    
    // 初始化方法(服务器启动时执行一次)
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 可读取 web.xml 中的初始化参数
        String param = filterConfig.getInitParameter("paramName");
    }

    // 核心过滤方法(每次请求都会执行)
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        
        // 1. 预处理请求
        System.out.println("Before processing request");
        
        // 2. 放行请求到下一个过滤器或目标资源
        chain.doFilter(request, response);
        
        // 3. 后处理响应
        System.out.println("After processing response");
    }

    // 销毁方法(服务器关闭时执行)
    @Override
    public void destroy() {
        // 释放资源
    }
}

2. 配置 Filter

方式一:web.xml 配置

<filter>
    <filter-name>myFilter</filter-name>
    <filter-class>com.example.MyFilter</filter-class>
    <init-param>
        <param-name>paramName</param-name>
        <param-value>value</param-value>
    </init-param>
</filter>

<filter-mapping>
    <filter-name>myFilter</filter-name>
    <url-pattern>/*</url-pattern>  <!-- 拦截所有请求 -->
    <dispatcher>REQUEST</dispatcher> <!-- 默认拦截直接请求 -->
</filter-mapping>

方式二:注解配置(Servlet 3.0+)

@WebFilter(
    urlPatterns = "/*",
    initParams = {
        @WebInitParam(name = "paramName", value = "value")
    },
    dispatcherTypes = {DispatcherType.REQUEST, DispatcherType.FORWARD}
)
public class MyFilter implements Filter {
    // ...
}

四、示例代码演示

1.MyFilter

package com.example;

import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;

import java.io.IOException;

// 过滤所有以 .do 结尾的请求
@WebFilter("*.do")
public class MyFilter implements Filter {

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

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        System.out.println("请求进入 MyFilter,开始处理请求...");
        // 继续执行后续的过滤器或 Servlet
        chain.doFilter(request, response);
        System.out.println("请求离开 MyFilter,响应已处理完成");
    }

    @Override
    public void destroy() {
        System.out.println("MyFilter 销毁完成");
    }
}

2. MyServlet

package com.example;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.PrintWriter;

// 处理 /test.do 请求
public class MyServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        out.println("<html><body>");
        out.println("<h2>这是 MyServlet 处理的请求</h2>");
        out.println("</body></html>");
    }
}

3.web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!-- 配置过滤器 -->
    <filter>
        <filter-name>MyFilter</filter-name>
        <filter-class>com.example.MyFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>MyFilter</filter-name>
        <url-pattern>*.do</url-pattern>
    </filter-mapping>

    <!-- 配置 Servlet -->
    <servlet>
        <servlet-name>MyServlet</servlet-name>
        <servlet-class>com.example.MyServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>MyServlet</servlet-name>
        <url-pattern>/test.do</url-pattern>
    </servlet-mapping>

</web-app>

五、典型应用场景

1. 统一字符编码

public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) 
        throws IOException, ServletException {
    req.setCharacterEncoding("UTF-8");
    resp.setContentType("text/html;charset=UTF-8");
    chain.doFilter(req, resp);
}

2. 权限验证

public void doFilter(...) {
    HttpServletRequest httpReq = (HttpServletRequest) request;
    HttpSession session = httpReq.getSession();
    
    if(session.getAttribute("user") == null) {
        ((HttpServletResponse)response).sendRedirect("/login");
    } else {
        chain.doFilter(request, response);
    }
}

3. 日志记录

public void doFilter(...) {
    long startTime = System.currentTimeMillis();
    chain.doFilter(request, response);
    long endTime = System.currentTimeMillis();
    System.out.println("请求耗时:" + (endTime - startTime) + "ms");
}

4. 防止 XSS 攻击

public void doFilter(...) {
    HttpServletRequest req = (HttpServletRequest) request;
    // 使用自定义的 RequestWrapper 进行参数过滤
    chain.doFilter(new XssRequestWrapper(req), response);
}

// 自定义 RequestWrapper
class XssRequestWrapper extends HttpServletRequestWrapper {
    // 重写 getParameter 等方法进行 HTML 转义
}

六、Filter的高级特性

1. 过滤器链(Filter Chain)

多个Filter可以按配置顺序形成一条链,每个Filter依次处理请求。例如:

<filter-mapping>
    <filter-name>FilterA</filter-name>
    <url-pattern>/api/*</url-pattern>
</filter-mapping>
<filter-mapping>
    <filter-name>FilterB</filter-name>
    <url-pattern>/api/*</url-pattern>
</filter-mapping>

执行顺序:FilterA → FilterB → 目标资源。

⑴示例运行代码

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

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

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        System.out.println("FilterA 预处理开始");
        // 调用下一个Filter或目标资源
        chain.doFilter(request, response); // 关键方法
        System.out.println("FilterA 后处理结束");
    }

    @Override
    public void destroy() {
        System.out.println("FilterA 销毁");
    }
}
 FilterB.java
import javax.servlet.*;
import java.io.IOException;

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

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        System.out.println("FilterB 预处理开始");
        chain.doFilter(request, response); // 调用下一个Filter或目标资源
        System.out.println("FilterB 后处理结束");
    }

    @Override
    public void destroy() {
        System.out.println("FilterB 销毁");
    }
}
目标Servlet(HelloServlet.java)
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
        resp.getWriter().write("Hello World from Servlet!");
    }
}
web.xml
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
         http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!-- 配置FilterA -->
    <filter>
        <filter-name>FilterA</filter-name>
        <filter-class>FilterA</filter-class>
    </filter>

    <!-- 配置FilterB -->
    <filter>
        <filter-name>FilterB</filter-name>
        <filter-class>FilterB</filter-class>
    </filter>

    <!-- FilterA 映射到所有请求 -->
    <filter-mapping>
        <filter-name>FilterA</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- FilterB 映射到所有请求 -->
    <filter-mapping>
        <filter-name>FilterB</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- 配置Servlet -->
    <servlet>
        <servlet-name>HelloServlet</servlet-name>
        <servlet-class>HelloServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>HelloServlet</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>

</web-app>

2. 调度类型(Dispatcher Type)

通过<dispatcher>元素指定Filter拦截的请求类型,默认为REQUEST

  • REQUEST:直接访问资源时触发(如用户直接访问/api/data)。
  • FORWARD:通过RequestDispatcher.forward()转发时触发。
  • INCLUDE:通过RequestDispatcher.include()包含时触发。
  • ERROR:通过错误页面(如<error-page>)跳转时触发。

示例配置

<filter-mapping>
    <filter-name>MyFilter</filter-name>
    <url-pattern>/target</url-pattern>
    <dispatcher>FORWARD</dispatcher>
    <dispatcher>REQUEST</dispatcher>
</filter-mapping>

3. Spring集成

在Spring应用中,可通过DelegatingFilterProxy将Filter与Spring Bean集成:

<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

七、Filter的生命周期与Servlet生命周期展现

和Servlet对象生命周期一致。

唯一的区别:Filter默认情况下,在服务器启动阶段就实例化。Servlet不会。

1.MyServlet.java

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(urlPatterns = "/myServlet")
public class MyServlet extends HttpServlet {

    public MyServlet() {
        System.out.println("Servlet 实例化");
    }

    @Override
    public void init() throws ServletException {
        System.out.println("Servlet 初始化");
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        System.out.println("Servlet 处理 GET 请求");
        response.getWriter().println("Hello from Servlet!");
    }

    @Override
    public void destroy() {
        System.out.println("Servlet 销毁");
    }
}    

2.MyFilter.java

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

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

    public MyFilter() {
        System.out.println("Filter 实例化");
    }

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

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        System.out.println("Filter 执行过滤操作");
        chain.doFilter(request, response);
    }

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

八、责任链设计模式详细介绍

1.概念

责任链设计模式是一种行为设计模式,它允许你将请求沿着处理者链进行传递,直到有一个处理者能够处理该请求为止。在这个模式中,每个处理者都有一个对下一个处理者的引用,形成一条链。当客户端发起请求时,请求会从链的头部开始传递,每个处理者可以选择处理该请求或者将其传递给链中的下一个处理者。

2.优点

  1. 解耦请求发送者和接收者:请求的发送者不需要知道具体哪个处理者会处理请求,只需要将请求发送到链上即可。
  2. 增强灵活性:可以在运行时动态地添加、删除或调整处理者的顺序,而不需要修改客户端代码。
  3. 符合开闭原则:可以方便地扩展新的处理者,而不影响现有的代码。

3.在 Filter 中的应用

在 Java Web 开发中,Filter 过滤器就是责任链设计模式的典型应用。多个 Filter 可以组成一个过滤器链,每个 Filter 负责执行特定的任务,如身份验证、日志记录、字符编码设置等。请求会依次通过这些 Filter,每个 Filter 可以选择继续将请求传递给下一个 Filter,或者终止请求的处理。Filter 的调用顺序通常在 web.xml 中配置,这就体现了在程序运行阶段动态组合调用顺序的特点。

4.示例运行代码

 AuthenticationFilter

package oop2;


import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;


import java.io.IOException;

@WebFilter(urlPatterns = "/*")
public class AuthenticationFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        System.out.println("AuthenticationFilter: 开始进行身份验证");
        // 模拟身份验证逻辑
        chain.doFilter(request, response);
        System.out.println("AuthenticationFilter: 身份验证处理结束");
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 初始化操作
    }

    @Override
    public void destroy() {
        // 销毁操作
    }
}

LoggingFilter

package oop2;


import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;


import java.io.IOException;



@WebFilter(urlPatterns = "/*")
public class LoggingFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        System.out.println("LoggingFilter: 开始记录日志");
        // 模拟日志记录逻辑
        chain.doFilter(request, response);
        System.out.println("LoggingFilter: 日志记录处理结束");
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 初始化操作
    }

    @Override
    public void destroy() {
        // 销毁操作
    }
}

MyServlet

package oop2;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

@WebServlet(urlPatterns = "/myServlet")
public class MyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.getWriter().println("Hello from MyServlet!");
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值