WebMvcConfigurationSupport serves as the core configuration mechanism for Spring's web MVC functionality in version 5.x. For Spring 6.x applications, developers should implement WebMvcConfigurer instead (preferably with @EnableWebMvc annotation).
This configuration class handles the fundamental setup required for Spring MVC operations. Understanding its role and functionality is crucial for Spring web development.
The abbreviation "WCS" will be used throughout this discussion to refer to WebMvcConfigurationSupport.
This analysis focuses on components related to DispatcherServlet and WCS interactions, excluding container-level HTTP request mechanisms.
After reading this article, you will:
- Understand what WCS accomplishes
- Learn how to utilize WCS effectively
- Gain reference material for WCS implementation
WCS Overview
Original Documentation Summary
WCS provides the primary configuration infrastructure for MVC Java-based configurations. It's typically imported by adding @EnableWebMvc to a @Configuration class. Alternatively, developers can extend this class directly and override methods as needed, ensuring the subclass includes @Configuration annotation and @Bean annotations on overridden methods.
This class registers several key components:
Handler Mappings:
- RequestMappingHandlerMapping at order 0 for annotated controller method mapping
- HandlerMapping at order 1 for direct URL-to-view-name mapping
- BeanNameUrlHandlerMapping at order 2 for URL-to-controller-bean-name mapping
- RouterFunctionMapping at order 3 for router function mapping
- HandlerMapping at Integer.MAX_VALUE-1 for static resource requests
- HandlerMapping at Integer.MAX_VALUE for default servlet forwarding
Handler Adapters:
- RequestMappingHandlerAdapter for annotated controller method processing
- HttpRequestHandlerAdapter for HttpRequestHandler processing
- SimpleControllerHandlerAdapter for interface-based controller processing
- HandlerFunctionAdapter for router function processing
Exception Resolvers:
- ExceptionHandlerExceptionResolver for @ExceptionHandler method handling
- ResponseStatusExceptionResolver for @ResponseStatus annotated exceptions
- DefaultHandlerExceptionResolver for standard Spring exception types
Core Functionality
WCS manages various web MVC configurations by defining numerous beans (approximately 25) that enable different MVC pipeline stages to retrieve and utilize these components for web MVC operations.
Key responsibilities include:
- Registering handler mappings to route requests to controllers, methods, views, and resources
- Registering handler adapters for specific processing tasks like parameter parsing and message conversion
- Registering exception handlers
- Registering path matching utilities
Essentially, WCS provides Spring with tools for handling request processing, mapping relationships, parameter parsing, and response messaging.
Key Terminology
Resolvers
English: Resolver
Refers to problem-solving components that handle different scenarios based on context.
Interceptors
English: Interceptor
Components that intercept HTTP requests, similar to filters but capable of interrupting request flow.
Controllers, Parameters, Methods
Controllers correspond to @Controller annotations, parameters represent method inputs, and methods refer to request-handling controller methods.
Return Values and Messages
Return values are controller method outputs, while messages encompass client requests and server responses, particularly concerning @ResponseBody annotations.
CORS
Cross-origin resource sharing enables controlled access to resources from different origins.
Resources and Views
Resources represent static data (images, scripts, text), while views represent display pages.
Validators
Components for validating specific parameters using simple business logic.
Formatters and Converters
Components for transforming and formatting data, including message and property conversions.
Application Context and Servlet Context
ApplicationContext: Spring's applicaiton context managing beans
ServletContext: Web server/container context containing application-wide information
Media Types
MediaType represents content type information, extending MimeType with additional Spring-specific functionality.
RequestMappingHandlerMapping
Handles RequestMapping annotation processing and establishes mapping relationships between annotations and resources:
@Bean
@SuppressWarnings("deprecation")
public RequestMappingHandlerMapping requestMappingHandlerMapping(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
mapping.setOrder(0);
mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
mapping.setContentNegotiationManager(contentNegotiationManager);
mapping.setCorsConfigurations(getCorsConfigurations());
PathMatchConfigurer pathConfig = getPathMatchConfigurer();
if (pathConfig.getPatternParser() != null) {
mapping.setPatternParser(pathConfig.getPatternParser());
} else {
mapping.setUrlPathHelper(pathConfig.getUrlPathHelperOrDefault());
mapping.setPathMatcher(pathConfig.getPathMatcherOrDefault());
Boolean useSuffixPatternMatch = pathConfig.isUseSuffixPatternMatch();
if (useSuffixPatternMatch != null) {
mapping.setUseSuffixPatternMatch(useSuffixPatternMatch);
}
Boolean useRegisteredSuffixPatternMatch = pathConfig.isUseRegisteredSuffixPatternMatch();
if (useRegisteredSuffixPatternMatch != null) {
mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch);
}
}
Boolean useTrailingSlashMatch = pathConfig.isUseTrailingSlashMatch();
if (useTrailingSlashMatch != null) {
mapping.setUseTrailingSlashMatch(useTrailingSlashMatch);
}
if (pathConfig.getPathPrefixes() != null) {
mapping.setPathPrefixes(pathConfig.getPathPrefixes());
}
return mapping;
}
This creates a handler mapping containing interceptors, content negotiation managers, CORS configurations, and path matching utilities.
RequestMappingHandlerAdapter
Processes RequestMapping annotated controller methods:
@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcValidator") Validator validator) {
RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
adapter.setContentNegotiationManager(contentNegotiationManager);
adapter.setMessageConverters(getMessageConverters());
adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer(conversionService, validator));
adapter.setCustomArgumentResolvers(getArgumentResolvers());
adapter.setCustomReturnValueHandlers(getReturnValueHandlers());
if (jackson2Present) {
adapter.setRequestBodyAdvice(Collections.singletonList(new JsonViewRequestBodyAdvice()));
adapter.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));
}
AsyncSupportConfigurer configurer = getAsyncSupportConfigurer();
if (configurer.getTaskExecutor() != null) {
adapter.setTaskExecutor(configurer.getTaskExecutor());
}
if (configurer.getTimeout() != null) {
adapter.setAsyncRequestTimeout(configurer.getTimeout());
}
adapter.setCallableInterceptors(configurer.getCallableInterceptors());
adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors());
return adapter;
}
Key features include media type management, message conversion, web binding initialization, custom argument resolvers, and asynchronous support.
HTTP Request Processing Flow
Spring MVC processes HTTP requests through various components managed by DispatcherServlet, which maintains properties including multipart resolvers, locale resolvers, handler mappings, handler adapters, and view resolvers.
Common Configuration: Interceptors
Interceptor configuration example:
@Override
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new CustomAuthenticationInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("*.js", "*.html", "*.css")
.excludePathPatterns("/public/**", "/health");
super.addInterceptors(registry);
}
Common Configuration: Custom Parameter Resolution
Spring allows custom parameter resolution beyond built-in annotations like @RequestBody and @RequestParam.
Custom Parameter Annotation Example
Define a custom annotation:
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface CurrentUser {
}
public class UserArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(CurrentUser.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
return request.getSession().getAttribute("currentUser");
}
}
// Register in WCS
@Override
public void addArgumentResolvers(List<handlermethodargumentresolver> resolvers) {
resolvers.add(new UserArgumentResolver());
}
// Usage in controller
@RequestMapping("/process")
@ResponseBody
public ResponseData process(@CurrentUser UserInfo currentUser) {
// Process with current user info
return new ResponseData();
}
</handlermethodargumentresolver>
Response Message Configuration
Multiple HTTP message converters can handle different media types by implementing HttpMessageConverter interface.
Jackson configuration example:
@Override
protected void configureMessageConverters(List<httpmessageconverter>> converters) {
converters.add(new ByteArrayHttpMessageConverter());
converters.add(new StringHttpMessageConverter());
converters.add(new ResourceHttpMessageConverter());
MappingJackson2HttpMessageConverter jacksonConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper mapper = jacksonConverter.getObjectMapper();
// Configure null serialization
mapper.getSerializerProvider().setNullValueSerializer(new JsonSerializer<object>() {
@Override
public void serialize(Object obj, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString("");
}
});
// Set date format
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
mapper.setDateFormat(dateFormat);
converters.add(jacksonConverter);
}
</object></httpmessageconverter>
View and Resource Configuration
Resource and view configuration examples:
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/")
.addResourceLocations("file:" + uploadDirectory);
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("home/index");
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
super.addViewControllers(registry);
}