Explain the deferred initialization of DelegatingFilterProxy and which ApplicationContext it resolves its delegate from. What can go wrong?
answer
- init() does nothing; resolve on first doFilter, then cache
- default = root WebApplicationContext
- contextAttribute overrides context
- targetFilterLifecycle default false
- NoSuchBeanDefinitionException at request time, not startup
basics
~10 sThe proxy is created by the container at startup but doesn't fetch its delegate bean until the first request, giving the Spring context time to initialize. By default it looks in the root WebApplicationContext.
solid answer
~50 sDelegatingFilterProxy separates container filter creation from Spring bean resolution. The container instantiates and calls init() on the proxy at startup, but the target bean may not exist yet because the ApplicationContext is still initializing. So the proxy defers the bean lookup: on the first doFilter it finds the WebApplicationContext, retrieves the delegate by targetBeanName, caches it (double-checked), and delegates thereafter. By default it uses the root WebApplicationContext obtained via WebApplicationContextUtils, though contextAttribute can point at a specific one. Things that go wrong: the target bean name doesn't match any bean (NoSuchBeanDefinitionException on first request), the delegate lives in a child DispatcherServlet context rather than the root, the root context failed to start so the lookup finds nothing, or the proxy is mapped too narrowly and never runs. targetFilterLifecycle controls whether the delegate's servlet init/destroy are invoked.
go deeper
Know it looks up the bean lazily on first request, not at startup.
Explain caching and that it uses the root WebApplicationContext by default.
Enumerate failure modes: name mismatch, wrong/child context, narrow mapping, dispatcher types.
Reason about context topology in legacy WARs, targetFilterLifecycle semantics, and the security risk of mis-scoped mappings.
## Why deferral exists Servlet containers build their filter chain **early** in startup, often before (or concurrently with) the Spring `ApplicationContext` finishing its refresh. If `DelegatingFilterProxy` tried to resolve the delegate bean in `Filter.init()`, the bean might not yet exist. The design choice: **do nothing bean-related in init; resolve lazily**. ### The lazy lookup mechanism - `DelegatingFilterProxy.doFilter` checks a cached `delegate` field. - On first call (guarded by synchronization / double-checked pattern), it calls `initDelegate(wac)`: - Finds the `WebApplicationContext` via `findWebApplicationContext()`. - Retrieves the bean by `targetBeanName` (defaults to the filter's registered name). - If `targetFilterLifecycle` is `true`, calls the delegate's `init(FilterConfig)`. - Caches the delegate for subsequent requests. - All later requests skip lookup and delegate directly. ## Which context? `findWebApplicationContext()`: - If the `contextAttribute` init-param is set, it looks up that named attribute in the `ServletContext`. - Otherwise it defaults to the **root** `WebApplicationContext` created by `ContextLoaderListener` (attribute `WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE`). - In Spring Boot there is a single application context, so 'root' is simply that context — no ambiguity. ## Failure modes 1. **Bean name mismatch** — `targetBeanName` (default = filter name) doesn't resolve. First request throws `NoSuchBeanDefinitionException`. Startup looks fine; failure only appears at request time. 2. **Wrong context** — In a classic app with a root context AND a `DispatcherServlet` child context, if the delegate bean is only in the child context, the default root lookup fails. Fix: move the bean to root or set `contextAttribute`. 3. **Root context failed** — If the Spring context didn't start, `findWebApplicationContext()` throws / returns null and the filter can't initialize. 4. **Mapping too narrow** — Proxy mapped to a subset of URLs; secured endpoints outside the mapping bypass security entirely (silent, dangerous). 5. **targetFilterLifecycle confusion** — Default `false` means the delegate's servlet `init`/`destroy` are NOT called by the proxy (Spring owns lifecycle). If a custom delegate relies on servlet init, set it `true`. 6. **Dispatcher-type gaps** — Default REQUEST-only; ASYNC/ERROR/FORWARD dispatches may skip the proxy unless configured. 7. **Thread-safety of first request** — The lazy init is synchronized; a burst of concurrent first requests is safe but serializes briefly on init. ## Interaction with FilterChainProxy Once resolved, the cached delegate is normally the `FilterChainProxy` (`springSecurityFilterChain`). From then on the proxy is a near-zero-cost pass-through. The deferral cost is paid once. ## Practical debugging - Symptom: security not applied -> check the proxy is registered and mapped to `/*`. - Symptom: `NoSuchBeanDefinitionException: No bean named 'springSecurityFilterChain'` on first hit -> name mismatch or context issue. - Symptom: works in Boot but breaks in WAR/legacy -> context lookup (root vs child) mismatch. ## When this matters Mostly in non-trivial or legacy multi-context deployments, custom filter bridging, or when diagnosing first-request errors. In a standard Boot app the deferral is invisible and 'just works'.
- A colleague sees NoSuchBeanDefinitionException for springSecurityFilterChain only on the first HTTP request, not at startup. Why only then?Because DelegatingFilterProxy resolves its delegate lazily on the first doFilter, not in init(); a name/context mismatch surfaces at request time, after a clean startup.
- What does the targetFilterLifecycle init-param control?Whether the proxy invokes the delegate's servlet Filter.init/destroy. Default false, because Spring already manages the bean's lifecycle.
saying these in an interview costs you the question
- Saying the delegate is resolved in init() at startup
- Assuming it always searches the DispatcherServlet child context
- Believing the proxy invokes the delegate's init/destroy by default