What is WebServerFactoryCustomizer in Spring Boot, and when would you use it instead of application.properties?
answer
- functional interface: customize(factory)
- runs before server is built
- factory = builder (TomcatServletWebServerFactory)
- use when no server.* property exists
- embedded only, not external WAR
basics
~20 sIt's a bean whose customize(factory) method lets you configure the embedded web server (port, context path, error pages, compression) in Java code. Use it when a setting isn't exposed as a server.* property or must be computed at runtime.
solid answer
~40 sWebServerFactoryCustomizer<T> is a functional interface with one method, customize(T factory). Spring Boot applies every such bean to the embedded server's factory (e.g. TomcatServletWebServerFactory) before the server starts, so you can call setter methods like setPort, addErrorPages, or setCompression programmatically. You reach for it when a knob isn't available as an application.properties setting (server.*), when the value must be computed dynamically, or when you need low-level container objects like a raw Tomcat Connector. It coexists with property-based config: Boot's own customizer binds the server.* properties, and your customizer runs alongside it. It only affects embedded servers, not a WAR deployed to an external container.
code
java · 17 linesimport org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
@Component
class ServerCustomizer
implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
@Override
public void customize(ConfigurableServletWebServerFactory factory) {
factory.setPort(9000);
factory.setContextPath("/app");
factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/404"));
}
}go deeper
Should know it's a code-based way to configure the embedded server and that most settings are also available as server.* properties.
Should explain that the factory is a builder configured before startup and give an example like error pages or context path.
Should articulate when programmatic config beats properties and the embedded-only limitation.
Should frame it as the sanctioned extension seam that still cooperates with property binding, versus replacing the factory bean outright.
## The concept When Spring Boot runs an **embedded server** (Tomcat by default, or Jetty/Undertow), it does not create the server directly. It first creates a **WebServerFactory** bean — for a servlet app this is a `ConfigurableServletWebServerFactory` (concretely `TomcatServletWebServerFactory`) — and then calls `factory.getWebServer(...)` to build and start the actual server. The factory is a builder: everything about the server (port, SSL, compression, error pages, context path) is configured on the factory *before* the server is created. `WebServerFactoryCustomizer<T extends WebServerFactory>` is a **functional interface**: ```java @FunctionalInterface public interface WebServerFactoryCustomizer<T extends WebServerFactory> { void customize(T factory); } ``` You register an implementation as a Spring bean (`@Component` or `@Bean`). Boot's `WebServerFactoryCustomizerBeanPostProcessor` finds every such bean and invokes `customize` on the factory before the server is built. Inside `customize` you call the factory's setter methods. ## Why not just use properties? Most common settings **are** exposed as `server.*` properties (`server.port`, `server.servlet.context-path`, `server.compression.enabled`, `server.error.*`). For those, use `application.properties`/`application.yml` — it's simpler and testable. Boot itself binds those properties using an internal `WebServerFactoryCustomizer` (`ServletWebServerFactoryCustomizer`). You use your own customizer when: - **The knob isn't a property.** Example: adding a *second* Tomcat `Connector` (a management/HTTP port next to HTTPS), or registering a low-level `TomcatConnectorCustomizer`. - **The value is computed at runtime** — e.g. port derived from an environment lookup or a service registry. - **You need container-specific objects** the properties can't express (raw Tomcat `Connector`, Jetty handlers, Undertow builder tweaks). - **You want error pages mapped in code**: `factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/404"))`. ## Minimal example ```java @Component class PortAndErrorCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> { @Override public void customize(ConfigurableServletWebServerFactory factory) { factory.setPort(9000); factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/404")); } } ``` ## Key terms - **Embedded server**: the servlet container packaged *inside* your JAR and started by Boot (as opposed to deploying a WAR to a standalone Tomcat). - **WebServerFactory**: the builder object Boot uses to create the server; the configurable servlet variant is `ConfigurableServletWebServerFactory`. - **BeanPostProcessor**: a Spring extension point that lets code run against a bean during its initialization — Boot uses one to apply customizers. ## Gotchas - Runs **only for embedded servers**. If you deploy a traditional WAR to an external container, the customizer is ignored (there's no factory). - You **cannot read the running port** inside `customize` — the server doesn't exist yet. For the actual bound port, listen for `WebServerInitializedEvent` or inject `ServletWebServerApplicationContext`. - Prefer properties for anything expressible as a property; reserve the customizer for the genuinely programmatic cases.
- Can you read the actual port the server bound to inside customize()?No — the server isn't created yet when customize runs. To get the real bound port (e.g. when using server.port=0 for a random port), listen for WebServerInitializedEvent, or read it from the ServletWebServerApplicationContext after startup.
- Does a WebServerFactoryCustomizer do anything when you deploy your app as a WAR to a standalone Tomcat?No. It configures the *embedded* factory. Deploying a WAR to an external container means there is no WebServerFactory, so the customizer never runs; those servers are configured by the container itself.
saying these in an interview costs you the question
- Thinking it replaces application.properties rather than complementing it
- Claiming you can call getPort()/read the bound port inside customize
- Believing it also configures an external Tomcat when deploying a WAR