docs
This documentation has been enhanced for AI coding agents with comprehensive examples, complete API signatures, thread safety notes, error handling patterns, and production-ready usage patterns.
| Component | Purpose | Thread Safety | Key Features |
|---|---|---|---|
ServletRegistrationBean | Register servlets | Thread-safe (Spring managed) | URL mappings, init params, load-on-startup |
FilterRegistrationBean | Register filters | Thread-safe (Spring managed) | URL patterns, servlet names, dispatcher types |
ServletListenerRegistrationBean | Register listeners | Thread-safe (Spring managed) | Servlet context, session, request listeners |
DelegatingFilterProxyRegistrationBean | Proxy Spring filters | Thread-safe | Lazy initialization, Spring Security integration |
ServletContextInitializer | Context initialization | N/A (functional interface) | Programmatic servlet context configuration |
| Context Type | Purpose | Thread Safety | Application Type |
|---|---|---|---|
AnnotationConfigServletWebApplicationContext | Servlet web apps | Thread-safe | Traditional servlet-based |
AnnotationConfigReactiveWebApplicationContext | Reactive web apps | Thread-safe | WebFlux reactive |
GenericReactiveWebApplicationContext | Generic reactive | Thread-safe | Reactive with manual configuration |
ServletWebServerApplicationContext | Embedded servlet server | Thread-safe | Spring Boot servlet apps |
ReactiveWebServerApplicationContext | Embedded reactive server | Thread-safe | Spring Boot reactive apps |
| Factory | Purpose | Thread Safety | Supported Servers |
|---|---|---|---|
ServletWebServerFactory | Create servlet servers | Thread-safe | Tomcat, Jetty, Undertow |
ReactiveWebServerFactory | Create reactive servers | Thread-safe | Netty, Tomcat, Jetty, Undertow |
TomcatServletWebServerFactory | Tomcat servlet server | Thread-safe | Tomcat-specific |
TomcatReactiveWebServerFactory | Tomcat reactive server | Thread-safe | Tomcat with WebFlux |
JettyServletWebServerFactory | Jetty servlet server | Thread-safe | Jetty-specific |
JettyReactiveWebServerFactory | Jetty reactive server | Thread-safe | Jetty with WebFlux |
UndertowServletWebServerFactory | Undertow servlet server | Thread-safe | Undertow-specific |
UndertowReactiveWebServerFactory | Undertow reactive server | Thread-safe | Undertow with WebFlux |
| Component | Purpose | Thread Safety | Use Cases |
|---|---|---|---|
ErrorPage | Error page definition | Thread-safe (immutable) | Status-based, exception-based routing |
ErrorPageRegistry | Error page registration | Thread-safe | Custom error page configuration |
ErrorPageRegistrar | Bulk error registration | N/A (callback) | Register multiple error pages |
ErrorPageFilter | Non-embedded error handling | Thread-safe | WAR deployments without embedded server |
ErrorAttributeOptions | Error attribute control | Thread-safe (immutable) | Include/exclude stack traces, messages, etc. |
| Component | Purpose | Thread Safety | Behavior |
|---|---|---|---|
GracefulShutdownCallback | Shutdown notification | Thread-safe | Async shutdown completion notification |
GracefulShutdownResult | Shutdown result | Thread-safe (enum) | SUCCESS, REQUESTS_ACTIVE, IDLE |
Shutdown | Shutdown mode | Thread-safe (enum) | IMMEDIATE, GRACEFUL |
This document provides comprehensive coverage of Spring Boot's web application support for both servlet-based and reactive applications, including servlet registration, context management, error handling, and reactive web support.
Spring Boot provides a comprehensive set of classes for programmatically registering servlets, filters, and listeners in a Servlet 3.0+ environment.
The foundational interface for servlet context initialization.
package org.springframework.boot.web.servlet;
/**
* Interface used to configure a Servlet 3.0+ ServletContext programmatically.
* Unlike WebApplicationInitializer, classes that implement this interface
* will not be automatically bootstrapped by the servlet container.
*
* This interface is designed to have a lifecycle managed by Spring, not the
* servlet container.
*
* @since 4.0.0
*/
@FunctionalInterface
public interface ServletContextInitializer {
/**
* Configure the given ServletContext with any servlets, filters, listeners
* context-params and attributes necessary for initialization.
*
* @param servletContext the ServletContext to initialize
* @throws ServletException if any call against the given ServletContext
* throws a ServletException
*/
void onStartup(ServletContext servletContext) throws ServletException;
}Usage Example:
@Configuration
public class WebConfig {
@Bean
public ServletContextInitializer customInitializer() {
return servletContext -> {
servletContext.setSessionTimeout(30);
servletContext.setRequestCharacterEncoding("UTF-8");
};
}
}A collection of ServletContextInitializer instances obtained from a ListableBeanFactory. This class automatically discovers and adapts servlet components (servlets, filters, and listeners) as well as explicit ServletContextInitializer beans, sorting them appropriately for initialization.
Thread Safety: Instances are immutable after construction and thread-safe for iteration.
package org.springframework.boot.web.servlet;
import java.util.AbstractCollection;
import java.util.Iterator;
import org.springframework.beans.factory.ListableBeanFactory;
/**
* A collection of ServletContextInitializers obtained from a ListableBeanFactory.
* Includes all ServletContextInitializer beans and also adapts Servlet, Filter
* and certain EventListener beans.
*
* Items are sorted so that adapted beans are top (Servlet, Filter then EventListener)
* and direct ServletContextInitializer beans are at the end. Further sorting is
* applied within these groups using the AnnotationAwareOrderComparator.
*
* @since 1.4.0
*/
public class ServletContextInitializerBeans
extends AbstractCollection<ServletContextInitializer> {
/**
* Create a new ServletContextInitializerBeans instance.
* Discovers and adapts all relevant beans from the given bean factory.
*
* @param beanFactory the bean factory to search
* @param initializerTypes the initializer types to include (defaults to
* ServletContextInitializer.class if not specified)
*/
@SafeVarargs
public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
Class<? extends ServletContextInitializer>... initializerTypes) {
// Implementation scans bean factory and creates sorted collection
}
/**
* Returns an iterator over the ServletContextInitializers in sorted order.
*
* Iteration Order:
* 1. Servlet beans (adapted to ServletRegistrationBean)
* 2. Filter beans (adapted to FilterRegistrationBean)
* 3. EventListener beans (adapted to ServletListenerRegistrationBean)
* 4. Direct ServletContextInitializer beans
*
* Within each group, beans are sorted by @Order annotation or Ordered interface.
*
* @return an iterator over ServletContextInitializers
*/
@Override
public Iterator<ServletContextInitializer> iterator() {
// Returns iterator over sorted list
}
/**
* Returns the number of ServletContextInitializers in the collection.
*
* @return the total count of initializers
*/
@Override
public int size() {
// Returns size of sorted list
}
}Automatic Bean Discovery and Adaptation:
ServletContextInitializerBeans automatically discovers and adapts the following bean types:
Servlet Beans - Automatically wrapped in ServletRegistrationBean
/{beanName}/ or / for dispatcherServlet@ServletRegistration annotation for configurationFilter Beans - Automatically wrapped in FilterRegistrationBean
@FilterRegistration annotation for configurationEventListener Beans - Automatically wrapped in ServletListenerRegistrationBean
ServletContextInitializer Beans - Used directly
Initialization Order:
The collection sorts initializers to ensure proper initialization sequence:
Priority 1 (Highest): Servlet beans (adapted)
Priority 2: Filter beans (adapted)
Priority 3: EventListener beans (adapted)
Priority 4 (Lowest): Direct ServletContextInitializer beansWithin each priority level, beans are further sorted by:
@Order annotation valueOrdered interface implementationOrdered.LOWEST_PRECEDENCE if not specifiedUsage Example:
import org.springframework.boot.web.servlet.ServletContextInitializerBeans;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.context.ApplicationContext;
@Component
public class ServletInitializerManager {
private final ServletContextInitializerBeans initializerBeans;
public ServletInitializerManager(ApplicationContext applicationContext) {
// Discover all ServletContextInitializer-related beans
this.initializerBeans = new ServletContextInitializerBeans(
applicationContext.getBeanFactory()
);
}
public void initializeServletContext(ServletContext servletContext)
throws ServletException {
// Initialize servlet context with all discovered beans in sorted order
for (ServletContextInitializer initializer : initializerBeans) {
initializer.onStartup(servletContext);
}
}
public int getInitializerCount() {
return initializerBeans.size();
}
}Automatic Servlet Registration Example:
import jakarta.servlet.Servlet;
import jakarta.servlet.http.HttpServlet;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
@Configuration
public class ServletConfig {
/**
* This servlet bean is automatically discovered and registered.
* No explicit ServletRegistrationBean needed.
*/
@Bean
@Order(1)
public Servlet myServlet() {
return new HttpServlet() {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
resp.getWriter().write("Hello from auto-registered servlet!");
}
};
}
/**
* This servlet uses annotation-based configuration.
*/
@Bean
@ServletRegistration(
name = "customServlet",
urlMappings = {"/custom/*", "/api/*"},
loadOnStartup = 1
)
@Order(1)
public Servlet annotatedServlet() {
return new CustomServlet();
}
}The myServlet bean above will be automatically:
ServletContextInitializerBeansServletRegistrationBean/myServlet/Filter Auto-Registration Example:
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
@Configuration
public class FilterConfig {
/**
* Filter bean automatically registered to all URLs.
*/
@Bean
@Order(1)
public Filter loggingFilter() {
return (request, response, chain) -> {
System.out.println("Request: " + request);
chain.doFilter(request, response);
System.out.println("Response: " + response);
};
}
/**
* Filter with annotation-based configuration.
*/
@Bean
@FilterRegistration(
urlPatterns = {"/api/*"},
servletNames = {"dispatcherServlet"},
dispatcherTypes = {DispatcherType.REQUEST, DispatcherType.FORWARD}
)
@Order(10)
public Filter apiFilter() {
return new ApiSecurityFilter();
}
}Listener Auto-Registration Example:
import jakarta.servlet.ServletContextListener;
import jakarta.servlet.ServletContextEvent;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ListenerConfig {
/**
* Listener bean automatically registered.
*/
@Bean
public ServletContextListener contextListener() {
return new ServletContextListener() {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("Servlet context initialized");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("Servlet context destroyed");
}
};
}
}Filtering by Initializer Type:
import org.springframework.boot.web.servlet.ServletContextInitializerBeans;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
// Only discover ServletRegistrationBean and FilterRegistrationBean
ServletContextInitializerBeans beans = new ServletContextInitializerBeans(
beanFactory,
ServletRegistrationBean.class,
FilterRegistrationBean.class
);
// Listeners and other initializer types are excludedBest Practices:
Use @Order for Control - Specify initialization order explicitly
@Bean
@Order(1) // Lower numbers initialize first
public Filter firstFilter() { ... }
@Bean
@Order(10)
public Filter secondFilter() { ... }Prefer Bean Registration - Let Spring Boot auto-register simple components
// GOOD: Simple bean registration
@Bean
public Filter myFilter() {
return new MyFilter();
}
// AVOID: Manual registration unless customization needed
@Bean
public FilterRegistrationBean<MyFilter> myFilterRegistration() {
FilterRegistrationBean<MyFilter> registration =
new FilterRegistrationBean<>(new MyFilter());
registration.setUrlPatterns(List.of("/*"));
return registration;
}Use Annotations for Configuration - Apply @ServletRegistration or @FilterRegistration
@Bean
@ServletRegistration(
urlMappings = {"/api/*"},
loadOnStartup = 1
)
@Order(1)
public Servlet apiServlet() {
return new ApiServlet();
}Explicit Registration for Complex Cases - Use registration beans when needed
@Bean
public FilterRegistrationBean<MyFilter> complexFilterRegistration() {
FilterRegistrationBean<MyFilter> registration =
new FilterRegistrationBean<>(new MyFilter());
registration.setUrlPatterns(List.of("/secure/*", "/admin/*"));
registration.setDispatcherTypes(
DispatcherType.REQUEST,
DispatcherType.FORWARD,
DispatcherType.ERROR
);
registration.setMatchAfter(false);
registration.setOrder(5);
return registration;
}Thread Safety:
ServletContextInitializerBeans collection is immutable after constructionCommon Use Cases:
Embedded Servlet Container Startup - Spring Boot uses this class internally to initialize the embedded servlet container with all discovered components
Testing - Verify correct servlet/filter/listener registration order
Custom Initialization Logic - Extend or wrap the initialization process
Diagnostic Tools - Inspect what components will be registered
Integration with Spring Boot:
Spring Boot's embedded servlet container support (Tomcat, Jetty, Undertow) automatically uses ServletContextInitializerBeans to:
This provides a seamless bridge between Spring's bean management and the Servlet API initialization process.
A ServletContextInitializer to register servlets in a Servlet 3.0+ container.
package org.springframework.boot.web.servlet;
/**
* A ServletContextInitializer to register Servlets in a Servlet 3.0+ container.
* Similar to ServletContext.addServlet() but with a Spring Bean friendly design.
*
* The servlet must be specified before calling onStartup. URL mapping can be
* configured using setUrlMappings or omitted when mapping to '/*'.
*
* @param <T> the type of the Servlet to register
* @since 1.4.0
*/
public class ServletRegistrationBean<T extends Servlet>
extends DynamicRegistrationBean<ServletRegistration.Dynamic> {
/**
* Create a new ServletRegistrationBean instance.
*/
public ServletRegistrationBean() { }
/**
* Create a new ServletRegistrationBean instance with the specified
* Servlet and URL mappings.
*
* @param servlet the servlet being mapped
* @param urlMappings the URLs being mapped
*/
public ServletRegistrationBean(T servlet, String... urlMappings) { }
/**
* Create a new ServletRegistrationBean instance with the specified
* Servlet and URL mappings.
*
* @param servlet the servlet being mapped
* @param alwaysMapUrl if omitted URL mappings should be replaced with '/*'
* @param urlMappings the URLs being mapped
*/
public ServletRegistrationBean(T servlet, boolean alwaysMapUrl,
String... urlMappings) { }
/**
* Sets the servlet to be registered.
*
* @param servlet the servlet
*/
public void setServlet(T servlet) { }
/**
* Return the servlet being registered.
*
* @return the servlet
*/
public T getServlet() { }
/**
* Set the URL mappings for the servlet. If not specified, the mapping
* will default to '/'.
*
* @param urlMappings the mappings to set
*/
public void setUrlMappings(Collection<String> urlMappings) { }
/**
* Return a mutable collection of the URL mappings for the servlet.
*
* @return the urlMappings
*/
public Collection<String> getUrlMappings() { }
/**
* Add URL mappings for the servlet.
*
* @param urlMappings the mappings to add
*/
public void addUrlMappings(String... urlMappings) { }
/**
* Sets the loadOnStartup priority.
*
* @param loadOnStartup if load on startup is enabled
*/
public void setLoadOnStartup(int loadOnStartup) { }
/**
* Set the multi-part configuration.
*
* @param multipartConfig the multipart configuration to set or null
*/
public void setMultipartConfig(MultipartConfigElement multipartConfig) { }
/**
* Returns the multi-part configuration to be applied or null.
*
* @return the multipart config
*/
public MultipartConfigElement getMultipartConfig() { }
/**
* Returns the servlet name that will be registered.
*
* @return the servlet name
*/
public String getServletName() { }
}Usage Example:
@Configuration
public class ServletConfig {
@Bean
public ServletRegistrationBean<MyServlet> myServlet() {
ServletRegistrationBean<MyServlet> bean =
new ServletRegistrationBean<>(new MyServlet(), "/api/*");
bean.setLoadOnStartup(1);
bean.addInitParameter("encoding", "UTF-8");
return bean;
}
@Bean
public ServletRegistrationBean<FileUploadServlet> fileUploadServlet() {
ServletRegistrationBean<FileUploadServlet> bean =
new ServletRegistrationBean<>(new FileUploadServlet(), "/upload/*");
MultipartConfigElement multipart = new MultipartConfigElement(
"/tmp", // location
10485760, // maxFileSize (10MB)
20971520, // maxRequestSize (20MB)
5242880 // fileSizeThreshold (5MB)
);
bean.setMultipartConfig(multipart);
return bean;
}
}Abstract base class for filter registration beans that provides common filter configuration functionality.
package org.springframework.boot.web.servlet;
/**
* Abstract base ServletContextInitializer to register Filters in a
* Servlet 3.0+ container.
*
* @param <T> the type of Filter to register
* @since 1.5.22
*/
public abstract class AbstractFilterRegistrationBean<T extends Filter>
extends DynamicRegistrationBean<FilterRegistration.Dynamic> {
/**
* Create a new instance to be registered with the specified
* ServletRegistrationBeans.
*
* @param servletRegistrationBeans associate ServletRegistrationBeans
*/
AbstractFilterRegistrationBean(ServletRegistrationBean<?>... servletRegistrationBeans) { }
/**
* Set ServletRegistrationBeans that the filter will be registered against.
*
* @param servletRegistrationBeans the Servlet registration beans
*/
public void setServletRegistrationBeans(
Collection<? extends ServletRegistrationBean<?>> servletRegistrationBeans) { }
/**
* Return a mutable collection of the ServletRegistrationBean that the filter
* will be registered against.
*
* @return the Servlet registration beans
*/
public Collection<ServletRegistrationBean<?>> getServletRegistrationBeans() { }
/**
* Add ServletRegistrationBeans for the filter.
*
* @param servletRegistrationBeans the servlet registration beans to add
*/
public void addServletRegistrationBeans(
ServletRegistrationBean<?>... servletRegistrationBeans) { }
/**
* Set servlet names that the filter will be registered against.
*
* @param servletNames the servlet names
*/
public void setServletNames(Collection<String> servletNames) { }
/**
* Return a mutable collection of servlet names that the filter will be
* registered against.
*
* @return the servlet names
*/
public Collection<String> getServletNames() { }
/**
* Add servlet names for the filter.
*
* @param servletNames the servlet names to add
*/
public void addServletNames(String... servletNames) { }
/**
* Set the URL patterns that the filter will be registered against.
*
* @param urlPatterns the URL patterns
*/
public void setUrlPatterns(Collection<String> urlPatterns) { }
/**
* Return a mutable collection of URL patterns, as defined in the Servlet
* specification, that the filter will be registered against.
*
* @return the URL patterns
*/
public Collection<String> getUrlPatterns() { }
/**
* Add URL patterns, as defined in the Servlet specification, that the filter
* will be registered against.
*
* @param urlPatterns the URL patterns
*/
public void addUrlPatterns(String... urlPatterns) { }
/**
* Determines the DispatcherType dispatcher types for which the filter should
* be registered.
*
* @return the dispatcher types, never null
* @since 3.2.0
*/
public EnumSet<DispatcherType> determineDispatcherTypes() { }
/**
* Convenience method to set dispatcher types using the specified elements.
*
* @param first the first dispatcher type
* @param rest additional dispatcher types
*/
public void setDispatcherTypes(DispatcherType first, DispatcherType... rest) { }
/**
* Sets the dispatcher types that should be used with the registration.
*
* @param dispatcherTypes the dispatcher types
*/
public void setDispatcherTypes(EnumSet<DispatcherType> dispatcherTypes) { }
/**
* Set if the filter mappings should be matched after any declared filter
* mappings of the ServletContext.
*
* @param matchAfter if filter mappings are matched after
*/
public void setMatchAfter(boolean matchAfter) { }
/**
* Return if filter mappings should be matched after any declared Filter
* mappings of the ServletContext.
*
* @return if filter mappings are matched after
*/
public boolean isMatchAfter() { }
/**
* Return the Filter to be registered.
*
* @return the filter
*/
public abstract T getFilter();
/**
* Returns the filter name that will be registered.
*
* @return the filter name
* @since 3.2.0
*/
public String getFilterName() { }
}A ServletContextInitializer to register filters in a Servlet 3.0+ container.
package org.springframework.boot.web.servlet;
/**
* A ServletContextInitializer to register Filters in a Servlet 3.0+ container.
* Similar to ServletContext.addFilter() but with a Spring Bean friendly design.
*
* The Filter must be specified before calling onStartup. Registrations can be
* associated with URL patterns and/or servlets. When no URL pattern or servlets
* are specified, the filter will be associated to '/*'.
*
* @param <T> the type of Filter to register
* @since 1.4.0
*/
public class FilterRegistrationBean<T extends Filter>
extends AbstractFilterRegistrationBean<T> {
/**
* Create a new FilterRegistrationBean instance.
*/
public FilterRegistrationBean() { }
/**
* Create a new FilterRegistrationBean instance to be registered with
* the specified ServletRegistrationBeans.
*
* @param filter the filter to register
* @param servletRegistrationBeans associate ServletRegistrationBeans
*/
public FilterRegistrationBean(T filter,
ServletRegistrationBean<?>... servletRegistrationBeans) { }
/**
* Return the filter being registered.
*
* @return the filter
*/
public T getFilter() { }
/**
* Set the filter to be registered.
*
* @param filter the filter
*/
public void setFilter(T filter) { }
}Usage Example:
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<LoggingFilter> loggingFilter() {
FilterRegistrationBean<LoggingFilter> bean =
new FilterRegistrationBean<>(new LoggingFilter());
bean.addUrlPatterns("/api/*");
bean.setOrder(1);
return bean;
}
@Bean
public FilterRegistrationBean<CharacterEncodingFilter> encodingFilter() {
FilterRegistrationBean<CharacterEncodingFilter> bean =
new FilterRegistrationBean<>();
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
filter.setForceEncoding(true);
bean.setFilter(filter);
bean.addUrlPatterns("/*");
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
}
}A ServletContextInitializer to register event listeners in a Servlet 3.0+ container.
package org.springframework.boot.web.servlet;
/**
* A ServletContextInitializer to register EventListeners in a Servlet 3.0+
* container. Similar to ServletContext.addListener() but with a Spring Bean
* friendly design.
*
* This bean can be used to register the following types of listener:
* - ServletContextAttributeListener
* - ServletRequestListener
* - ServletRequestAttributeListener
* - HttpSessionAttributeListener
* - HttpSessionIdListener
* - HttpSessionListener
* - ServletContextListener
*
* @param <T> the type of listener
* @since 1.4.0
*/
public class ServletListenerRegistrationBean<T extends EventListener>
extends RegistrationBean {
/**
* Create a new ServletListenerRegistrationBean instance.
*/
public ServletListenerRegistrationBean() { }
/**
* Create a new ServletListenerRegistrationBean instance.
*
* @param listener the listener to register
*/
public ServletListenerRegistrationBean(T listener) { }
/**
* Set the listener that will be registered.
*
* @param listener the listener to register
*/
public void setListener(T listener) { }
/**
* Return the listener to be registered.
*
* @return the listener to be registered
*/
public T getListener() { }
/**
* Returns true if the specified listener is one of the supported types.
*
* @param listener the listener to test
* @return if the listener is of a supported type
*/
public static boolean isSupportedType(EventListener listener) { }
/**
* Return the supported types for this registration.
*
* @return the supported types
*/
public static Set<Class<?>> getSupportedTypes() { }
}Usage Example:
@Configuration
public class ListenerConfig {
@Bean
public ServletListenerRegistrationBean<SessionListener> sessionListener() {
return new ServletListenerRegistrationBean<>(new SessionListener());
}
@Bean
public ServletListenerRegistrationBean<RequestListener> requestListener() {
ServletListenerRegistrationBean<RequestListener> bean =
new ServletListenerRegistrationBean<>();
bean.setListener(new RequestListener());
bean.setOrder(1);
return bean;
}
}
// Custom session listener
class SessionListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent se) {
System.out.println("Session created: " + se.getSession().getId());
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
System.out.println("Session destroyed: " + se.getSession().getId());
}
}A ServletContextInitializer to register DelegatingFilterProxy instances that delegate to Spring-managed filter beans.
package org.springframework.boot.web.servlet;
/**
* A ServletContextInitializer to register DelegatingFilterProxys in a
* Servlet 3.0+ container. Unlike FilterRegistrationBean, referenced filters
* are not instantiated early. If the delegate filter bean is marked @Lazy,
* it won't be instantiated until the filter is called.
*
* The targetBeanName will be used as the filter name if not otherwise specified.
*
* @since 1.4.0
*/
public class DelegatingFilterProxyRegistrationBean
extends AbstractFilterRegistrationBean<DelegatingFilterProxy>
implements ApplicationContextAware {
/**
* Create a new DelegatingFilterProxyRegistrationBean instance to be
* registered with the specified ServletRegistrationBeans.
*
* @param targetBeanName name of the target filter bean to look up in
* the Spring application context (must not be null)
* @param servletRegistrationBeans associate ServletRegistrationBeans
*/
public DelegatingFilterProxyRegistrationBean(
String targetBeanName,
ServletRegistrationBean<?>... servletRegistrationBeans) { }
/**
* Get the target bean name.
*
* @return the target bean name
*/
protected String getTargetBeanName() { }
/**
* Return the filter to be registered.
*
* @return the filter
*/
@Override
public DelegatingFilterProxy getFilter() { }
}Usage Example:
@Configuration
public class SecurityFilterConfig {
@Bean
public DelegatingFilterProxyRegistrationBean securityFilterChain() {
DelegatingFilterProxyRegistrationBean bean =
new DelegatingFilterProxyRegistrationBean("springSecurityFilterChain");
bean.addUrlPatterns("/*");
bean.setOrder(SecurityProperties.DEFAULT_FILTER_ORDER);
return bean;
}
// The actual filter bean (can be @Lazy)
@Bean("springSecurityFilterChain")
@Lazy
public Filter springSecurityFilterChain() {
return new CustomSecurityFilter();
}
}Base class for all servlet registration beans.
package org.springframework.boot.web.servlet;
/**
* Base class for Servlet 3.0+ based registration beans.
*
* @since 1.4.0
*/
public abstract class RegistrationBean
implements ServletContextInitializer, Ordered {
/**
* Flag to indicate that the registration is enabled.
*
* @param enabled the enabled to set
*/
public void setEnabled(boolean enabled) { }
/**
* Return if the registration is enabled.
*
* @return if enabled (default true)
*/
public boolean isEnabled() { }
/**
* Set the order of the registration bean.
*
* @param order the order
*/
public void setOrder(int order) { }
/**
* Get the order of the registration bean.
*
* @return the order
*/
@Override
public int getOrder() { }
/**
* Return a description of the registration.
*
* @return a description of the registration
*/
protected abstract String getDescription();
/**
* Register this bean with the servlet context.
*
* @param description a description of the item being registered
* @param servletContext the servlet context
*/
protected abstract void register(String description, ServletContext servletContext);
}Spring Boot provides specialized application contexts and environments for servlet-based web applications.
An annotation-based servlet web application context.
package org.springframework.boot.web.context.servlet;
/**
* GenericWebApplicationContext that accepts annotated classes as input -
* in particular @Configuration-annotated classes, but also plain @Component
* classes and JSR-330 compliant classes using javax.inject annotations.
*
* Allows for registering classes one by one (specifying class names as config
* location) as well as for classpath scanning (specifying base packages as
* config location).
*
* Note: In case of multiple @Configuration classes, later @Bean definitions
* will override ones defined in earlier loaded files.
*
* @since 2.2.0
*/
public class AnnotationConfigServletWebApplicationContext
extends GenericWebApplicationContext
implements AnnotationConfigRegistry {
/**
* Create a new AnnotationConfigServletWebApplicationContext that needs
* to be populated through register calls and then manually refreshed.
*/
public AnnotationConfigServletWebApplicationContext() { }
/**
* Create a new AnnotationConfigServletWebApplicationContext with the
* given DefaultListableBeanFactory.
*
* @param beanFactory the DefaultListableBeanFactory instance to use
* for this context
*/
public AnnotationConfigServletWebApplicationContext(
DefaultListableBeanFactory beanFactory) { }
/**
* Create a new AnnotationConfigServletWebApplicationContext, deriving
* bean definitions from the given annotated classes and automatically
* refreshing the context.
*
* @param annotatedClasses one or more annotated classes,
* e.g. @Configuration classes
*/
public AnnotationConfigServletWebApplicationContext(
Class<?>... annotatedClasses) { }
/**
* Create a new AnnotationConfigServletWebApplicationContext, scanning
* for bean definitions in the given packages and automatically
* refreshing the context.
*
* @param basePackages the packages to check for annotated classes
*/
public AnnotationConfigServletWebApplicationContext(String... basePackages) { }
/**
* Set the Environment to use.
*
* @param environment the environment
*/
@Override
public void setEnvironment(ConfigurableEnvironment environment) { }
/**
* Provide a custom BeanNameGenerator for use with
* AnnotatedBeanDefinitionReader and/or ClassPathBeanDefinitionScanner.
*
* @param beanNameGenerator the bean name generator
*/
public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) { }
/**
* Set the ScopeMetadataResolver to use for detected bean classes.
*
* @param scopeMetadataResolver the scope metadata resolver
*/
public void setScopeMetadataResolver(
ScopeMetadataResolver scopeMetadataResolver) { }
/**
* Register one or more annotated classes to be processed.
*
* @param annotatedClasses one or more annotated classes,
* e.g. @Configuration classes
*/
@Override
public final void register(Class<?>... annotatedClasses) { }
/**
* Perform a scan within the specified base packages.
*
* @param basePackages the packages to check for annotated classes
*/
@Override
public final void scan(String... basePackages) { }
}Usage Example:
public class WebApplicationBootstrap {
public static void main(String[] args) {
// Create context with configuration classes
AnnotationConfigServletWebApplicationContext context =
new AnnotationConfigServletWebApplicationContext(AppConfig.class);
// Or create and configure manually
AnnotationConfigServletWebApplicationContext context2 =
new AnnotationConfigServletWebApplicationContext();
context2.register(AppConfig.class, WebConfig.class);
context2.scan("com.example.controllers");
context2.refresh();
}
}The standard servlet environment for Spring Boot applications.
package org.springframework.boot.web.context.servlet;
/**
* StandardServletEnvironment for typical use in a typical SpringApplication.
*
* @since 4.0.0
*/
public class ApplicationServletEnvironment extends StandardServletEnvironment {
/**
* Return the active profiles property name (returns null to delegate
* to Spring Boot's profile management).
*
* @return null
*/
@Override
protected String doGetActiveProfilesProperty() { }
/**
* Return the default profiles property name (returns null to delegate
* to Spring Boot's profile management).
*
* @return null
*/
@Override
protected String doGetDefaultProfilesProperty() { }
/**
* Create a property resolver that uses ConfigurationPropertySources.
*
* @param propertySources the property sources
* @return the configured property resolver
*/
@Override
protected ConfigurablePropertyResolver createPropertyResolver(
MutablePropertySources propertySources) { }
}An opinionated WebApplicationInitializer to run a SpringApplication from a traditional WAR deployment.
package org.springframework.boot.web.servlet.support;
/**
* An opinionated WebApplicationInitializer to run a SpringApplication from
* a traditional WAR deployment. Binds Servlet, Filter and ServletContextInitializer
* beans from the application context to the server.
*
* To configure the application, either override the configure() method or
* make the initializer itself a @Configuration.
*
* Note: A WebApplicationInitializer is only needed if you are building a
* war file. If you prefer to run an embedded web server, you won't need this.
*
* @since 2.0.0
*/
public abstract class SpringBootServletInitializer
implements WebApplicationInitializer {
/**
* Set if the ErrorPageFilter should be registered. Set to false if
* error page mappings should be handled through the server and not
* Spring Boot.
*
* @param registerErrorPageFilter if the ErrorPageFilter should be registered
*/
protected final void setRegisterErrorPageFilter(
boolean registerErrorPageFilter) { }
/**
* Called when the servlet container starts up the application.
*
* @param servletContext the servlet context
* @throws ServletException if startup fails
*/
@Override
public void onStartup(ServletContext servletContext) throws ServletException { }
/**
* Deregisters the JDBC drivers that were registered by the application.
*
* @param servletContext the web application's servlet context
* @since 2.3.0
*/
protected void deregisterJdbcDrivers(ServletContext servletContext) { }
/**
* Shuts down the reactor Schedulers that were initialized.
*
* @param servletContext the web application's servlet context
* @since 3.4.0
*/
protected void shutDownSharedReactorSchedulers(ServletContext servletContext) { }
/**
* Creates the root application context.
*
* @param servletContext the servlet context
* @return the root application context
*/
protected WebApplicationContext createRootApplicationContext(
ServletContext servletContext) { }
/**
* Returns the SpringApplicationBuilder that is used to configure and
* create the SpringApplication.
*
* @return the SpringApplicationBuilder
*/
protected SpringApplicationBuilder createSpringApplicationBuilder() { }
/**
* Called to run a fully configured SpringApplication.
*
* @param application the application to run
* @return the WebApplicationContext
*/
protected WebApplicationContext run(SpringApplication application) { }
/**
* Configure the application. Normally all you would need to do is add
* sources (e.g. config classes).
*
* @param builder a builder for the application context
* @return the application builder
*/
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { }
}Usage Example:
public class MyWebAppInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(MyApplication.class);
}
// Optional: disable error page filter if server handles errors
public MyWebAppInitializer() {
setRegisterErrorPageFilter(false);
}
}
// Then package as WAR and deploy to servlet containerA servlet filter that provides error page handling for non-embedded applications.
package org.springframework.boot.web.servlet.support;
/**
* A Servlet Filter that provides an ErrorPageRegistry for non-embedded
* applications (i.e. deployed WAR files). It registers error pages and
* handles application errors by filtering requests and forwarding to the
* error pages instead of letting the server handle them.
*
* @since 2.0.0
*/
public class ErrorPageFilter implements Filter, ErrorPageRegistry, Ordered {
/**
* The name of the servlet attribute containing request URI.
*/
public static final String ERROR_REQUEST_URI =
"jakarta.servlet.error.request_uri";
/**
* Initialize the filter.
*
* @param filterConfig the filter configuration
* @throws ServletException if initialization fails
*/
@Override
public void init(FilterConfig filterConfig) throws ServletException { }
/**
* Apply the filter.
*
* @param request the servlet request
* @param response the servlet response
* @param chain the filter chain
* @throws IOException if an I/O error occurs
* @throws ServletException if a servlet error occurs
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException { }
/**
* Add error pages to be handled by this filter.
*
* @param errorPages the error pages
*/
@Override
public void addErrorPages(ErrorPage... errorPages) { }
/**
* Destroy the filter.
*/
@Override
public void destroy() { }
/**
* Get the order of this filter.
*
* @return the order (HIGHEST_PRECEDENCE + 1)
*/
@Override
public int getOrder() { }
/**
* Return a description for the given request.
*
* @param request the source request
* @return the description
*/
protected String getDescription(HttpServletRequest request) { }
}Usage Example:
@Configuration
public class ErrorPageConfig {
@Bean
public ErrorPageFilter errorPageFilter() {
ErrorPageFilter filter = new ErrorPageFilter();
filter.addErrorPages(
new ErrorPage(HttpStatus.NOT_FOUND, "/error/404"),
new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error/500"),
new ErrorPage(Exception.class, "/error/exception")
);
return filter;
}
}Spring Boot provides comprehensive support for reactive web applications using Project Reactor and Spring WebFlux.
The marker interface for reactive web application contexts.
package org.springframework.boot.web.context.reactive;
/**
* Interface to provide configuration for a reactive web application.
*
* @since 2.0.0
*/
public interface ReactiveWebApplicationContext extends ApplicationContext {
// Marker interface
}A configurable reactive web application context interface.
package org.springframework.boot.web.context.reactive;
/**
* Configurable interface for reactive web application contexts.
*
* @since 2.0.0
*/
public interface ConfigurableReactiveWebApplicationContext
extends ConfigurableApplicationContext, ReactiveWebApplicationContext {
// Combined interface for configuration and reactive web support
}An annotation-based reactive web application context.
package org.springframework.boot.web.context.reactive;
/**
* ConfigurableReactiveWebApplicationContext that accepts annotated classes
* as input - in particular @Configuration-annotated classes, but also plain
* @Component classes and JSR-330 compliant classes.
*
* Allows for registering classes one by one or for classpath scanning.
*
* Note: In case of multiple @Configuration classes, later @Bean definitions
* will override ones defined in earlier loaded files.
*
* @since 2.0.0
*/
public class AnnotationConfigReactiveWebApplicationContext
extends AnnotationConfigApplicationContext
implements ConfigurableReactiveWebApplicationContext {
/**
* Create a new AnnotationConfigReactiveWebApplicationContext that needs
* to be populated through register calls and then manually refreshed.
*/
public AnnotationConfigReactiveWebApplicationContext() { }
/**
* Create a new AnnotationConfigReactiveWebApplicationContext with the
* given DefaultListableBeanFactory.
*
* @param beanFactory the DefaultListableBeanFactory instance to use
* for this context
* @since 2.2.0
*/
public AnnotationConfigReactiveWebApplicationContext(
DefaultListableBeanFactory beanFactory) { }
/**
* Create a new AnnotationConfigReactiveWebApplicationContext, deriving
* bean definitions from the given annotated classes and automatically
* refreshing the context.
*
* @param annotatedClasses one or more annotated classes,
* e.g. @Configuration classes
* @since 2.2.0
*/
public AnnotationConfigReactiveWebApplicationContext(
Class<?>... annotatedClasses) { }
/**
* Create a new AnnotationConfigReactiveWebApplicationContext, scanning
* for bean definitions in the given packages and automatically refreshing
* the context.
*
* @param basePackages the packages to check for annotated classes
* @since 2.2.0
*/
public AnnotationConfigReactiveWebApplicationContext(String... basePackages) { }
/**
* Create the environment for this context.
*
* @return a new StandardReactiveWebEnvironment
*/
@Override
protected ConfigurableEnvironment createEnvironment() { }
/**
* Get a resource by path, ensuring classpath resources are not exposed.
*
* @param path the resource path
* @return the resource
*/
@Override
protected Resource getResourceByPath(String path) { }
}Usage Example:
@Configuration
@EnableWebFlux
public class ReactiveWebConfig {
public static void main(String[] args) {
// Create reactive context
AnnotationConfigReactiveWebApplicationContext context =
new AnnotationConfigReactiveWebApplicationContext(
ReactiveWebConfig.class
);
// Or configure manually
AnnotationConfigReactiveWebApplicationContext context2 =
new AnnotationConfigReactiveWebApplicationContext();
context2.register(ReactiveWebConfig.class);
context2.scan("com.example.handlers");
context2.refresh();
}
@Bean
public RouterFunction<ServerResponse> routes(UserHandler handler) {
return RouterFunctions.route()
.GET("/users", handler::listUsers)
.GET("/users/{id}", handler::getUser)
.POST("/users", handler::createUser)
.build();
}
}A generic reactive web application context.
package org.springframework.boot.web.context.reactive;
/**
* Subclass of GenericApplicationContext, suitable for reactive web environments.
*
* @since 2.0.0
*/
public class GenericReactiveWebApplicationContext
extends GenericApplicationContext
implements ConfigurableReactiveWebApplicationContext {
/**
* Create a new GenericReactiveWebApplicationContext.
*/
public GenericReactiveWebApplicationContext() { }
/**
* Create a new GenericReactiveWebApplicationContext with the given
* DefaultListableBeanFactory.
*
* @param beanFactory the DefaultListableBeanFactory instance to use
* for this context
*/
public GenericReactiveWebApplicationContext(
DefaultListableBeanFactory beanFactory) { }
/**
* Create the environment for this context.
*
* @return a new StandardReactiveWebEnvironment
*/
@Override
protected ConfigurableEnvironment createEnvironment() { }
/**
* Get a resource by path, ensuring classpath resources are not exposed.
*
* @param path the resource path
* @return the resource
*/
@Override
protected Resource getResourceByPath(String path) { }
}The standard environment implementation for reactive web applications.
package org.springframework.boot.web.context.reactive;
/**
* Environment implementation to be used by Reactive-based web applications.
* All web-related (reactive-based) ApplicationContext classes initialize
* an instance by default.
*
* @since 2.0.0
*/
public class StandardReactiveWebEnvironment extends StandardEnvironment
implements ConfigurableReactiveWebEnvironment {
/**
* Create a new StandardReactiveWebEnvironment.
*/
public StandardReactiveWebEnvironment() { }
/**
* Create a new StandardReactiveWebEnvironment with the given property sources.
*
* @param propertySources the property sources
*/
protected StandardReactiveWebEnvironment(MutablePropertySources propertySources) { }
}Usage Example:
@SpringBootApplication
public class ReactiveApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(ReactiveApplication.class);
app.setWebApplicationType(WebApplicationType.REACTIVE);
app.run(args);
}
}
@RestController
@RequestMapping("/api")
public class ReactiveController {
@GetMapping("/users")
public Flux<User> getUsers() {
return userRepository.findAll();
}
@GetMapping("/users/{id}")
public Mono<User> getUser(@PathVariable String id) {
return userRepository.findById(id);
}
@PostMapping("/users")
public Mono<User> createUser(@RequestBody User user) {
return userRepository.save(user);
}
}Spring Boot provides a comprehensive error page mechanism for customizing error responses.
A simple server-independent abstraction for error pages.
package org.springframework.boot.web.error;
/**
* Simple server-independent abstraction for error pages. Roughly equivalent
* to the <error-page> element traditionally found in web.xml.
*
* @since 4.0.0
*/
public class ErrorPage {
/**
* Create a global error page (matches all unmatched errors).
*
* @param path the path to render
*/
public ErrorPage(String path) { }
/**
* Create an error page for a specific HTTP status.
*
* @param status the HTTP status
* @param path the path to render
*/
public ErrorPage(HttpStatus status, String path) { }
/**
* Create an error page for a specific exception type.
*
* @param exception the exception class
* @param path the path to render
*/
public ErrorPage(Class<? extends Throwable> exception, String path) { }
/**
* The path to render (usually implemented as a forward), starting with "/".
*
* @return the path that will be rendered for this error
*/
public String getPath() { }
/**
* Returns the exception type (or null for a page that matches by status).
*
* @return the exception type or null
*/
public Class<? extends Throwable> getException() { }
/**
* The HTTP status value that this error page matches (or null for a page
* that matches by exception).
*
* @return the status or null
*/
public HttpStatus getStatus() { }
/**
* The HTTP status value that this error page matches.
*
* @return the status value (or 0 for a page that matches any status)
*/
public int getStatusCode() { }
/**
* The exception type name.
*
* @return the exception type name (or null if there is none)
*/
public String getExceptionName() { }
/**
* Return if this error page is a global one (matches all unmatched
* status and exception types).
*
* @return if this is a global error page
*/
public boolean isGlobal() { }
}Usage Example:
@Configuration
public class ErrorPageConfiguration {
@Bean
public ErrorPageRegistrar errorPageRegistrar() {
return registry -> {
// Status-based error pages
registry.addErrorPages(
new ErrorPage(HttpStatus.NOT_FOUND, "/error/404"),
new ErrorPage(HttpStatus.FORBIDDEN, "/error/403"),
new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error/500")
);
// Exception-based error pages
registry.addErrorPages(
new ErrorPage(IllegalArgumentException.class, "/error/bad-request"),
new ErrorPage(DataAccessException.class, "/error/database")
);
// Global error page (catches all unmatched errors)
registry.addErrorPages(new ErrorPage("/error/general"));
};
}
}
@Controller
public class ErrorController {
@RequestMapping("/error/404")
public String notFound(Model model) {
model.addAttribute("message", "Page not found");
return "error/404";
}
@RequestMapping("/error/500")
public String serverError(Model model) {
model.addAttribute("message", "Internal server error");
return "error/500";
}
}An interface for registries that hold error pages.
package org.springframework.boot.web.error;
/**
* Interface for a registry that holds ErrorPages.
*
* @since 4.0.0
*/
@FunctionalInterface
public interface ErrorPageRegistry {
/**
* Adds error pages that will be used when handling exceptions.
*
* @param errorPages the error pages
*/
void addErrorPages(ErrorPage... errorPages);
}An interface for types that register error pages.
package org.springframework.boot.web.error;
/**
* Interface to be implemented by types that register ErrorPages.
*
* @since 4.0.0
*/
@FunctionalInterface
public interface ErrorPageRegistrar {
/**
* Register pages as required with the given registry.
*
* @param registry the error page registry
*/
void registerErrorPages(ErrorPageRegistry registry);
}Usage Example:
@Component
public class CustomErrorPageRegistrar implements ErrorPageRegistrar {
@Override
public void registerErrorPages(ErrorPageRegistry registry) {
// Register custom error pages
registry.addErrorPages(
new ErrorPage(HttpStatus.BAD_REQUEST, "/error/400"),
new ErrorPage(HttpStatus.UNAUTHORIZED, "/error/401"),
new ErrorPage(HttpStatus.PAYMENT_REQUIRED, "/error/402"),
new ErrorPage(HttpStatus.NOT_FOUND, "/error/404"),
new ErrorPage(HttpStatus.METHOD_NOT_ALLOWED, "/error/405")
);
}
}Options controlling the contents of error attributes.
package org.springframework.boot.web.error;
/**
* Options controlling the contents of ErrorAttributes.
*
* @since 2.3.0
*/
public final class ErrorAttributeOptions {
/**
* Get the option for including the specified attribute in the error response.
*
* @param include error attribute to get
* @return true if the Include attribute is included in the error response
*/
public boolean isIncluded(Include include) { }
/**
* Get all options for including attributes in the error response.
*
* @return the options
*/
public Set<Include> getIncludes() { }
/**
* Return an ErrorAttributeOptions that includes the specified attribute
* Include options.
*
* @param includes error attributes to include
* @return an ErrorAttributeOptions
*/
public ErrorAttributeOptions including(Include... includes) { }
/**
* Return an ErrorAttributeOptions that excludes the specified attribute
* Include options.
*
* @param excludes error attributes to exclude
* @return an ErrorAttributeOptions
*/
public ErrorAttributeOptions excluding(Include... excludes) { }
/**
* Remove elements from the given map if they are not included in this
* set of options.
*
* @param map the map to update
* @since 3.2.7
*/
public void retainIncluded(Map<String, Object> map) { }
/**
* Create an ErrorAttributeOptions with defaults.
*
* @return an ErrorAttributeOptions
*/
public static ErrorAttributeOptions defaults() { }
/**
* Create an ErrorAttributeOptions that includes the specified attribute
* Include options.
*
* @param includes error attributes to include
* @return an ErrorAttributeOptions
*/
public static ErrorAttributeOptions of(Include... includes) { }
/**
* Create an ErrorAttributeOptions that includes the specified attribute
* Include options.
*
* @param includes error attributes to include
* @return an ErrorAttributeOptions
*/
public static ErrorAttributeOptions of(Collection<Include> includes) { }
/**
* Error attributes that can be included in an error response.
*/
public enum Include {
/**
* Include the exception class name attribute.
*/
EXCEPTION,
/**
* Include the stack trace attribute.
*/
STACK_TRACE,
/**
* Include the message attribute.
*/
MESSAGE,
/**
* Include the binding errors attribute.
*/
BINDING_ERRORS,
/**
* Include the HTTP status code.
*
* @since 3.2.7
*/
STATUS,
/**
* Include the error type.
*
* @since 3.2.7
*/
ERROR,
/**
* Include the request path.
*
* @since 3.3.0
*/
PATH
}
}Usage Example:
@ControllerAdvice
public class CustomErrorAttributes extends DefaultErrorAttributes {
@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest,
ErrorAttributeOptions options) {
// Create custom options
ErrorAttributeOptions customOptions = ErrorAttributeOptions.of(
Include.MESSAGE,
Include.STATUS,
Include.ERROR,
Include.PATH
);
// Or modify existing options
ErrorAttributeOptions withStack = options.including(Include.STACK_TRACE);
ErrorAttributeOptions withoutBinding = options.excluding(Include.BINDING_ERRORS);
// Get error attributes with custom options
Map<String, Object> errorAttributes =
super.getErrorAttributes(webRequest, customOptions);
// Add custom attributes
errorAttributes.put("timestamp", LocalDateTime.now());
errorAttributes.put("support", "contact@example.com");
return errorAttributes;
}
}
@RestControllerAdvice
public class ApiErrorHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity<Map<String, Object>> handleException(
Exception ex, WebRequest request) {
// Use ErrorAttributeOptions to control what gets included
ErrorAttributeOptions options = ErrorAttributeOptions
.defaults()
.including(Include.MESSAGE, Include.STACK_TRACE);
Map<String, Object> body = new HashMap<>();
body.put("timestamp", LocalDateTime.now());
body.put("message", ex.getMessage());
// Only include stack trace in development
if (options.isIncluded(Include.STACK_TRACE)) {
body.put("trace", getStackTrace(ex));
}
return ResponseEntity
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(body);
}
}A wrapper class for MessageSourceResolvable errors that is safe for JSON serialization.
package org.springframework.boot.web.error;
/**
* A wrapper class for MessageSourceResolvable errors that is safe for JSON
* serialization.
*
* @since 3.5.0
*/
public final class Error implements MessageSourceResolvable {
/**
* Get the codes for message resolution.
*
* @return the codes (may be null)
*/
@Override
public String[] getCodes();
/**
* Get the arguments for message resolution.
*
* @return the arguments (may be null)
*/
@Override
public Object[] getArguments();
/**
* Get the default message.
*
* @return the default message (may be null)
*/
@Override
public String getDefaultMessage();
/**
* Return the original cause of the error.
*
* @return the error cause
*/
@JsonIgnore
public MessageSourceResolvable getCause();
/**
* Wrap the given errors, if necessary, such that they are suitable for serialization
* to JSON. MessageSourceResolvable implementations that are known to be
* suitable are not wrapped.
*
* @param errors the errors to wrap
* @return a new Error list
* @since 3.5.4
*/
public static List<MessageSourceResolvable> wrapIfNecessary(
List<? extends MessageSourceResolvable> errors);
}Usage Example:
import org.springframework.boot.web.error.Error;
import org.springframework.context.MessageSourceResolvable;
import org.springframework.validation.FieldError;
@RestControllerAdvice
public class ValidationErrorHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, Object>> handleValidationErrors(
MethodArgumentNotValidException ex) {
// Get validation errors
List<FieldError> fieldErrors = ex.getBindingResult().getFieldErrors();
// Wrap errors for JSON serialization
List<MessageSourceResolvable> wrapped = Error.wrapIfNecessary(fieldErrors);
Map<String, Object> body = new HashMap<>();
body.put("timestamp", LocalDateTime.now());
body.put("status", HttpStatus.BAD_REQUEST.value());
body.put("errors", wrapped);
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(body);
}
}Spring Boot provides a comprehensive set of interfaces and classes for creating and customizing embedded web servers.
The core interface representing a fully configured web server that can be started and stopped.
package org.springframework.boot.web.server;
/**
* Simple interface that represents a fully configured web server (for example
* Tomcat, Jetty, Netty). Allows the server to be started and stopped.
*
* @since 2.0.0
*/
public interface WebServer {
/**
* Starts the web server. Calling this method on an already started server
* has no effect.
*
* @throws WebServerException if the server cannot be started
*/
void start() throws WebServerException;
/**
* Stops the web server. Calling this method on an already stopped server
* has no effect.
*
* @throws WebServerException if the server cannot be stopped
*/
void stop() throws WebServerException;
/**
* Return the port this server is listening on.
*
* @return the port (or -1 if none)
*/
int getPort();
/**
* Initiates a graceful shutdown of the web server. Handling of new requests
* is prevented and the given callback is invoked at the end of the attempt.
*
* @param callback the callback to invoke when the graceful shutdown completes
* @since 2.3.0
*/
default void shutDownGracefully(GracefulShutdownCallback callback) {
callback.shutdownComplete(GracefulShutdownResult.IMMEDIATE);
}
/**
* Destroys the web server such that it cannot be started again.
*
* @since 3.2.0
*/
default void destroy() {
stop();
}
}A callback for the result of a graceful shutdown request.
package org.springframework.boot.web.server;
/**
* A callback for the result of a graceful shutdown request.
*
* @since 2.3.0
*/
@FunctionalInterface
public interface GracefulShutdownCallback {
/**
* Graceful shutdown has completed with the given result.
*
* @param result the result of the shutdown
*/
void shutdownComplete(GracefulShutdownResult result);
}The result of a graceful shutdown request.
package org.springframework.boot.web.server;
/**
* The result of a graceful shutdown request.
*
* @since 2.3.0
*/
public enum GracefulShutdownResult {
/**
* Requests remained active at the end of the grace period.
*/
REQUESTS_ACTIVE,
/**
* The server was idle with no active requests at the end of the grace period.
*/
IDLE,
/**
* The server was shutdown immediately, ignoring any active requests.
*/
IMMEDIATE
}Configuration for shutting down a WebServer.
package org.springframework.boot.web.server;
/**
* Configuration for shutting down a WebServer.
*
* @since 2.3.0
*/
public enum Shutdown {
/**
* The WebServer should support graceful shutdown, allowing active
* requests time to complete.
*/
GRACEFUL,
/**
* The WebServer should shut down immediately.
*/
IMMEDIATE
}Tagging interface for factories that create a WebServer.
package org.springframework.boot.web.server;
/**
* Tagging interface for factories that create a WebServer.
*
* @since 2.0.0
*/
public interface WebServerFactory {
// Marker interface
}A configurable WebServerFactory with common configuration options.
package org.springframework.boot.web.server;
/**
* A configurable WebServerFactory.
*
* @since 2.0.0
*/
public interface ConfigurableWebServerFactory
extends WebServerFactory, ErrorPageRegistry {
/**
* Sets the port that the web server should listen on. If not specified
* port '8080' will be used. Use port -1 to disable auto-start.
*
* @param port the port to set
*/
void setPort(int port);
/**
* Sets the specific network address that the server should bind to.
*
* @param address the address to set (defaults to null)
*/
void setAddress(InetAddress address);
/**
* Sets the error pages that will be used when handling exceptions.
*
* @param errorPages the error pages
*/
void setErrorPages(Set<? extends ErrorPage> errorPages);
/**
* Sets the SSL configuration that will be applied to the server's
* default connector.
*
* @param ssl the SSL configuration
*/
void setSsl(Ssl ssl);
/**
* Sets the SSL bundles that can be used to configure SSL connections.
*
* @param sslBundles the SSL bundles
* @since 3.1.0
*/
void setSslBundles(SslBundles sslBundles);
/**
* Sets the HTTP/2 configuration that will be applied to the server.
*
* @param http2 the HTTP/2 configuration
*/
void setHttp2(Http2 http2);
/**
* Sets the compression configuration that will be applied to the
* server's default connector.
*
* @param compression the compression configuration
*/
void setCompression(Compression compression);
/**
* Sets the server header value.
*
* @param serverHeader the server header value
*/
void setServerHeader(String serverHeader);
/**
* Sets the shutdown configuration that will be applied to the server.
*
* @param shutdown the shutdown configuration
* @since 2.3.0
*/
default void setShutdown(Shutdown shutdown) { }
}Factory interface for creating servlet-based web servers.
package org.springframework.boot.web.server.servlet;
/**
* Factory interface that can be used to create a WebServer.
*
* @since 4.0.0
*/
@FunctionalInterface
public interface ServletWebServerFactory extends WebServerFactory {
/**
* Gets a new fully configured but paused WebServer instance. Clients
* should not be able to connect to the returned server until
* WebServer.start() is called.
*
* @param initializers ServletContextInitializers that should be applied
* as the server starts
* @return a fully configured and started WebServer
*/
WebServer getWebServer(ServletContextInitializer... initializers);
}A configurable ServletWebServerFactory with servlet-specific configuration options.
package org.springframework.boot.web.server.servlet;
/**
* A configurable ServletWebServerFactory.
*
* @since 4.0.0
*/
public interface ConfigurableServletWebServerFactory
extends ConfigurableWebServerFactory, ServletWebServerFactory, WebListenerRegistry {
/**
* Gets the settings for this servlet web server factory.
*
* @return the servlet web server settings
*/
ServletWebServerSettings getSettings();
/**
* Sets the context path for the web server. The context should start
* with a "/" character but not end with a "/" character.
*
* @param contextPath the context path to set
*/
default void setContextPath(String contextPath) {
getSettings().setContextPath(ContextPath.of(contextPath));
}
/**
* Returns the context path for the servlet web server.
*
* @return the context path
*/
default String getContextPath() {
return getSettings().getContextPath().toString();
}
/**
* Sets the display name of the application deployed in the web server.
*
* @param displayName the displayName to set
*/
default void setDisplayName(String displayName) {
getSettings().setDisplayName(displayName);
}
/**
* Sets the configuration that will be applied to the container's HTTP
* session support.
*
* @param session the session configuration
*/
default void setSession(Session session) {
getSettings().setSession(session);
}
/**
* Set if the DefaultServlet should be registered.
*
* @param registerDefaultServlet if the default servlet should be registered
*/
default void setRegisterDefaultServlet(boolean registerDefaultServlet) {
getSettings().setRegisterDefaultServlet(registerDefaultServlet);
}
/**
* Sets the mime-type mappings.
*
* @param mimeMappings the mime type mappings
*/
default void setMimeMappings(MimeMappings mimeMappings) {
getSettings().setMimeMappings(mimeMappings);
}
/**
* Sets the document root directory which will be used by the web
* context to serve static files.
*
* @param documentRoot the document root or null if not required
*/
default void setDocumentRoot(File documentRoot) {
getSettings().setDocumentRoot(documentRoot);
}
/**
* Sets ServletContextInitializers that should be applied in addition to
* ServletWebServerFactory.getWebServer parameters.
*
* @param initializers the initializers to set
*/
default void setInitializers(List<? extends ServletContextInitializer> initializers) {
getSettings().setInitializers(initializers);
}
/**
* Add ServletContextInitializers to those that should be applied.
*
* @param initializers the initializers to add
*/
default void addInitializers(ServletContextInitializer... initializers) {
getSettings().addInitializers(initializers);
}
/**
* Sets the configuration that will be applied to the server's JSP servlet.
*
* @param jsp the JSP servlet configuration
*/
default void setJsp(Jsp jsp) {
getSettings().setJsp(jsp);
}
/**
* Sets the init parameters that are applied to the container's ServletContext.
*
* @param initParameters the init parameters
*/
default void setInitParameters(Map<String, String> initParameters) {
getSettings().setInitParameters(initParameters);
}
}Factory interface for creating reactive web servers.
package org.springframework.boot.web.server.reactive;
/**
* Factory interface that can be used to create a reactive WebServer.
*
* @since 4.0.0
*/
@FunctionalInterface
public interface ReactiveWebServerFactory extends WebServerFactory {
/**
* Gets a new fully configured but paused WebServer instance. Clients
* should not be able to connect to the returned server until
* WebServer.start() is called.
*
* @param httpHandler the HTTP handler in charge of processing requests
* @return a fully configured and started WebServer
*/
WebServer getWebServer(HttpHandler httpHandler);
}A configurable ReactiveWebServerFactory.
package org.springframework.boot.web.server.reactive;
/**
* Configurable ReactiveWebServerFactory.
*
* @since 4.0.0
*/
public interface ConfigurableReactiveWebServerFactory
extends ConfigurableWebServerFactory, ReactiveWebServerFactory {
// Combined interface for configuration and reactive web support
}Strategy interface for customizing web server factories.
package org.springframework.boot.web.server;
/**
* Strategy interface for customizing WebServerFactory web server factories.
* Any beans of this type will get a callback with the server factory before
* the server itself is started, so you can set the port, address, error
* pages etc.
*
* @param <T> the configurable web server factory
* @since 2.0.0
*/
@FunctionalInterface
public interface WebServerFactoryCustomizer<T extends WebServerFactory> {
/**
* Customize the specified WebServerFactory.
*
* @param factory the web server factory to customize
*/
void customize(T factory);
}Usage Example:
@Configuration
public class WebServerConfiguration {
@Bean
public WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>
servletWebServerCustomizer() {
return factory -> {
// Customize port
factory.setPort(9090);
// Set context path
factory.setContextPath("/myapp");
// Configure session timeout
Session session = new Session();
session.setTimeout(Duration.ofMinutes(30));
factory.setSession(session);
// Add error pages
factory.addErrorPages(
new ErrorPage(HttpStatus.NOT_FOUND, "/error/404"),
new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error/500")
);
// Configure shutdown
factory.setShutdown(Shutdown.GRACEFUL);
};
}
@Bean
public WebServerFactoryCustomizer<ConfigurableReactiveWebServerFactory>
reactiveWebServerCustomizer() {
return factory -> {
factory.setPort(8081);
// Configure SSL
Ssl ssl = new Ssl();
ssl.setEnabled(true);
ssl.setKeyStore("classpath:keystore.jks");
ssl.setKeyStorePassword("password");
factory.setSsl(ssl);
// Enable HTTP/2
Http2 http2 = new Http2();
http2.setEnabled(true);
factory.setHttp2(http2);
};
}
// Graceful shutdown example
@Bean
public ApplicationListener<ContextClosedEvent> gracefulShutdownListener(
WebServerApplicationContext context) {
return event -> {
WebServer webServer = context.getWebServer();
if (webServer != null) {
webServer.shutDownGracefully(result -> {
switch (result) {
case IDLE -> System.out.println("Server shutdown gracefully - was idle");
case REQUESTS_ACTIVE -> System.out.println("Server shutdown with active requests");
case IMMEDIATE -> System.out.println("Server shutdown immediately");
}
});
}
};
}
}Here's a comprehensive example that demonstrates servlet support, error handling, and both servlet and reactive contexts:
@SpringBootApplication
public class WebSupportDemoApplication {
public static void main(String[] args) {
SpringApplication.run(WebSupportDemoApplication.class, args);
}
}
// Servlet Registration
@Configuration
public class ServletConfiguration {
@Bean
public ServletRegistrationBean<ApiServlet> apiServlet() {
ServletRegistrationBean<ApiServlet> bean =
new ServletRegistrationBean<>(new ApiServlet(), "/api/v1/*");
bean.setLoadOnStartup(1);
bean.addInitParameter("api-version", "1.0");
return bean;
}
@Bean
public FilterRegistrationBean<CorsFilter> corsFilter() {
FilterRegistrationBean<CorsFilter> bean =
new FilterRegistrationBean<>(new CorsFilter());
bean.addUrlPatterns("/*");
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
}
@Bean
public ServletListenerRegistrationBean<ApplicationListener> appListener() {
return new ServletListenerRegistrationBean<>(
new ApplicationListener()
);
}
}
// Error Page Configuration
@Configuration
public class ErrorConfiguration {
@Bean
public ErrorPageRegistrar errorPageRegistrar() {
return registry -> {
registry.addErrorPages(
new ErrorPage(HttpStatus.NOT_FOUND, "/error/404"),
new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error/500"),
new ErrorPage(IllegalArgumentException.class, "/error/bad-request"),
new ErrorPage("/error") // Global error page
);
};
}
}
@Controller
@RequestMapping("/error")
public class CustomErrorController {
@RequestMapping("/404")
public String notFound(Model model, HttpServletRequest request) {
model.addAttribute("path", request.getAttribute(
ErrorPageFilter.ERROR_REQUEST_URI));
return "error/404";
}
@RequestMapping("/500")
public String serverError(Model model) {
return "error/500";
}
@RequestMapping
public String generalError(Model model, HttpServletRequest request) {
ErrorAttributeOptions options = ErrorAttributeOptions.defaults()
.including(Include.MESSAGE, Include.EXCEPTION);
model.addAttribute("options", options);
return "error/general";
}
}
// Reactive Web Configuration
@Configuration
@EnableWebFlux
public class ReactiveWebConfiguration {
@Bean
public RouterFunction<ServerResponse> userRoutes(UserHandler handler) {
return RouterFunctions.route()
.GET("/users", handler::list)
.GET("/users/{id}", handler::get)
.POST("/users", handler::create)
.PUT("/users/{id}", handler::update)
.DELETE("/users/{id}", handler::delete)
.build();
}
@Bean
public WebFilter loggingFilter() {
return (exchange, chain) -> {
System.out.println("Request: " + exchange.getRequest().getPath());
return chain.filter(exchange)
.doOnSuccess(v -> System.out.println("Response sent"));
};
}
}
@Component
public class UserHandler {
private final UserRepository repository;
public UserHandler(UserRepository repository) {
this.repository = repository;
}
public Mono<ServerResponse> list(ServerRequest request) {
return ServerResponse.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(repository.findAll(), User.class);
}
public Mono<ServerResponse> get(ServerRequest request) {
String id = request.pathVariable("id");
return repository.findById(id)
.flatMap(user -> ServerResponse.ok()
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(user))
.switchIfEmpty(ServerResponse.notFound().build());
}
public Mono<ServerResponse> create(ServerRequest request) {
return request.bodyToMono(User.class)
.flatMap(repository::save)
.flatMap(user -> ServerResponse.created(
URI.create("/users/" + user.getId()))
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(user));
}
}Spring Boot provides specialized application context implementations and events for web server lifecycle management.
Interface to be implemented by application contexts that create and manage the lifecycle of an embedded WebServer.
package org.springframework.boot.web.server.context;
/**
* Interface to be implemented by application contexts that create and manage
* the lifecycle of an embedded WebServer.
*
* @since 4.0.0
*/
public interface WebServerApplicationContext extends ApplicationContext {
/**
* SmartLifecycle phase in which graceful shutdown of the web server is
* performed.
*
* @since 4.0.0
*/
int GRACEFUL_SHUTDOWN_PHASE = SmartLifecycle.DEFAULT_PHASE - 1024;
/**
* SmartLifecycle phase in which starting and stopping of the web server
* is performed.
*/
int START_STOP_LIFECYCLE_PHASE = GRACEFUL_SHUTDOWN_PHASE - 1024;
/**
* Returns the WebServer that was created by the context or null if the
* server has not yet been created.
*
* @return the web server
*/
WebServer getWebServer();
/**
* Returns the namespace of the web server application context or null if
* no namespace has been set. Used for disambiguation when multiple web
* servers are running in the same application.
*
* @return the server namespace
*/
String getServerNamespace();
/**
* Returns true if the specified context is a WebServerApplicationContext
* with a matching server namespace.
*
* @param context the context to check
* @param serverNamespace the server namespace to match against
* @return true if the server namespace of the context matches
*/
static boolean hasServerNamespace(ApplicationContext context,
String serverNamespace) {
return (context instanceof WebServerApplicationContext webServerApplicationContext)
&& ObjectUtils.nullSafeEquals(
webServerApplicationContext.getServerNamespace(),
serverNamespace);
}
/**
* Returns the server namespace if the specified context is a
* WebServerApplicationContext.
*
* @param context the context
* @return the server namespace or null if the context is not a
* WebServerApplicationContext
*/
static String getServerNamespace(ApplicationContext context) {
return (context instanceof WebServerApplicationContext configurableContext)
? configurableContext.getServerNamespace() : null;
}
}SPI interface to be implemented by most if not all web server application contexts.
package org.springframework.boot.web.server.context;
/**
* SPI interface to be implemented by most if not all web server application
* contexts. Provides facilities to configure the context, in addition to the
* methods in the WebServerApplicationContext interface.
*
* @since 4.0.0
*/
public interface ConfigurableWebServerApplicationContext
extends ConfigurableApplicationContext, WebServerApplicationContext {
/**
* Set the server namespace of the context.
*
* @param serverNamespace the server namespace
*/
void setServerNamespace(String serverNamespace);
}Event to be published when the WebServer is ready.
package org.springframework.boot.web.server.context;
/**
* Event to be published when the WebServer is ready. Useful for obtaining the
* local port of a running server.
*
* @since 4.0.0
*/
public abstract class WebServerInitializedEvent extends ApplicationEvent {
/**
* Creates a new WebServerInitializedEvent.
*
* @param webServer the web server
*/
protected WebServerInitializedEvent(WebServer webServer) { }
/**
* Access the WebServer.
*
* @return the embedded web server
*/
public WebServer getWebServer() {
return getSource();
}
/**
* Access the application context that the server was created in.
*
* @return the applicationContext that the server was created from
*/
public abstract WebServerApplicationContext getApplicationContext();
/**
* Access the source of the event (an WebServer).
*
* @return the embedded web server
*/
@Override
public WebServer getSource() {
return (WebServer) super.getSource();
}
}Usage Example:
@Component
public class WebServerPortListener {
@EventListener
public void onApplicationEvent(WebServerInitializedEvent event) {
int port = event.getWebServer().getPort();
System.out.println("Web server started on port: " + port);
}
}Register custom servlets and filters with proper URL mapping and security configuration.
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import jakarta.servlet.*;
import jakarta.servlet.http.*;
import java.io.IOException;
import java.util.EnumSet;
@Configuration
public class ServletConfiguration {
// Register custom servlet
@Bean
public ServletRegistrationBean<CustomServlet> customServlet() {
ServletRegistrationBean<CustomServlet> registration =
new ServletRegistrationBean<>(new CustomServlet(), "/api/custom/*");
// Set load-on-startup priority
registration.setLoadOnStartup(1);
// Add initialization parameters
registration.addInitParameter("configLocation", "classpath:custom-config.xml");
registration.addInitParameter("debug", "true");
return registration;
}
// Register authentication filter
@Bean
public FilterRegistrationBean<AuthenticationFilter> authFilter() {
FilterRegistrationBean<AuthenticationFilter> registration =
new FilterRegistrationBean<>(new AuthenticationFilter());
// Apply to specific URL patterns
registration.addUrlPatterns("/api/*", "/admin/*");
// Set dispatcher types
registration.setDispatcherTypes(
EnumSet.of(
DispatcherType.REQUEST,
DispatcherType.FORWARD,
DispatcherType.ERROR
)
);
// Set filter order (lower values execute first)
registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
return registration;
}
// Register CORS filter
@Bean
public FilterRegistrationBean<CorsFilter> corsFilter() {
FilterRegistrationBean<CorsFilter> registration =
new FilterRegistrationBean<>(new CorsFilter());
// Apply to all paths
registration.addUrlPatterns("/*");
// Execute before authentication
registration.setOrder(Ordered.HIGHEST_PRECEDENCE - 1);
return registration;
}
// Register session listener
@Bean
public ServletListenerRegistrationBean<SessionListener> sessionListener() {
return new ServletListenerRegistrationBean<>(new SessionListener());
}
}
// Custom servlet implementation
public class CustomServlet extends HttpServlet {
private String configLocation;
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
this.configLocation = config.getInitParameter("configLocation");
System.out.println("CustomServlet initialized with config: " + configLocation);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("application/json");
resp.getWriter().write("{\"status\":\"ok\",\"config\":\"" + configLocation + "\"}");
}
@Override
public void destroy() {
System.out.println("CustomServlet destroyed");
}
}
// Authentication filter
public class AuthenticationFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String token = httpRequest.getHeader("Authorization");
if (token == null || !validateToken(token)) {
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
return;
}
// Add user info to request attributes
httpRequest.setAttribute("userId", extractUserId(token));
chain.doFilter(request, response);
}
private boolean validateToken(String token) {
return token != null && token.startsWith("Bearer ");
}
private String extractUserId(String token) {
return "user123"; // Extract from JWT
}
}
// CORS filter
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setHeader("Access-Control-Allow-Origin", "*");
httpResponse.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
httpResponse.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type");
httpResponse.setHeader("Access-Control-Max-Age", "3600");
chain.doFilter(request, response);
}
}
// Session listener
public class SessionListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent se) {
System.out.println("Session created: " + se.getSession().getId());
se.getSession().setMaxInactiveInterval(1800); // 30 minutes
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
System.out.println("Session destroyed: " + se.getSession().getId());
}
}Use Cases:
Implement comprehensive error handling with custom error pages.
import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.ErrorPageRegistrar;
import org.springframework.boot.web.server.ErrorPageRegistry;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.WebRequest;
import java.util.Map;
@Configuration
public class ErrorPageConfiguration {
@Bean
public ErrorPageRegistrar errorPageRegistrar() {
return registry -> {
// Register status code error pages
registry.addErrorPages(
new ErrorPage(HttpStatus.NOT_FOUND, "/error/404"),
new ErrorPage(HttpStatus.FORBIDDEN, "/error/403"),
new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error/500"),
new ErrorPage(HttpStatus.SERVICE_UNAVAILABLE, "/error/503")
);
// Register exception-based error pages
registry.addErrorPages(
new ErrorPage(IllegalArgumentException.class, "/error/badrequest"),
new ErrorPage(AccessDeniedException.class, "/error/accessdenied"),
new ErrorPage(DataAccessException.class, "/error/database")
);
// Generic error page for uncaught exceptions
registry.addErrorPages(new ErrorPage("/error/general"));
};
}
}
@Controller
public class CustomErrorController implements ErrorController {
private final ErrorAttributes errorAttributes;
public CustomErrorController(ErrorAttributes errorAttributes) {
this.errorAttributes = errorAttributes;
}
@RequestMapping("/error/404")
public ResponseEntity<ErrorResponse> handle404(WebRequest webRequest) {
Map<String, Object> attributes = getErrorAttributes(webRequest);
ErrorResponse error = new ErrorResponse(
"NOT_FOUND",
"The requested resource was not found",
(String) attributes.get("path")
);
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}
@RequestMapping("/error/403")
public ResponseEntity<ErrorResponse> handle403(WebRequest webRequest) {
Map<String, Object> attributes = getErrorAttributes(webRequest);
ErrorResponse error = new ErrorResponse(
"FORBIDDEN",
"You don't have permission to access this resource",
(String) attributes.get("path")
);
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(error);
}
@RequestMapping("/error/500")
public ResponseEntity<ErrorResponse> handle500(WebRequest webRequest) {
Map<String, Object> attributes = getErrorAttributes(webRequest);
ErrorResponse error = new ErrorResponse(
"INTERNAL_SERVER_ERROR",
"An internal server error occurred",
(String) attributes.get("path")
);
// Log the exception
Throwable exception = errorAttributes.getError(webRequest);
if (exception != null) {
log.error("Internal server error", exception);
}
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
}
@RequestMapping("/error/badrequest")
public ResponseEntity<ErrorResponse> handleBadRequest(WebRequest webRequest) {
Map<String, Object> attributes = getErrorAttributes(webRequest);
Throwable exception = errorAttributes.getError(webRequest);
ErrorResponse error = new ErrorResponse(
"BAD_REQUEST",
exception != null ? exception.getMessage() : "Invalid request",
(String) attributes.get("path")
);
return ResponseEntity.badRequest().body(error);
}
@RequestMapping("/error/general")
public ResponseEntity<ErrorResponse> handleGeneral(WebRequest webRequest) {
Map<String, Object> attributes = getErrorAttributes(webRequest);
Integer status = (Integer) attributes.get("status");
ErrorResponse error = new ErrorResponse(
"ERROR",
"An error occurred while processing your request",
(String) attributes.get("path")
);
return ResponseEntity.status(status != null ? status : 500).body(error);
}
private Map<String, Object> getErrorAttributes(WebRequest webRequest) {
return errorAttributes.getErrorAttributes(webRequest,
ErrorAttributeOptions.of(
ErrorAttributeOptions.Include.MESSAGE,
ErrorAttributeOptions.Include.BINDING_ERRORS
));
}
}
// Error response DTO
public record ErrorResponse(
String code,
String message,
String path,
long timestamp
) {
public ErrorResponse(String code, String message, String path) {
this(code, message, path, System.currentTimeMillis());
}
}Use Cases:
Configure embedded servers with production-ready settings.
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.boot.web.server.Compression;
import org.springframework.boot.web.server.Http2;
import org.springframework.boot.web.server.Ssl;
import org.apache.catalina.connector.Connector;
import org.apache.coyote.http11.Http11NioProtocol;
import java.io.File;
import java.time.Duration;
@Configuration
public class EmbeddedServerConfiguration {
// General server configuration
@Bean
public WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> webServerCustomizer() {
return factory -> {
// Port configuration
factory.setPort(8443);
// Context path
factory.setContextPath("/api");
// Session timeout
factory.getSession().setTimeout(Duration.ofMinutes(30));
factory.getSession().getCookie().setHttpOnly(true);
factory.getSession().getCookie().setSecure(true);
// Compression
Compression compression = new Compression();
compression.setEnabled(true);
compression.setMinResponseSize(1024);
compression.setMimeTypes(new String[]{
"text/html", "text/xml", "text/plain",
"text/css", "application/javascript",
"application/json", "application/xml"
});
factory.setCompression(compression);
// HTTP/2 support
Http2 http2 = new Http2();
http2.setEnabled(true);
factory.setHttp2(http2);
// SSL configuration
Ssl ssl = new Ssl();
ssl.setEnabled(true);
ssl.setKeyStore("classpath:keystore.p12");
ssl.setKeyStorePassword("changeit");
ssl.setKeyStoreType("PKCS12");
ssl.setKeyAlias("tomcat");
factory.setSsl(ssl);
};
}
// Tomcat-specific configuration
@Bean
public WebServerFactoryCustomizer<TomcatServletWebServerFactory> tomcatCustomizer() {
return factory -> {
// Connection pool settings
factory.addConnectorCustomizers(connector -> {
Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
// Thread pool
protocol.setMaxThreads(200);
protocol.setMinSpareThreads(10);
// Connection settings
protocol.setMaxConnections(10000);
protocol.setAcceptCount(100);
// Timeouts
protocol.setConnectionTimeout(20000);
protocol.setKeepAliveTimeout(60000);
// Compression (if not set globally)
protocol.setCompression("on");
protocol.setCompressionMinSize(1024);
});
// Add AJP connector for load balancer
factory.addAdditionalTomcatConnectors(createAjpConnector());
// Access log
factory.addContextCustomizers(context -> {
context.getParent().getPipeline().addValve(
new org.apache.catalina.valves.AccessLogValve()
);
});
};
}
private Connector createAjpConnector() {
Connector connector = new Connector("AJP/1.3");
connector.setPort(8009);
connector.setSecure(false);
connector.setAllowTrace(false);
connector.setScheme("http");
return connector;
}
// Undertow-specific configuration
@Bean
public WebServerFactoryCustomizer<UndertowServletWebServerFactory> undertowCustomizer() {
return factory -> {
// Buffer sizes
factory.setBufferSize(16384);
factory.setIoThreads(Runtime.getRuntime().availableProcessors() * 2);
factory.setWorkerThreads(200);
// Direct buffers
factory.setDirectBuffers(true);
// Undertow options
factory.addBuilderCustomizers(builder -> {
builder.setServerOption(
io.undertow.UndertowOptions.ENABLE_HTTP2, true
);
builder.setServerOption(
io.undertow.UndertowOptions.MAX_ENTITY_SIZE, 100L * 1024 * 1024
);
});
};
}
}application.yml:
server:
port: 8443
shutdown: graceful
max-http-request-header-size: 16KB
tomcat:
threads:
max: 200
min-spare: 10
max-connections: 10000
accept-count: 100
connection-timeout: 20s
compression:
enabled: true
min-response-size: 1KB
mime-types:
- text/html
- text/xml
- text/plain
- text/css
- application/javascript
- application/json
- application/xml
spring:
lifecycle:
timeout-per-shutdown-phase: 30sUse Cases:
Implement graceful shutdown with monitoring of active requests.
import org.springframework.boot.web.server.GracefulShutdownCallback;
import org.springframework.boot.web.server.GracefulShutdownResult;
import org.springframework.boot.web.server.WebServer;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;
// Track active requests
@Component
public class ActiveRequestTracker extends OncePerRequestFilter {
private final AtomicInteger activeRequests = new AtomicInteger(0);
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
activeRequests.incrementAndGet();
try {
filterChain.doFilter(request, response);
} finally {
activeRequests.decrementAndGet();
}
}
public int getActiveRequests() {
return activeRequests.get();
}
}
// Graceful shutdown manager
@Component
public class GracefulShutdownManager implements ApplicationListener<ContextClosedEvent> {
private static final Logger log = LoggerFactory.getLogger(GracefulShutdownManager.class);
private final ActiveRequestTracker requestTracker;
private final WebServer webServer;
public GracefulShutdownManager(ActiveRequestTracker requestTracker,
WebServerApplicationContext context) {
this.requestTracker = requestTracker;
this.webServer = context.getWebServer();
}
@Override
public void onApplicationEvent(ContextClosedEvent event) {
log.info("Initiating graceful shutdown");
// Stop accepting new requests
webServer.shutDownGracefully(result -> {
log.info("Graceful shutdown completed with result: {}", result);
if (result == GracefulShutdownResult.REQUESTS_ACTIVE) {
int active = requestTracker.getActiveRequests();
log.warn("{} requests were still active during shutdown", active);
}
});
// Monitor active requests
monitorActiveRequests();
}
private void monitorActiveRequests() {
int checkCount = 0;
int maxChecks = 30; // 30 seconds max wait
while (checkCount < maxChecks) {
int active = requestTracker.getActiveRequests();
if (active == 0) {
log.info("All requests completed");
break;
}
log.info("Waiting for {} active requests to complete", active);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
checkCount++;
}
if (requestTracker.getActiveRequests() > 0) {
log.warn("Shutdown timeout reached with {} active requests",
requestTracker.getActiveRequests());
}
}
}
// Health check endpoint that considers shutdown state
@RestController
public class HealthController {
private volatile boolean shuttingDown = false;
@EventListener(ContextClosedEvent.class)
public void onShutdown() {
shuttingDown = true;
}
@GetMapping("/health")
public ResponseEntity<Map<String, String>> health() {
if (shuttingDown) {
return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE)
.body(Map.of("status", "SHUTTING_DOWN"));
}
return ResponseEntity.ok(Map.of("status", "UP"));
}
@GetMapping("/health/ready")
public ResponseEntity<Map<String, String>> readiness() {
if (shuttingDown) {
return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE)
.body(Map.of("status", "NOT_READY"));
}
return ResponseEntity.ok(Map.of("status", "READY"));
}
}Use Cases:
Configure WebFlux reactive server with optimized settings.
import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;
import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.reactive.ReactorResourceFactory;
import reactor.netty.http.server.HttpServer;
import reactor.netty.resources.ConnectionProvider;
import reactor.netty.resources.LoopResources;
import java.time.Duration;
@Configuration
public class ReactiveServerConfiguration {
@Bean
public ReactorResourceFactory reactorResourceFactory() {
ReactorResourceFactory factory = new ReactorResourceFactory();
// Connection provider settings
ConnectionProvider provider = ConnectionProvider.builder("custom")
.maxConnections(500)
.maxIdleTime(Duration.ofSeconds(20))
.maxLifeTime(Duration.ofSeconds(60))
.pendingAcquireTimeout(Duration.ofSeconds(45))
.evictInBackground(Duration.ofSeconds(120))
.build();
factory.setConnectionProvider(provider);
// Event loop settings
LoopResources loopResources = LoopResources.create(
"event-loop",
Runtime.getRuntime().availableProcessors(),
true
);
factory.setLoopResources(loopResources);
factory.setUseGlobalResources(false);
return factory;
}
@Bean
public WebServerFactoryCustomizer<NettyReactiveWebServerFactory> nettyCustomizer(
ReactorResourceFactory resourceFactory) {
return factory -> {
factory.setResourceFactory(resourceFactory);
// Netty-specific customization
factory.addServerCustomizers(httpServer ->
httpServer
.idleTimeout(Duration.ofSeconds(30))
.maxKeepAliveRequests(100)
.compress(true)
.accessLog(true)
.http2Settings(spec ->
spec.maxConcurrentStreams(100)
.initialWindowSize(65535)
)
);
};
}
// Custom error handler for WebFlux
@Bean
public WebExceptionHandler customWebExceptionHandler() {
return (exchange, ex) -> {
log.error("Unhandled error in reactive request", ex);
exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
ErrorResponse error = new ErrorResponse(
"INTERNAL_ERROR",
"An unexpected error occurred",
exchange.getRequest().getPath().value()
);
DataBuffer buffer = exchange.getResponse()
.bufferFactory()
.wrap(new ObjectMapper().writeValueAsBytes(error));
return exchange.getResponse().writeWith(Mono.just(buffer));
};
}
}
// Reactive request tracking filter
@Component
public class ReactiveRequestTrackingFilter implements WebFilter {
private static final Logger log = LoggerFactory.getLogger(ReactiveRequestTrackingFilter.class);
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
long startTime = System.currentTimeMillis();
String requestId = UUID.randomUUID().toString();
return chain.filter(exchange)
.contextWrite(Context.of("requestId", requestId))
.doFinally(signalType -> {
long duration = System.currentTimeMillis() - startTime;
log.info("Request {} completed in {}ms with signal: {}",
requestId, duration, signalType);
});
}
}application.yml:
spring:
webflux:
base-path: /api
reactor:
context-propagation: auto
server:
netty:
connection-timeout: 20s
idle-timeout: 30s
max-http-request-header-size: 16KBUse Cases:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class WebConfigurationTest {
@LocalServerPort
private int port;
@Autowired
private TestRestTemplate restTemplate;
@Test
void shouldReturnCustomErrorPage() {
ResponseEntity<String> response =
restTemplate.getForEntity("http://localhost:" + port + "/nonexistent",
String.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
assertThat(response.getBody()).contains("NOT_FOUND");
}
@Test
void shouldApplyCompressionToTextContent() {
HttpHeaders headers = new HttpHeaders();
headers.setAcceptEncoding(List.of(ContentCodingType.GZIP));
ResponseEntity<String> response = restTemplate.exchange(
"http://localhost:" + port + "/api/data",
HttpMethod.GET,
new HttpEntity<>(headers),
String.class
);
assertThat(response.getHeaders().getFirst("Content-Encoding"))
.isEqualTo("gzip");
}
}Spring Boot's web application support provides:
Servlet Support: Comprehensive APIs for registering servlets, filters, and listeners programmatically
ServletRegistrationBean - Register servlets with URL mappings and initialization parametersFilterRegistrationBean and AbstractFilterRegistrationBean - Register filters with URL patterns, servlet mappings, and dispatcher typesServletListenerRegistrationBean - Register event listeners for servlet context, session, and request eventsDelegatingFilterProxyRegistrationBean - Register filters that delegate to Spring-managed beans with lazy initialization supportServlet Context Support: Specialized contexts and environments for servlet-based applications, including WAR deployment support
AnnotationConfigServletWebApplicationContext - Annotation-based servlet web application contextApplicationServletEnvironment - Standard servlet environment for Spring Boot applicationsSpringBootServletInitializer - Support for traditional WAR deployment with servlet containersReactive Support: Full reactive web application support with specialized contexts and environments
ReactiveWebApplicationContext - Marker interface for reactive web contextsAnnotationConfigReactiveWebApplicationContext - Annotation-based reactive web application contextGenericReactiveWebApplicationContext - Generic reactive web application contextStandardReactiveWebEnvironment - Standard environment for reactive applicationsError Page Support: Flexible error page registration and handling for both servlet and reactive applications
ErrorPage - Simple server-independent abstraction for error pagesErrorPageRegistry - Interface for registries that hold error pagesErrorPageRegistrar - Interface for types that register error pagesErrorPageFilter - Filter that provides error page handling for non-embedded applicationsErrorAttributeOptions - Options controlling the contents of error attributesWeb Server Factory and Customization: Complete API for creating and customizing embedded web servers
WebServer - Core interface representing a fully configured web server with lifecycle managementWebServerFactory - Base interface for web server factoriesConfigurableWebServerFactory - Configurable factory with common server configuration optionsServletWebServerFactory and ConfigurableServletWebServerFactory - Factories for servlet-based web serversReactiveWebServerFactory and ConfigurableReactiveWebServerFactory - Factories for reactive web serversWebServerFactoryCustomizer - Strategy interface for customizing web server factoriesGracefulShutdownCallback, GracefulShutdownResult, and Shutdown - Support for graceful server shutdownWeb Server Context and Lifecycle: Specialized application contexts and events for web server lifecycle management
WebServerApplicationContext - Interface for contexts that create and manage embedded web serversConfigurableWebServerApplicationContext - Configurable web server application context with namespace supportWebServerInitializedEvent - Event published when the web server is readyThese components work together to provide a complete, production-ready web application framework that supports both traditional servlet-based and modern reactive architectures. The framework provides fine-grained control over server configuration, error handling, graceful shutdown, and lifecycle management while maintaining a simple and consistent API across different server types.