Description / Meta Description
Learn the Chain of Responsibility Design Pattern in Java with practical examples. Understand how requests flow through multiple handlers, how Spring Security, Servlet Filters, API Gateways, and validation frameworks use this pattern, and why it helps build flexible and extensible processing pipelines.
Chain of Responsibility Design Pattern in Java: Building Flexible Request Processing Pipelines
In the previous article, we explored the Command Pattern, which helps encapsulate actions and requests as objects.
Command answers:
How do I represent an action as an object?
Chain of Responsibility answers a different question:
How do I pass a request through multiple processing steps without tightly coupling the sender to the processors?
This pattern is everywhere in enterprise applications.
If you’ve worked with:
- Spring Security Filters
- Servlet Filters
- API Gateways
- Validation Pipelines
- Middleware Frameworks
- Request Interceptors
then you’ve almost certainly encountered Chain of Responsibility.
The Problem: Growing Request Processing Logic
Imagine you’re building an API.
Every incoming request must go through:
Authentication
↓
Authorization
↓
Input Validation
↓
Rate Limiting
↓
Logging
↓
Business Logic
A naive implementation might look like:
public class RequestProcessor {
public void process(Request request) {
authenticate(request);
authorize(request);
validate(request);
rateLimit(request);
log(request);
executeBusinessLogic(request);
}
}
Looks simple initially.
Now business requirements evolve.
Additional processing appears:
Fraud Detection
Audit Logging
Encryption
Tracing
Metrics Collection
Request Transformation
Caching
Suddenly:
RequestProcessor
Becomes Huge
Difficult To Extend
Difficult To Test
Difficult To Maintain
Every new step requires modifying existing code.
This violates the:
Open/Closed Principle
What We Really Want
Instead of:
One Giant Processor
We want:
Request
↓
Authentication Handler
↓
Authorization Handler
↓
Validation Handler
↓
Logging Handler
↓
Business Logic Handler
Each handler performs one responsibility.
Then passes the request forward.
This is Chain of Responsibility.
What is Chain of Responsibility?
Chain of Responsibility is a Behavioral Design Pattern that:
Passes a request through a chain of handlers where each handler decides whether to process the request and whether to pass it further.
Think of it like airport security.
Passenger arrives.
Identity Check
↓
Security Scan
↓
Passport Verification
↓
Boarding Gate
Each checkpoint performs its responsibility.
Then forwards the passenger.
Real Life Analogy
Imagine a customer support escalation process.
Support Agent
↓
Team Lead
↓
Manager
↓
Director
If one level cannot resolve the issue:
Pass To Next Level
This is Chain of Responsibility.
Chain Architecture
Client
│
▼
Handler 1
│
▼
Handler 2
│
▼
Handler 3
│
▼
Handler N
Each handler decides:
Process Request?
Continue?
Stop?
Step 1: Create Handler Base Class
public abstract class Handler {
protected Handler next;
public Handler setNext(
Handler next) {
this.next = next;
return next;
}
public abstract void handle(
Request request);
}
This creates the chain.
Step 2: Create Authentication Handler
public class AuthenticationHandler
extends Handler {
@Override
public void handle(
Request request) {
System.out.println(
"Authentication Passed");
if(next != null) {
next.handle(request);
}
}
}
Step 3: Create Authorization Handler
public class AuthorizationHandler
extends Handler {
@Override
public void handle(
Request request) {
System.out.println(
"Authorization Passed");
if(next != null) {
next.handle(request);
}
}
}
Step 4: Create Logging Handler
public class LoggingHandler
extends Handler {
@Override
public void handle(
Request request) {
System.out.println(
"Request Logged");
if(next != null) {
next.handle(request);
}
}
}
Step 5: Build The Chain
Handler auth =
new AuthenticationHandler();
Handler authorization =
new AuthorizationHandler();
Handler logging =
new LoggingHandler();
auth.setNext(authorization)
.setNext(logging);
Chain becomes:
Authentication
↓
Authorization
↓
Logging
Step 6: Execute Request
auth.handle(request);
Output:
Authentication Passed
Authorization Passed
Request Logged
The request flows through the chain.
Why Chain of Responsibility Works
Without Chain:
Huge Method
Huge Class
Huge Conditional Logic
With Chain:
Small Independent Handlers
Composable Pipeline
Easy Extensibility
Each responsibility remains isolated.
Request Lifecycle Visualization
Incoming Request
│
▼
Authentication
│
▼
Authorization
│
▼
Validation
│
▼
Logging
│
▼
Business Logic
│
▼
Response
This is exactly how many enterprise frameworks operate.
Spring Security Example
One of the best examples of Chain of Responsibility.
Incoming request:
HTTP Request
Passes through:
SecurityContextFilter
↓
AuthenticationFilter
↓
AuthorizationFilter
↓
ExceptionFilter
↓
Controller
Each filter performs a single responsibility.
Then forwards the request.
Classic Chain Pattern.
Servlet Filter Example
Java Servlet Filters work the same way.
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain) {
logRequest();
chain.doFilter(
request,
response);
}
Notice:
chain.doFilter(...)
This passes execution to the next handler.
Pure Chain of Responsibility.
API Gateway Example
Incoming API request:
Authentication
↓
JWT Validation
↓
Rate Limiting
↓
Tracing
↓
Routing
Each stage processes and forwards.
Exactly the same pattern.
Validation Framework Example
User Registration:
Validate Name
↓
Validate Email
↓
Validate Phone
↓
Validate Password
Instead of:
if(...)
if(...)
if(...)
if(...)
Use handlers.
Cleaner design.
Workflow Engine Example
Loan Approval:
Credit Check
↓
Income Verification
↓
Fraud Detection
↓
Manager Approval
Each stage becomes a handler.
The workflow becomes flexible.
Dynamic Chain Construction
One powerful feature:
Handlers can be added
Handlers can be removed
Handlers can be reordered
Example:
chain.add(
new AuditHandler());
chain.add(
new MetricsHandler());
No existing code changes required.
Benefits of Chain of Responsibility
1. Loose Coupling
Sender doesn’t know processors.
2. Single Responsibility Principle
Each handler does one thing.
3. Open/Closed Principle
Add new handlers easily.
4. Reusable Components
Handlers can be reused across chains.
5. Flexible Pipelines
Chains can be configured dynamically.
Common Mistakes
Mistake 1: God Handlers
Avoid:
Authentication
Authorization
Validation
Logging
Inside One Handler
Keep handlers focused.
Mistake 2: Broken Chains
Forgetting:
next.handle(request);
stops processing unexpectedly.
Mistake 3: Excessively Long Chains
Chains with:
30+
Handlers
become difficult to debug.
Keep pipelines meaningful.
Chain of Responsibility vs Observer
Common interview question.
Observer
Purpose:
One Event
Many Subscribers
All listeners receive notification.
Chain of Responsibility
Purpose:
One Request
Many Processors
Request travels through a pipeline.
Chain of Responsibility vs Command
Command
Represents:
An Action
Example:
Generate Report
Delete User
Send Email
Chain
Represents:
Request Processing Pipeline
Example:
Authentication
Validation
Authorization
Chain of Responsibility vs Strategy
Strategy
Select one algorithm.
Card Payment
UPI Payment
Wallet Payment
Only one strategy executes.
Chain
Multiple handlers execute.
Handler 1
Handler 2
Handler 3
Pipeline processing.
When Should You Use Chain of Responsibility?
Use Chain when:
✔ Requests pass through multiple stages
✔ Processing steps vary
✔ Dynamic pipelines are required
✔ Middleware exists
✔ Validation chains exist
Examples:
- Spring Security
- API Gateways
- Validation Frameworks
- Workflow Engines
- Middleware Pipelines
- Servlet Filters
Avoid Chain when:
❌ Only one processor exists
❌ Processing order never changes
❌ Simpler method calls suffice
Behavioral Patterns Covered So Far
| Pattern | Purpose |
|---|---|
| Strategy | Choose algorithms dynamically |
| Observer | Publish-subscribe communication |
| Command | Encapsulate actions |
| Chain of Responsibility | Process requests through handlers |
Behavioral Pattern Cheat Sheet
Strategy
→ Choose Behavior
Observer
→ Broadcast Events
Command
→ Encapsulate Actions
Chain of Responsibility
→ Process Through Pipeline
Final Thoughts
The Chain of Responsibility Pattern solves one of the most common architectural challenges:
How do we process requests through multiple independent stages without tightly coupling them together?
By creating chains of focused handlers, we gain:
- Flexibility
- Extensibility
- Maintainability
- Reusability
- Better Separation of Concerns
This is why Chain of Responsibility powers:
- Spring Security
- Servlet Filters
- API Gateways
- Validation Pipelines
- Middleware Frameworks
- Enterprise Workflow Engines
Whenever you see a request flowing through multiple processing stages, you’re likely looking at a Chain of Responsibility implementation.
In the next article, we’ll explore another highly practical Behavioral Pattern:
State Design Pattern — Changing Object Behavior Dynamically Based on Its Current State
You’ll learn how order management systems, workflow engines, approval processes, ticketing systems, and finite state machines rely heavily on State Pattern concepts.