Master Spring MVC Customization: From WebConfig to YAML Message Converters
This tutorial walks through enabling Spring MVC, configuring type conversion, validation, interceptors, content negotiation, custom YAML message converters, view controllers, view resolvers, and static resource handling in a Spring Boot 2.7.12 application, complete with code examples and output screenshots.
1. Enable Spring MVC
Activate Spring MVC by adding @EnableWebMvc in a configuration class.
<code>@Configuration
@EnableWebMvc
public class WebConfig {
}
</code>2. Type Conversion Configuration
Register a custom ConverterFactory to convert String to Number (example shown for Integer ).
<code>@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverterFactory(new ConverterFactory<String, Number>() {
@Override
public <T extends Number> Converter<String, T> getConverter(Class<T> targetType) {
return new Converter<String, T>() {
public T convert(String source) {
return (T) Integer.valueOf(source);
}
};
}
});
}
}
</code>Converters are stored in a ConvertersForPair queue where newer converters are added to the front:
<code>private static class ConvertersForPair {
private final Deque<GenericConverter> converters = new ConcurrentLinkedDeque<>();
public void add(GenericConverter converter) {
this.converters.addFirst(converter);
}
}
</code>3. Data Validation
If a Bean Validation implementation (e.g., Hibernate Validator) is on the classpath, Spring registers LocalValidatorFactoryBean as the global Validator :
<code>@Configuration
public class WebConfig implements WebMvcConfigurer {
public Validator getValidator() {
return new LocalValidatorFactoryBean();
}
}
</code>4. Request Interceptor
Example interceptor that checks for a token header before allowing the request to proceed.
<code>@Configuration
public class WebConfig implements WebMvcConfigurer {
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new HandlerInterceptor() {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (request.getHeader("token") == null) {
return false;
}
return true;
}
}).addPathPatterns("/**");
}
}
</code>Interceptors are not a security layer; for robust security use Spring Security with MvcRequestMatcher .
5. Content Negotiation
Configure Spring MVC to recognize a custom yaml media type.
<code>@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
// map "yaml" extension to "application/yaml"
configurer.mediaType("yaml", new MediaType("application", "yaml"));
}
}
</code>Register the ContentNegotiationManager bean when needed:
<code>@Bean
public ContentNegotiationManager mvcContentNegotiationManager() {
if (this.contentNegotiationManager == null) {
ContentNegotiationConfigurer configurer = new ContentNegotiationConfigurer(this.servletContext);
configurer.mediaTypes(getDefaultMediaTypes());
configureContentNegotiation(configurer);
this.contentNegotiationManager = configurer.buildContentNegotiationManager();
}
return this.contentNegotiationManager;
}
protected ContentNegotiationManager buildContentNegotiationManager() {
this.factory.addMediaTypes(this.mediaTypes);
return this.factory.build();
}
</code>6. Custom YAML Message Converter
Define a HttpMessageConverter that writes objects as YAML.
<code>public class YamlHttpMessageConverter implements HttpMessageConverter<Object> {
@Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
return User.class.isAssignableFrom(clazz);
}
@Override
public List<MediaType> getSupportedMediaTypes() {
return Arrays.asList(new MediaType("application", "yaml"));
}
@Override
public void write(Object t, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
StreamUtils.copy(new org.yaml.snakeyaml.Yaml().dump(t), StandardCharsets.UTF_8, outputMessage.getBody());
}
}
</code>Register the converter with highest priority:
<code>@Configuration
public class WebConfig implements WebMvcConfigurer {
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
// add at position 0 to override default JSON converter
converters.add(0, new YamlHttpMessageConverter());
}
}
</code>Test endpoint:
<code>@GetMapping("/yaml")
public Object yaml() {
return new User(10, "zhangsan");
}
</code>Resulting YAML output is shown below:
7. View Controller
Map a URL directly to a view without a controller method.
<code>@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// /index renders the "test" view
registry.addViewController("/index").setViewName("test");
}
}
</code>Define the view bean:
<code>@Component("test")
public class PackView implements View {
@Override
public String getContentType() {
return "text/html";
}
@Override
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
response.getWriter().print("View Controllers");
}
}
</code>Rendered result:
8. View Resolver
Register the custom YamlViewResolver so that views ending with .yaml are resolved.
<code>@Configuration
public class WebConfig implements WebMvcConfigurer {
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.viewResolver(new YamlViewResolver());
}
}
</code>The resolver is added to ViewResolverComposite , which aggregates all resolvers.
9. Static Resource Configuration
Expose static resources under /resources/** from /public or classpath:/static/ directories.
<code>@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/public", "classpath:/static/");
}
}
</code>The article concludes with a hope that these configurations help you customize Spring MVC effectively.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.