Backend Development 9 min read

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.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Master Spring MVC Customization: From WebConfig to YAML Message Converters

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 BootSpring MVCMessage ConverterContent NegotiationWebConfig
Spring Full-Stack Practical Cases
Written by

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.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.