Spring Boot MVC Autoconfiguration

Spring Boot provides out of the box a large set of auto-configuration settings that work well with most applications. The auto-configuration includes the following:

  • Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.
  • Automatic registration of Converter, GenericConverter, and Formatter beans.
  • Support for HttpMessageConverters.
  • Automatic registration of MessageCodesResolver.
  • Static index.html support.
  • Support for serving static resources.
  • Custom Favicon support.
  • Automatic use of a ConfigurableWebBindingInitializer bean.

If you want to keep those Spring Boot MVC customizations and make more MVC customizations (interceptors, formatters, view controllers, and other features), you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc.

Here is an example of a View Controller:

@Configuration public class ViewConfig implements WebMvcConfigurer {
  @Override public void addViewControllers(ViewControllerRegistry registry) {
    registry.addViewController("/signup").setViewName("signup");
    registry.addViewController("/login").setViewName("login");
    registry.addViewController("/dashboard").setViewName("dashboard");
    registry.addViewController("/logout").setViewName("redirect:/");
  }

Configuring HttpMessageConverters

Spring MVC uses the HttpMessageConverter interface to convert HTTP requests and responses. Defaults values are included out of the box. For example, objects can be automatically converted to JSON/XML using Jackson’s library or by using JAXB if the Jackson XML extension is not available.

If you need to add or customize converters, you can use Spring Boot’s HttpMessageConverters class, as shown in the following example:

@Configuration
public class MyConfiguration {
  @Bean
  public HttpMessageConverters customConverters() {
    MarshallingHttpMessageConverter xmlConverter = new MarshallingHttpMessageConverter();
    XStreamMarshaller xstream = new XStreamMarshaller();
    xmlConverter.setMarshaller(xstream);
    xmlConverter.setUnmarshaller(xstream);
    return new HttpMessageConverters(xmlConverter);
  }
}

Any HttpMessageConverter bean that is present in the context is added to the list of converters. You can also override default converters in the same way.

Custom JSON Serializers and Deserializers

If you use Jackson to serialize and deserialize JSON data, you might want to write your own JsonSerializer and JsonDeserializer classes. Custom serializers are usually registered with Jackson through a module, but Spring Boot provides an alternative @JsonComponent annotation that makes it easier to directly register Spring Beans.

You can use the @JsonComponent annotation directly on JsonSerializer, JsonDeserializer or KeyDeserializer implementations. You can also use it on classes that contain serializers/deserializers as inner classes, as shown in the following example:

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import org.springframework.boot.jackson.JsonComponent;

@JsonComponent
public class CustomDateConverter {
  private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
  private static final SimpleDateFormat sdf1 = new SimpleDateFormat(DATE_FORMAT);

  public static class Serialize extends JsonSerializer<Instant> {
    @Override
    public void serialize(Instant value, JsonGenerator jgen, SerializerProvider provider) {
      try {
        if (value == null) {
          jgen.writeNull();
        } else {
          jgen.writeString(
              DateTimeFormatter.ofPattern(DATE_FORMAT)
                  .withZone(ZoneId.systemDefault())
                  .format(value));
        }
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }

  public static class Deserialize extends JsonDeserializer<Instant> {
    @Override
    public Instant deserialize(
        com.fasterxml.jackson.core.JsonParser jp, DeserializationContext ctxt) throws IOException {
      try {
        String dateAsString = jp.getText();
        if (dateAsString == null) {
          return null;
        } else {
          return Instant.ofEpochMilli(sdf1.parse(dateAsString).getTime());
        }
      } catch (Exception e) {
        e.printStackTrace();
      }
      return null;
    }
  }
}

All @JsonComponent beans in the ApplicationContext are automatically registered with Jackson.

Spring Boot also provides JsonObjectSerializer and JsonObjectDeserializer base classes that provide useful alternatives to the standard Jackson versions when serializing objects. See JsonObjectSerializer and JsonObjectDeserializer in the Javadoc for details.

MessageCodesResolver

Spring MVC has a strategy for generating error codes for rendering error messages from binding errors: MessageCodesResolver. If you set the spring.mvc.message-codes-resolver-format property PREFIX_ERROR_CODE or POSTFIX_ERROR_CODE, Spring Boot creates one for you as in this example:

@Configuration
public class ExampleApp {
  @Bean
  OrderValidator validator() {
    return new OrderValidator();
  }

  public static void main(String[] args) {
    AnnotationConfigApplicationContext context =
        new AnnotationConfigApplicationContext(ExampleApp.class);
    Order order = new Order();
    order.setOrderPrice(BigDecimal.ZERO);
    BeanPropertyBindingResult bindingResult =
        new BeanPropertyBindingResult(order, Order.class.getName());
    DefaultMessageCodesResolver messageCodesResolver =
        (DefaultMessageCodesResolver) bindingResult.getMessageCodesResolver();
    messageCodesResolver.setMessageCodeFormatter(
        DefaultMessageCodesResolver.Format.POSTFIX_ERROR_CODE);
    OrderValidator validator = context.getBean(OrderValidator.class);
    ValidationUtils.invokeValidator(validator, order, bindingResult);
    bindingResult.getAllErrors().forEach(e -> System.out.println(Arrays.toString(e.getCodes())));
  }
}

In the above example we have changed the DefaultMessageCodesResolver to use Format.POSTFIX_ERROR_CODE:

Static Content

By default, Spring Boot serves static content from a directory called /static (or /public or /resources or /META-INF/resources) in the classpath or from the root of the ServletContext. It uses the ResourceHttpRequestHandler from Spring MVC so that you can modify that behavior by adding your own WebMvcConfigurer and overriding the addResourceHandlers method.

@Configuration
public class DispatcherServletConfig implements WebMvcConfigurer {
  private static final String[] RESOURCE_HANDLER_PATH = {"/favicon.ico", "/image/**", "/js/**"};
  private static final String[] RESOURCE_HANDLER_LOCATION = {
    "/resources/favicon.ico", "/resources/image/", "/resources/js/"
  };

  @Override
  public void addResourceHandlers(ResourceHandlerRegistry registry) {
    CacheControl cacheControl = CacheControl.empty().cachePrivate();
    registry
        .addResourceHandler(RESOURCE_HANDLER_PATH)
        .addResourceLocations(RESOURCE_HANDLER_LOCATION)
        .setCacheControl(cacheControl);
  }
}

In a stand-alone web application, the default servlet from the container is also enabled and acts as a fallback, serving content from the root of the ServletContext if Spring decides not to handle it. Most of the time, this does not happen (unless you modify the default MVC configuration), because Spring can always handle requests through the DispatcherServlet.

By default, resources are mapped on /**, but you can tune that with the spring.mvc.static-path-pattern property. For instance, relocating all resources to /resources/** can be achieved as follows:

spring.mvc.static-path-pattern=/resources/**

You can also customize the static resource locations by using the spring.resources.static-locations property (replacing the default values with a list of directory locations). The root Servlet context path, “/”, is automatically added as a location as well.

In addition to the “standard” static resource locations mentioned earlier, a special case is made for Webjars content. Any resources with a path in /webjars/** are served from jar files if they are packaged in the Webjars format.

Found the article helpful? if so please follow us on Socials
Twitter Icon       Facebook Icon       LinkedIn Icon       Mastodon Icon