Understanding Servlet Filters and Filter Life Cycle
Course: Advanced Java Training
Topic: Understanding Servlet Filters and Filter Life Cycle
Course Link: Advanced Java Training Course
Demo Link: Register for a Free Course Demo
Official Website: JustAcademy
1. Introduction to Servlet Filters
A Servlet Filter is a Java component that intercepts HTTP requests and responses before they reach a target servlet or after the servlet has processed them. Filters are commonly used to perform reusable tasks such as request logging, authentication checks, authorization, character encoding, security header configuration, and request validation.
Instead of repeating the same logic in multiple servlets, developers can implement that logic once in a filter and map it to the URLs or servlet resources that need it.
For example, suppose an application contains a dashboard, a profile page, an account page, and an admin page. If all these pages require authentication, a filter can check whether the user has a valid session before allowing the request to continue.
Why Are Servlet Filters Important?
- Code reusability: Common request-processing logic can be written once and reused.
- Security: Filters can check authentication, enforce access policies, and add security-related headers.
- Request processing: Filters can inspect request headers, parameters, and other request information.
- Response processing: Filters can add headers or wrap responses when response modification is required.
- Logging and monitoring: Filters can record request paths, processing durations, and response statuses.
- Separation of concerns: Business logic remains in servlets while cross-cutting tasks are handled separately.
2. What Is the Servlet Filter Architecture?
A servlet filter works between the client and the requested web resource. The servlet container, such as Apache Tomcat, manages the filter lifecycle and invokes the filter when a matching request arrives.
Request and Response Flow
Client Browser
|
v
Servlet Container
|
v
Filter 1: Request Logging
|
v
Filter 2: Authentication
|
v
Filter 3: Character Encoding
|
v
Target Servlet
|
v
Response returns through the filter chain
|
v
Client Browser
The exact filters and their order depend on the application's configuration. A request may pass through several filters before reaching the target servlet. Filters can also process the response after the downstream component returns.
3. The Jakarta Servlet Filter Interface
To create a filter, implement the jakarta.servlet.Filter interface. The interface defines three main lifecycle methods: init(), doFilter(), and destroy().
In older Java EE applications, the equivalent interface is javax.servlet.Filter. The package depends on the Servlet API and server version used by the project. The examples in these notes use the modern jakarta.servlet namespace.
Important Filter Methods
| Method |
Purpose |
When It Is Called |
init(FilterConfig config) |
Initializes the filter and reads configuration. |
When the container places the filter into service. |
doFilter(ServletRequest request, ServletResponse response, FilterChain chain) |
Processes or checks a request and response. |
For each matching request passing through the filter. |
destroy() |
Releases resources used by the filter. |
When the container removes the filter from service. |
4. Understanding the Filter Life Cycle
The filter life cycle describes the stages a filter goes through from initialization to removal. The servlet container controls these stages; developers should not manually call the lifecycle methods during normal application operation.
Stage 1: Filter Instantiation
When the application is deployed and the container needs to prepare a configured filter, it creates an instance of the filter class. The precise timing can depend on the container and configuration.
A filter is usually instantiated through a public no-argument constructor. After creation, the container initializes the filter before using it to process matching requests.
Stage 2: Initialization Using init()
The container calls init(FilterConfig config) to initialize the filter. This method is normally called once for each filter instance. It is suitable for reading initialization parameters and preparing resources required throughout the filter's service period.
The FilterConfig object provides access to the filter's name, initialization parameters, and the associated ServletContext.
Example:
import jakarta.servlet.Filter;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
public class RequestLogFilter implements Filter {
private String applicationLabel;
@Override
public void init(FilterConfig config) throws ServletException {
applicationLabel = config.getInitParameter("applicationLabel");
if (applicationLabel == null) {
applicationLabel = "Web Application";
}
System.out.println(
applicationLabel + " filter initialized"
);
}
// doFilter() and destroy() are implemented below.
}
Initialization should complete successfully before the container invokes the filter's doFilter() method. If initialization fails, the container cannot use that filter instance normally.
Stage 3: Request Processing Using doFilter()
The doFilter() method contains the filter's main processing logic. The container invokes it whenever a matching request passes through the filter chain.
Inside this method, the filter can:
- Read request headers, methods, and URI information.
- Check authentication or authorization requirements.
- Record request details for debugging and monitoring.
- Wrap the request or response to customize their behavior.
- Call
chain.doFilter(request, response) to continue processing.
- Stop the chain and return a response when a request should be rejected.
- Perform post-processing after the downstream resource returns.
The filter's doFilter() method can run concurrently for multiple requests. Therefore, request-specific data should normally be stored in local variables rather than shared mutable instance fields.
Stage 4: Filter Destruction Using destroy()
When the container takes the filter out of service, it calls destroy(). This gives the filter an opportunity to release resources such as file handles, background tasks, or other resources it owns.
Example:
@Override
public void destroy() {
System.out.println("RequestLogFilter is being removed");
}
The container normally calls destroy() after active filter invocations have completed or after a container-defined timeout. The filter should not expect further requests after destruction.
Filter Life Cycle Summary
| Stage |
Method or Action |
Typical Responsibility |
| 1 |
Instantiation |
Create the filter object. |
| 2 |
init() |
Read configuration and initialize resources. |
| 3 |
doFilter() |
Process each matching request and response. |
| 4 |
destroy() |
Release resources before the filter is removed. |
5. Understanding FilterConfig
FilterConfig is an object supplied by the servlet container during initialization. It allows a filter to retrieve configuration values without hardcoding every setting in Java code.
Common FilterConfig Methods
| Method |
Description |
getFilterName() |
Returns the configured filter name. |
getInitParameter(String name) |
Returns the value of a named initialization parameter. |
getInitParameterNames() |
Returns the names of the initialization parameters. |
getServletContext() |
Returns the associated application context. |
For example, a logging filter can receive a configurable application label, while an encoding filter can receive a character-encoding setting through initialization parameters.
6. Creating a Servlet Filter Using @WebFilter
The @WebFilter annotation can declare a filter and specify the URL patterns it should handle. This approach is convenient for applications that use annotation-based configuration.
Example: Request Logging Filter
package com.example.filter;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
@WebFilter(urlPatterns = "/*")
public class RequestLoggingFilter implements Filter {
@Override
public void init(FilterConfig config) throws ServletException {
System.out.println("RequestLoggingFilter initialized");
}
@Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest =
(HttpServletRequest) request;
long startTime = System.currentTimeMillis();
System.out.println(
"Incoming request: " + httpRequest.getMethod()
+ " " + httpRequest.getRequestURI()
);
try {
chain.doFilter(request, response);
} finally {
long duration =
System.currentTimeMillis() - startTime;
System.out.println(
"Request processing time: " + duration + " ms"
);
}
}
@Override
public void destroy() {
System.out.println("RequestLoggingFilter destroyed");
}
}
Explanation of the Code
@WebFilter(urlPatterns = "/*") maps the filter to all URL paths in the application, subject to the application's filter and dispatcher configuration.
init() prints a message when the container initializes the filter.
doFilter() converts the generic request to HttpServletRequest so HTTP-specific information can be accessed.
getMethod() retrieves the HTTP method, such as GET or POST.
getRequestURI() retrieves the request URI.
chain.doFilter() passes control to the next filter or target resource.
- The
finally block records elapsed time even if downstream processing throws an exception.
destroy() logs when the filter is removed from service.
Important: In production, avoid logging passwords, session identifiers, access tokens, or other sensitive request information.
7. Understanding FilterChain
A FilterChain represents the remaining filters and the target web resource that should process a request. Calling chain.doFilter(request, response) continues the chain.
How the Chain Works
Client Request
|
v
LoggingFilter.doFilter()
|
| chain.doFilter()
v
AuthenticationFilter.doFilter()
|
| chain.doFilter()
v
Target Servlet
|
v
Response returns to previous filters
|
v
Client Response
Before calling chain.doFilter(), a filter can perform request-side checks. After the call returns, it can perform response-side work, such as logging the status or elapsed time.
If a filter does not call chain.doFilter(), the remaining chain is not invoked through that call. The filter must then produce or arrange an appropriate response if it is intentionally blocking the request.
8. Filter Processing Before and After a Servlet
A filter can execute code on both sides of the downstream call. This pattern is useful for measuring processing time and performing cleanup.
@Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
System.out.println("Before the servlet");
try {
chain.doFilter(request, response);
} finally {
System.out.println("After downstream processing");
}
}
The message before chain.doFilter() is printed before downstream processing. The message in the finally block is printed after the downstream call returns or throws an exception. This does not guarantee that a successful HTTP response was delivered to the client.
9. Configuring a Filter Using web.xml
Filters can also be declared in the deployment descriptor, WEB-INF/web.xml. This approach is useful when filter mappings and initialization parameters should be managed centrally.
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
version="6.0>
<filter>
<filter-name>RequestLoggingFilter</filter-name>
<filter-class>
com.example.filter.RequestLoggingFilter
</filter-class>
<init-param>
<param-name>applicationLabel</param-name>
<param-value>Student Portal</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>RequestLoggingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
Note: The XML above illustrates the configuration structure. For a real project, ensure the namespace, schema declaration, and version match the Servlet API supported by your application server. In the opening tag, the version attribute should be written as version="6.0", with the quote closed before the tag ends.
When using web.xml, the <filter> element declares the filter class and optional parameters. The <filter-mapping> element determines which resources the filter applies to.
10. Configuring Filter Initialization Parameters
Initialization parameters allow filter behavior to be changed without editing its Java source code.
Annotation-Based Configuration
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.annotation.WebInitParam;
@WebFilter(
urlPatterns = "/reports/*",
initParams = {
@WebInitParam(
name = "applicationLabel",
value = "Reporting Module"
)
}
)
public class ReportFilter implements Filter {
// Implement lifecycle methods.
}
Reading a Parameter
@Override
public void init(FilterConfig config) throws ServletException {
String label = config.getInitParameter("applicationLabel");
System.out.println("Initialized for: " + label);
}
Choose either annotation-based registration or descriptor-based registration deliberately. Avoid accidentally registering the same filter twice unless duplicate execution is intended.
11. Common Types of Servlet Filters
| Filter Type |
Purpose |
Example |
| Authentication filter |
Checks whether a user has authenticated. |
Require a valid session for dashboard pages. |
| Authorization filter |
Checks whether a user is allowed to access a resource. |
Restrict admin URLs to authorized roles. |
| Logging filter |
Records request activity and timing. |
Log method, URI, and duration. |
| Encoding filter |
Sets request character encoding where appropriate. |
Configure UTF-8 before reading form parameters. |
| Security header filter |
Adds appropriate HTTP security headers. |
Set a content security policy based on application requirements. |
| Validation filter |
Performs shared preliminary checks. |
Reject requests missing a required header. |
| Compression or transformation filter |
Wraps or transforms response content when correctly implemented. |
Compress suitable response bodies. |
12. Example: Authentication Filter
An authentication filter can check whether a user has an authenticated session before accessing protected pages. This example is educational and assumes the application creates the user session attribute only after successful authentication.
package com.example.filter;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import java.io.IOException;
@WebFilter(urlPatterns = {"/dashboard/*", "/account/*"})
public class AuthenticationFilter implements Filter {
@Override
public void init(FilterConfig config) throws ServletException {
System.out.println("AuthenticationFilter initialized");
}
@Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest =
(HttpServletRequest) request;
HttpServletResponse httpResponse =
(HttpServletResponse) response;
HttpSession session =
httpRequest.getSession(false);
boolean authenticated =
session != null
&& session.getAttribute("user") != null;
if (authenticated) {
chain.doFilter(request, response);
} else {
httpResponse.sendRedirect(
httpRequest.getContextPath() + "/login"
);
}
}
@Override
public void destroy() {
System.out.println("AuthenticationFilter destroyed");
}
}
How It Works
- The filter is mapped to the dashboard and account URL patterns.
getSession(false) retrieves an existing session without creating a new one.
- The filter checks whether the expected authenticated-user attribute exists.
- If the session indicates authentication, the filter continues the chain.
- Otherwise, it redirects the request to the login path.
Security reminder: A session attribute check is only one part of authentication. The application must set the attribute after verifying credentials, invalidate sessions on logout, enforce authorization for protected operations, and apply appropriate session-cookie and transport security. Never trust a client-controlled parameter as proof of authentication.
13. Filter Mapping and URL Patterns
Filter mappings determine which requests are processed by a filter. A mapping can target URL patterns or named servlets.
| URL Pattern |
Meaning |
Example Use |
/* |
Matches paths throughout the application. |
General request logging. |
/admin/* |
Matches paths under the admin route. |
Admin access control. |
/account/* |
Matches account-related paths. |
Account authentication checks. |
*.jsp |
Matches paths ending in the JSP extension where extension mapping applies. |
Selected JSP request processing. |
Use the narrowest suitable mapping for a filter. A filter mapped to /* may also execute for static assets and other requests, depending on dispatcher configuration.
14. Filter Dispatcher Types
Servlet filters can be configured for different dispatcher types. The dispatcher type determines how the resource is being reached.
| Dispatcher Type |
Meaning |
REQUEST |
A normal client request. This is the default dispatcher type for annotation-based filter mappings unless configured otherwise. |
FORWARD |
The request is forwarded to another resource within the server. |
INCLUDE |
A resource is included during processing of another resource. |
ERROR |
The request is being handled through error-page processing. |
ASYNC |
The filter is eligible for asynchronous dispatches when configured appropriately. |
For example, a logging filter may need to observe forwarded requests, while a security filter should be designed carefully to avoid unintended duplicate checks or gaps. Select dispatcher types based on the application's actual request flow.
15. Filter Order and Multiple Filters
Multiple filters can apply to the same request. Their order matters because one filter may modify a request or response before another filter sees it.
When filters are declared in web.xml, mapping order influences the chain order for matching mappings. Annotation-based registration can have container-dependent ordering behavior; if a precise order is important, use an explicit and documented configuration strategy.
Example Filter Sequence
Incoming Request
|
v
CharacterEncodingFilter
|
v
RequestLoggingFilter
|
v
AuthenticationFilter
|
v
AuthorizationFilter
|
v
Target Servlet
This is an illustrative sequence, not a universal requirement. For example, encoding should be established before reading request parameters, while logging should avoid exposing sensitive data. Authentication and authorization checks should be positioned consistently with the application's security design.
16. Filter Life Cycle vs Servlet Life Cycle
| Feature |
Filter |
Servlet |
| Primary purpose |
Intercepts or transforms requests and responses. |
Handles a target web request and produces application behavior. |
| Initialization |
init(FilterConfig) |
init(ServletConfig) or inherited initialization methods. |
| Request method |
doFilter() |
Typically service() and HTTP methods such as doGet() or doPost(). |
| Cleanup |
destroy() |
destroy() |
| Typical responsibilities |
Logging, shared security checks, encoding, request/response wrapping. |
Application logic, data retrieval, and request-specific processing. |
17. Best Practices for Servlet Filters
- Keep filters focused on cross-cutting concerns instead of placing business logic in them.
- Use
init() for one-time configuration and resource initialization.
- Release owned resources in
destroy().
- Assume that
doFilter() may be called concurrently by multiple request threads.
- Avoid storing request-specific data in mutable instance fields.
- Use
chain.doFilter() whenever the request should continue to the next filter or resource.
- Use
try/finally for cleanup and timing operations that must occur after downstream processing.
- Do not log passwords, authorization headers, session IDs, or other sensitive values.
- Use the correct URL patterns and dispatcher types.
- Prevent duplicate registration of the same filter.
- Choose
jakarta.servlet or javax.servlet according to the application's server and API version.
- Handle exceptions carefully and avoid swallowing errors that should be reported to the container or application error handler.
- Use HTTPS and suitable session-cookie settings for applications handling authenticated sessions.
18. Common Errors and Troubleshooting
| Problem |
Possible Cause |
Solution |
| Filter does not execute |
Incorrect URL mapping or filter registration. |
Check @WebFilter, web.xml, deployment logs, and URL patterns. |
| Filter executes twice |
Duplicate registration or multiple matching dispatches. |
Review annotations, XML mappings, and dispatcher types. |
| Servlet is never reached |
The filter does not call chain.doFilter(). |
Continue the chain when the request is allowed to proceed. |
| Initialization fails |
Invalid configuration or an exception in init(). |
Inspect container logs and validate initialization parameters. |
| Request parameters are garbled |
Character encoding is set too late or incorrectly. |
Set the appropriate encoding before reading parameters and verify the client request encoding. |
| Concurrent requests cause incorrect values |
Shared mutable state in the filter instance. |
Keep request-specific values in local variables or properly scoped request data. |
| Login redirect loops |
The login URL is also protected by the same authentication rule. |
Exclude public login resources from the protected mapping or handle them explicitly. |
| Compilation errors for servlet imports |
The API namespace does not match the server version. |
Use the compatible Servlet API dependency and container. |
19. Practical Example: Build a Request Monitoring Filter
Objective: Create a filter that records the HTTP method, request URI, and elapsed processing time for each matching request.
Steps
- Create a Java web application in an IDE such as Eclipse or IntelliJ IDEA.
- Ensure the project uses a compatible Jakarta Servlet API and servlet container.
- Create a package named
com.example.filter.
- Create the
RequestLoggingFilter class and implement Filter.
- Implement
init(), doFilter(), and destroy().
- Map the filter to a suitable URL pattern, such as
/app/* or /*.
- Create a test servlet and send several requests to it.
- Review the server console to verify request details and elapsed times.
- Test both successful requests and requests that trigger exceptions.
Expected Output Example
RequestLoggingFilter initialized
Incoming request: GET /myapp/dashboard
Request processing time: 18 ms
Incoming request: POST /myapp/login
Request processing time: 24 ms
RequestLoggingFilter destroyed
The numbers are illustrative. Actual execution time depends on the application, server, network, and environment.
20. Practice Exercises
- Create a filter that logs the request method and URI.
- Write a filter that sets an appropriate character encoding before a servlet reads request parameters.
- Create a filter that checks for a session attribute before allowing access to a protected route.
- Configure a filter using
web.xml and pass an initialization parameter.
- Create two filters and observe their execution order before and after the target servlet.
- Modify a filter so it records elapsed time even when downstream processing throws an exception.
- Test a filter with different dispatcher types, including forward and error dispatches.
- Investigate why a filter may execute more than once and document the fix.
21. Interview Questions and Answers
Q1. What is a Servlet Filter?
Answer: A Servlet Filter is a Java component that intercepts requests and responses to perform reusable tasks such as logging, authentication checks, and request or response processing.
Q2. Which interface is used to create a filter?
Answer: Implement jakarta.servlet.Filter in modern Jakarta Servlet applications. Older Java EE applications use javax.servlet.Filter.
Q3. What are the main filter lifecycle methods?
Answer: init() initializes the filter, doFilter() processes matching requests, and destroy() releases resources when the filter is taken out of service.
Q4. How many times is init() called?
Answer: The container normally calls init() once for each filter instance, before that instance begins processing requests.
Q5. Is doFilter() called only once?
Answer: No. The container invokes doFilter() for each matching request and applicable dispatch that passes through the filter chain.
Q6. What is FilterConfig?
Answer: It provides filter configuration information, including the filter name, initialization parameters, and the associated servlet context.
Q7. What is FilterChain?
Answer: It represents the remaining chain of filters and the target resource. Calling chain.doFilter() passes the request and response to the next entity.
Q8. What happens if chain.doFilter() is not called?
Answer: The request does not proceed through the remaining chain through that invocation. A filter may intentionally block a request, but it should provide an appropriate response when doing so.
Q9. Can a filter modify a response?
Answer: Yes. A filter can set response headers and can wrap the response to inspect or transform output when implemented correctly.
Q10. What is the purpose of destroy()?
Answer: It gives the container-managed filter an opportunity to release resources before the filter instance is removed from service.
Q11. Can multiple filters be applied to one request?
Answer: Yes. The container invokes matching filters in the configured chain order before reaching the target resource.
Q12. What is the difference between a filter and a servlet?
Answer: A filter performs reusable processing around a request or response, while a servlet typically handles the target web request and application behavior.
Q13. What is @WebFilter?
Answer: It is an annotation used to declare a filter and configure its URL patterns, dispatcher types, and initialization parameters.
Q14. Why should filter fields be used carefully?
Answer: A container may use one filter instance to process multiple requests concurrently. Mutable shared fields can cause race conditions or leak request-specific information between requests.
Q15. What is the difference between REQUEST and FORWARD dispatching?
Answer: A REQUEST dispatch represents a normal client request, while a FORWARD dispatch occurs when the server forwards processing to another resource. A filter must be mapped to the relevant dispatcher type to process that dispatch.
22. Quick Revision Checklist
- A filter intercepts requests and responses around web resources.
- The servlet container manages filter creation, initialization, invocation, and destruction.
init() is used for initialization and configuration.
doFilter() contains request-processing logic and may run concurrently.
chain.doFilter() continues the filter chain.
destroy() is used for resource cleanup.
FilterConfig supplies initialization information.
@WebFilter and web.xml are common filter registration approaches.
- URL patterns and dispatcher types determine when a filter applies.
- Filter order affects request and response processing.
- Authentication filters must be designed with session security and authorization in mind.
- Never log credentials or sensitive tokens.
23. Conclusion
Servlet Filters are an important feature of Advanced Java web application development. They provide a reusable mechanism for handling common tasks such as logging, security checks, encoding, and request monitoring without duplicating the same code in every servlet.
Understanding the filter life cycle—initialization through init(), request processing through doFilter(), and cleanup through destroy()—helps developers build maintainable and reliable web applications. Practical experience with filter mappings, initialization parameters, filter chains, and dispatcher types is especially useful when developing enterprise Java applications.
To learn more about Advanced Java, visit the Advanced Java Training Course, explore the course demo registration page, or visit the JustAcademy website.