How do you control the execution order of multiple `WebFilter`s, and what does the order value mean?
answer
- @Order or implement Ordered
- lower value = runs first = outermost
- HIGHEST_PRECEDENCE = Integer.MIN_VALUE
- AnnotationAwareOrderComparator sorts them
- no order = LOWEST_PRECEDENCE, undefined ties
basics
~20 sGive each filter an order via the @Order annotation or by implementing Ordered. Spring sorts WebFilter beans by that value — lower value = higher precedence = runs earlier (and, because filters wrap each other, later on the way back out).
solid answer
~40 sWhen several `WebFilter` beans exist, `WebHttpHandlerBuilder` sorts them with `AnnotationAwareOrderComparator`. You assign order either by annotating the bean with `@Order(n)` (or `@Order` on the `@Bean`/class), or by implementing `org.springframework.core.Ordered` and returning a value from `getOrder()`. **Lower numbers run first** (`Ordered.HIGHEST_PRECEDENCE` = `Integer.MIN_VALUE`, `LOWEST_PRECEDENCE` = `Integer.MAX_VALUE`). Ordering is nesting, not just sequence: the first filter's code before `chain.filter(...)` runs first on the way in, and its code composed after the chain (`.then`/`.doFinally`) runs last on the way out — filters wrap one another like Russian dolls. So an auth/tracing filter that must see the request before others gets a low order; something that must observe the final response gets a low order too (because it's outermost). Without explicit ordering, order is effectively undefined, so always set it when it matters.
code
java · 28 linesimport org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.server.*;
import reactor.core.publisher.Mono;
@Component
@Order(Ordered.HIGHEST_PRECEDENCE) // outermost: runs first in, last out
public class CorrelationIdFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
String id = exchange.getRequest().getHeaders()
.getFirst("X-Correlation-Id");
ServerWebExchange mutated = (id != null) ? exchange
: exchange.mutate().request(r -> r.header("X-Correlation-Id",
java.util.UUID.randomUUID().toString())).build();
return chain.filter(mutated);
}
}
@Component
@Order(0) // runs after CorrelationIdFilter, so it can read the id
class LoggingFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
return chain.filter(exchange);
}
}go deeper
Know @Order sets the order and lower runs first.
Explain @Order vs Ordered, the Integer.MIN/MAX constants, and default LOWEST_PRECEDENCE.
Explain the nesting/wrapping model — lowest order is outermost, first-in/last-out — and how to place filters relative to Spring Security.
Reason about ordering as a cross-cutting contract, tie-break hazards, and separating top-level WebFilter ordering from intra-Security-chain ordering.
## Why ordering matters Filters wrap the handler and each other. If a correlation-ID filter must set an ID that a logging filter reads, the correlation filter has to run *first*. If a decompression/mutation filter must transform the request before auth inspects it, order decides correctness. ## How to set order Two interchangeable mechanisms, both understood by `AnnotationAwareOrderComparator`: ### a) `@Order` annotation ```java @Component @Order(-100) public class TracingFilter implements WebFilter { ... } ``` Or on a `@Bean` factory method: ```java @Bean @Order(10) WebFilter loggingFilter() { return new LoggingFilter(); } ``` ### b) Implement `Ordered` ```java @Component public class TracingFilter implements WebFilter, Ordered { @Override public int getOrder() { return Ordered.HIGHEST_PRECEDENCE + 10; } // ... } ``` ## The number semantics - Order is an `int`. **Lower = earlier / higher precedence.** - Constants: `Ordered.HIGHEST_PRECEDENCE = Integer.MIN_VALUE`, `Ordered.LOWEST_PRECEDENCE = Integer.MAX_VALUE`. - Beans without any order default to `LOWEST_PRECEDENCE` (they sort last). Relying on the default among several filters gives undefined relative order — don't. ## Wrapping (in vs out) Think of the chain as nested calls. With filters A (order 1) and B (order 2): - On the way **in**: A's pre-chain code → B's pre-chain code → handler. - On the way **out**: handler → B's post-chain (`.then`/`.doFinally`) → A's post-chain. So the **lowest-order filter is the outermost**: it's first to touch the request and last to touch the response. That's exactly where you want metrics/tracing that must measure the total time including all other filters. ## Interaction with Spring Security Spring Security's reactive filter chain is itself a `WebFilter` (`WebFilterChainProxy`) placed at a well-known order. Custom filters that must run before/after security ordering should be set relative to it (Security exposes `SecurityWebFiltersOrder` for filters *within* its own chain; a top-level custom `WebFilter` uses ordinary `@Order`). ## Gotchas - **Two filters with the same order** → tie broken unpredictably; give distinct values. - **`@Order` on a class also implementing `Ordered`**: the `Ordered` interface (getOrder) generally takes precedence over the annotation for the same bean — pick one mechanism to avoid confusion. - **Order is per WebFilter bean list only.** It does not interleave with `HandlerFilterFunction` (functional web filters) or `HandlerInterceptor`-style concepts — different extension points. - Ordering affects both the request-in and response-out phases; reason about both. ## When to use which value - Very early (low/negative): tracing, correlation IDs, request mutation that everyone else depends on. - Late (high): filters that only care about the already-processed request or that add finishing touches close to the handler.
- If filter A has @Order(1) and filter B has @Order(2), which sees the response last on the way out?A. Lower order = outermost. A wraps B, so on the way in A runs first, and on the way out (post-chain composition) A runs last — making A ideal for total-latency metrics that must include B.
- What happens if you give two WebFilters the same @Order value?Their relative order becomes unpredictable — the comparator has no tiebreaker you control (it may fall back to bean name/registration order, which you shouldn't depend on). Assign distinct values when order matters.
saying these in an interview costs you the question
- Thinking higher @Order value runs first (it's the opposite)
- Assuming filters run in bean-declaration order without @Order
- Believing order only affects the request phase, not the response phase
- Confusing top-level WebFilter @Order with SecurityWebFiltersOrder inside Spring Security's own chain