What is the Chain of Responsibility pattern, and how do Servlet Filters embody it?
answer
- Handlers in a line; each handles or delegates
- doFilter(request, response, chain)
- chain.doFilter() = pass to next; omit it = stop
- Before + after hooks (nested/onion)
- Decouples sender from receiver
basics
~20 sIt is a design pattern where a request passes through a series of handlers, and each one decides whether to process it or pass it along. Servlet Filters are a chain: each filter can act on a request, then call the next one.
solid answer
~40 sChain of Responsibility decouples the sender of a request from its receiver by giving multiple handler objects a chance to process it. Each handler holds a reference to the next; it either handles the request and stops, or delegates onward. Servlet Filters are the classic Java example: the container assembles configured filters into a FilterChain, and each Filter.doFilter receives the request, response, and the chain. A filter can run logic before and after the rest of the chain, and calls chain.doFilter(request, response) to pass control to the next filter, eventually reaching the target servlet. If a filter does not call chain.doFilter, the request stops there. This lets you add cross-cutting concerns like logging, auth, compression, or CORS without modifying the servlet, and reorder or remove them independently.
go deeper
Can state that a request flows through handlers and each either handles it or passes it on, and recognize Servlet Filters as an example with doFilter and chain.doFilter.
Explains the before/after (nested) execution model, the pass-through semantics of calling vs. omitting chain.doFilter, and gives concrete filter use-cases.
Relates the pattern's intent (decouple sender from receiver, compose cross-cutting concerns) to the Filter API design, and discusses ordering and short-circuiting trade-offs.
Frames filters within the broader interception architecture (filters vs. Spring HandlerInterceptors vs. AOP), reasons about where each concern belongs, and the lifecycle/threading implications.
### The problem Imagine an HTTP request arriving at a web server. Before your actual business code (the *servlet*) runs, you often want several independent things to happen: log the request, check authentication, validate input, set CORS headers, compress the response, and so on. You do **not** want to cram all of that into one giant method, because each concern changes for different reasons and you want to add, remove, or reorder them freely. ### The pattern **Chain of Responsibility** is one of the original "Gang of Four" design patterns. The idea: instead of one object handling a request, you arrange a **chain of handler objects**. The request is given to the first handler. Each handler does one of: - **Handle it fully** and stop the chain, or - **Do part of the work and pass it on** to the next handler, or - **Ignore it and pass it on** unchanged. The *sender* of the request does not know which handler will deal with it — it just hands it to the front of the chain. This **decouples** sender from receiver and lets you change the set of handlers without touching the sender. ### Key terms - **Handler**: an object with a method that takes the request and a reference to "the rest of the chain." - **Delegation / pass-through**: a handler calling the next handler in line. - **Cross-cutting concern**: logic (logging, security) that applies to many requests regardless of business meaning. ### Servlet Filters: the Java/Jakarta embodiment A **Servlet** is a Java object that handles HTTP requests in a web container (Tomcat, Jetty). A **Filter** (interface `jakarta.servlet.Filter`, formerly `javax.servlet.Filter`) is an interceptor that runs *before and after* a servlet. Its single method is: ```java void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) ``` The `FilterChain` object **is** the "reference to the rest of the chain." When the container receives a request, it builds a `FilterChain` from all filters mapped to that URL (in configured order), with the target servlet at the very end. It invokes the first filter's `doFilter`. Inside, the filter typically: 1. Runs *pre-processing* logic (e.g. record start time). 2. Calls `chain.doFilter(request, response)` to invoke the **next** filter — or the servlet, if it is last. 3. Runs *post-processing* logic after that call returns (e.g. log elapsed time), because the call is synchronous and the rest of the chain runs nested inside it. This nesting means a filter wraps the entire downstream chain, giving it both a "before" and an "after" hook — like an onion of nested calls. ### Pass-through semantics If a filter **does not** call `chain.doFilter`, the request **stops** there — nothing downstream runs. That is exactly how an auth filter rejects an unauthenticated request: it writes a 401 response and simply returns without delegating. So calling (or not calling) `chain.doFilter` is the chain's control mechanism. ### Why it matters You can add logging, security, compression, or CORS as independent filters, configure their order, and enable/disable them per URL — all without changing the servlet. That is Chain of Responsibility delivering exactly its promised benefit.
- What happens if a filter never calls chain.doFilter()?The request stops at that filter; no downstream filter or the servlet runs. The filter is responsible for producing the response itself (e.g. a 401 or a redirect).
- Name two real cross-cutting concerns commonly implemented as filters.Authentication/authorization and request/response logging; also compression, CORS, character-encoding, and rate limiting.
saying these in an interview costs you the question
- Saying every filter must handle the request — handlers may just pass it on
- Confusing Filter with Servlet (filter intercepts; servlet is the endpoint)
- Thinking the chain is parallel — it is synchronous, nested calls
- Claiming you cannot run code after the chain returns — you can (post-processing)