杰瑞科技汇

Servlet Filter 如何实现请求拦截与处理?

Of course! Let's dive deep into Java Servlet Filters. This is a fundamental and powerful concept in Java web development.

Servlet Filter 如何实现请求拦截与处理?-图1
(图片来源网络,侵删)

What is a Servlet Filter?

A Servlet Filter is a Java class that is used to intercept requests and responses from a client to a servlet. It acts as a "chain" or a "pre-processor/post-processor" for your web application.

Think of it like a security checkpoint at an airport. Before you can get to your gate (the servlet), you must pass through several checkpoints (filters):

  1. Show ID: Verifies your identity (Authentication Filter).
  2. Check-in Luggage: Processes your baggage (Logging/Encoding Filter).
  3. Security Screening: Scans for threats (Security Filter).

You can't get to the gate without passing through these filters. Similarly, a request can't reach a servlet without passing through any configured filters.


Key Characteristics of a Filter

  • Interception: It sits between the client and the servlet, allowing it to inspect or modify the request and response objects.
  • Reusable: A single filter can be mapped to multiple servlets or URL patterns.
  • Configurable: You can define the order in which filters are executed and what they apply to.
  • Pluggable: You can easily add or remove filters from your application's configuration without changing your servlet code.

The Core Interface: javax.servlet.Filter

To create a filter, you must implement the Filter interface, which has three mandatory methods:

Servlet Filter 如何实现请求拦截与处理?-图2
(图片来源网络,侵删)
  1. init(FilterConfig config): This method is called by the servlet container only once when the filter is first initialized. It's used to perform any one-time setup, like reading configuration parameters from web.xml or initializing resources.
  2. doFilter(ServletRequest request, ServletResponse response, FilterChain chain): This is the heart of the filter. It's called for every request that matches the filter's URL mapping.
    • request: The ServletRequest object (or its subclass HttpServletRequest) that contains the client's request.
    • response: The ServletResponse object (or its subclass HttpServletResponse) that will be sent back to the client.
    • chain: The FilterChain object. This is the most important part. To pass the request to the next filter in the chain (or to the servlet itself), you must call chain.doFilter(request, response). If you don't, the request processing stops dead.
  3. destroy(): This method is called by the servlet container only once when the web application is being shut down. It's used to clean up any resources the filter has acquired (e.g., closing database connections, releasing memory).

A Simple Practical Example: Character Encoding Filter

A very common use case for a filter is to ensure that all incoming requests use a specific character encoding (like UTF-8), preventing issues with special characters in form data.

The Filter Class (CharacterEncodingFilter.java)

import java.io.IOException;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter; // Modern way to configure
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
// Using annotation to map this filter to all URLs
@WebFilter("/*") 
public class CharacterEncodingFilter implements Filter {
    // The encoding we want to enforce
    private String encoding;
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // Read encoding from web.xml, or use a default
        encoding = filterConfig.getInitParameter("encoding");
        if (encoding == null) {
            encoding = "UTF-8"; // Default encoding
        }
        System.out.println("CharacterEncodingFilter initialized with encoding: " + encoding);
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        // Cast to HttpServletRequest and HttpServletResponse for more features
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;
        // 1. PRE-PROCESSING: Set the request encoding
        req.setCharacterEncoding(encoding);
        // 2. You can also modify the response here if needed
        // For example, setting the response content type
        // res.setContentType("text/html; charset=" + encoding);
        System.out.println("CharacterEncodingFilter: Processing request for " + req.getRequestURI());
        // 3. PASS THE REQUEST TO THE NEXT FILTER/SERVLET IN THE CHAIN
        // This is the crucial step!
        chain.doFilter(request, response);
        System.out.println("CharacterEncodingFilter: Response processed for " + req.getRequestURI());
    }
    @Override
    public void destroy() {
        // Clean up resources if any were created in init()
        System.out.println("CharacterEncodingFilter destroyed.");
    }
}

Configuration (Two Methods)

There are two primary ways to configure a filter:

Method A: Using Annotations (Modern & Recommended for Servlet 3.0+)

This is the simplest way. You just add the @WebFilter annotation to your filter class.

Servlet Filter 如何实现请求拦截与处理?-图3
(图片来源网络,侵删)
@WebFilter("/*") // Applies this filter to all URL patterns in the application
public class CharacterEncodingFilter implements Filter { /* ... code from above ... */ }

Method B: Using web.xml (Traditional)

This method gives you more control, especially for complex configurations or when you need to specify the filter order.

<!-- 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">
    <!-- Define the filter -->
    <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>com.example.CharacterEncodingFilter</filter-class>
        <!-- You can pass initialization parameters here -->
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <!-- Map the filter to URL patterns -->
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <!-- The url-pattern determines which requests this filter handles -->
        <url-pattern>/*</url-pattern> 
    </filter-mapping>
</web-app>

The Filter Chain Concept

Filters are executed in a specific order. This order is determined by:

  1. The order of <filter-mapping> declarations in web.xml.
  2. The alphabetical order of class names when using annotations (which is not ideal for control).

When you call chain.doFilter(), you are passing control to the next element in the chain.

Example Flow:

Imagine you have two filters: SecurityFilter and LoggingFilter, and a HomeServlet.

  1. A request comes in for /home.
  2. The container determines which filters match /home.
  3. Let's assume the order is SecurityFilter -> LoggingFilter -> HomeServlet.
  4. SecurityFilter.doFilter() is called.
    • It performs security checks.
    • It then calls chain.doFilter().
  5. LoggingFilter.doFilter() is now called.
    • It logs the request details.
    • It then calls chain.doFilter().
  6. HomeServlet.doGet() is now called.

    It generates the HTML response.

  7. The response travels back up the chain:
    • LoggingFilter can modify the response (e.g., add a footer timestamp).
    • SecurityFilter can modify the response (e.g., add security headers).
    • The final response is sent to the client.
// Inside SecurityFilter.doFilter()
// ... security checks ...
System.out.println("SecurityFilter: Request passed security check.");
chain.doFilter(request, response); // Passes to LoggingFilter
System.out.println("SecurityFilter: Response received from servlet.");
// Inside LoggingFilter.doFilter()
System.out.println("LoggingFilter: Logging request details.");
chain.doFilter(request, response); // Passes to HomeServlet
System.out.println("LoggingFilter: Logging response details.");

Common Use Cases for Filters

  • Authentication & Authorization: Check if the user is logged in and has permission to access the resource.
  • Logging & Auditing: Log all incoming requests and outgoing responses for debugging or monitoring.
  • Data Compression: Compress the response data (e.g., using GZIP) before sending it to the client to reduce bandwidth.
  • Input Validation: Sanitize or validate user input before it reaches the servlet.
  • Image Processing: Dynamically resize or watermark images served by the application.
  • Setting Character Encoding: As shown in our example.
  • Caching: Control caching headers for responses.

Summary

Feature Description
Purpose Intercept and modify requests/responses.
Interface javax.servlet.Filter
Key Methods init(), doFilter(), destroy()
Configuration Annotations (`
分享:
扫描分享到社交APP
上一篇
下一篇