Filter / Chain of Responsibility Pattern
IntermediatePasses a request along a chain of handlers where each handler either processes it, enriches it, or forwards it to the next handler.
Overview
Chain of Responsibility decouples request senders from receivers by letting multiple handlers get a chance to process a request. The chain is built at runtime by linking handler objects. Each handler holds a reference to the next handler in the chain. The handler can process and stop, process and forward, or just forward. In web frameworks, this is called a Filter Chain or Middleware Pipeline — Spring Security filter chain, Servlet filters, and OkHttp interceptors all use this pattern. Unlike the basic pattern, a filter chain always passes the request forward unless it explicitly short-circuits (e.g. authentication failure).
Classic Chain of Responsibility
A logging/approval workflow where different levels handle requests based on authority. Each handler checks if it can handle the request; if not, it forwards to the next handler.
// Abstract Handler
public abstract class ApprovalHandler {
protected ApprovalHandler next;
public ApprovalHandler setNext(ApprovalHandler next) {
this.next = next;
return next; // fluent chaining
}
public abstract void handleRequest(ExpenseRequest request);
}
public class ExpenseRequest {
public final double amount;
public final String description;
public ExpenseRequest(double amount, String description) {
this.amount = amount; this.description = description;
}
}
// Concrete Handlers
public class TeamLeadApprover extends ApprovalHandler {
@Override
public void handleRequest(ExpenseRequest request) {
if (request.amount <= 1_000) {
System.out.println("Team Lead approved: " + request.description);
} else if (next != null) {
next.handleRequest(request); // forward up the chain
}
}
}
public class ManagerApprover extends ApprovalHandler {
@Override
public void handleRequest(ExpenseRequest request) {
if (request.amount <= 10_000) {
System.out.println("Manager approved: " + request.description);
} else if (next != null) {
next.handleRequest(request);
}
}
}
public class DirectorApprover extends ApprovalHandler {
@Override
public void handleRequest(ExpenseRequest request) {
System.out.println("Director approved: " + request.description + " (₹" + request.amount + ")");
}
}
// Build the chain
ApprovalHandler chain = new TeamLeadApprover();
chain.setNext(new ManagerApprover())
.setNext(new DirectorApprover());
chain.handleRequest(new ExpenseRequest(500, "Team lunch")); // Team Lead
chain.handleRequest(new ExpenseRequest(5_000, "Laptop RAM")); // Manager
chain.handleRequest(new ExpenseRequest(50_000, "Server upgrade")); // DirectorFilter Chain (HTTP Middleware)
A filter chain applies transformations/checks to a request sequentially. Each filter calls chain.doFilter() to pass to the next. This is how Servlet filters and Spring Security work. Short-circuiting (not calling doFilter) blocks the request.
// Filter interface (mirrors javax.servlet.Filter)
@FunctionalInterface
public interface HttpFilter {
void doFilter(HttpRequest request, HttpResponse response, FilterChain chain);
}
public class FilterChain {
private final List<HttpFilter> filters;
private int index = 0;
public FilterChain(List<HttpFilter> filters) { this.filters = filters; }
public void doFilter(HttpRequest request, HttpResponse response) {
if (index < filters.size()) {
HttpFilter filter = filters.get(index++);
filter.doFilter(request, response, this);
}
}
}
// Concrete Filters
public class AuthFilter implements HttpFilter {
@Override
public void doFilter(HttpRequest req, HttpResponse res, FilterChain chain) {
String token = req.getHeader("Authorization");
if (token == null || !isValid(token)) {
res.setStatus(401);
res.setBody("Unauthorized");
return; // short-circuit — do NOT call chain.doFilter()
}
System.out.println("Auth passed");
chain.doFilter(req, res); // forward to next filter
}
private boolean isValid(String token) { return token.startsWith("Bearer "); }
}
public class LoggingFilter implements HttpFilter {
@Override
public void doFilter(HttpRequest req, HttpResponse res, FilterChain chain) {
long start = System.currentTimeMillis();
System.out.println("→ " + req.getMethod() + " " + req.getPath());
chain.doFilter(req, res);
System.out.println("← " + res.getStatus() + " (" +
(System.currentTimeMillis() - start) + "ms)");
}
}
// Wiring
FilterChain pipeline = new FilterChain(List.of(
new LoggingFilter(),
new AuthFilter(),
new RateLimitFilter()
));
pipeline.doFilter(incomingRequest, response);Key Points to Remember
- 1Chain of Responsibility decouples sender from receiver — sender does not know which handler processes the request.
- 2Each handler can process, enrich, forward, or short-circuit the request.
- 3Servlet filters and Spring Security filter chain are production examples of this pattern.
- 4Filter order matters — authentication before authorization, logging wrapping both.
- 5Unlike Command pattern, Chain of Responsibility has multiple potential handlers; Command has one.
Interview Questions
Sign in to ask AriaHow does the Chain of Responsibility pattern relate to Servlet filters?
What is the difference between Chain of Responsibility and Decorator?
How does Spring Security implement its filter chain?
When would a handler short-circuit the chain vs forward?
Ask Aria about Filter / Chain of Responsibility Pattern
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.