Spring Boot is great for developing web applications. You can create a standalone HTTP server using built-in Tomcat, Jetty, Undertow or Netty. Most web applications use the spring-boot-starter-web module for fast execution. You can also compile reactive web applications using the spring-boot-starter-webflux module.

If you want to build servlet-based web applications, you can take advantage of Spring's autoconfiguration capabilities Boot for Spring MVC or Jersey.

Spring Web MVC Framework

Spring Web MVC (often referred to as "Spring MVC") is a full-fledged web framework based on the model -view-controller". Spring MVC allows you to create custom beans with @Controller or @RestController annotations to handle incoming HTTP requests. Methods in the controller are mapped to the HTTP protocol using @RequestMapping annotations.

The following code shows a typical @RestController annotation that works with JSON data :

Java

import java.util.List;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/users")
public class MyRestController {
    private final UserRepository userRepository;
    private final CustomerRepository customerRepository;
    public MyRestController(UserRepository userRepository, CustomerRepository customerRepository) {
        this.userRepository = userRepository;
        this.customerRepository = customerRepository;
    }
    @GetMapping("/{userId}")
    public User getUser(@PathVariable Long userId) {
        return this.userRepository.findById(userId).get();
    }
    @GetMapping("/{userId}/customers")
    public List<Customer> getUserCustomers(@PathVariable Long userId) {
        return this.userRepository.findById(userId).map(this.customerRepository::findByUser).get();
    }
    @DeleteMapping("/{userId}")
    public void deleteUser(@PathVariable Long userId) {
        this.userRepository.deleteById(userId);
    }
}
Kotlin

import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping("/users")
class MyRestController(private val userRepository: UserRepository, private val customerRepository: CustomerRepository) {
    @GetMapping("/{userId}")
    fun getUser(@PathVariable userId: Long): User {
        return userRepository.findById(userId).get()
    }
    @GetMapping("/{userId}/customers")
    fun getUserCustomers(@PathVariable userId: Long): ListCustomer> {
        return userRepository.findById(userId).map(customerRepository::findByUser).get()
    }
    @DeleteMapping("/{userId}")
    fun deleteUser(@PathVariable userId: Long) {
        userRepository.deleteById(userId)
    }
}

The "WebMvc.fn", a functional option, separates the routing configuration from the actual processing of requests, as shown in the following example:

Java

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.function.RequestPredicate;
import org.springframework.web.servlet.function.RouterFunction;
import org.springframework.web.servlet.function.ServerResponse;
import static org.springframework.web.servlet.function.RequestPredicates.accept;
import static org.springframework.web.servlet.function.RouterFunctions.route;
@Configuration(proxyBeanMethods = false)
public class MyRoutingConfiguration {
    private static final RequestPredicate ACCEPT_JSON = accept(MediaType.APPLICATION_JSON);
    @Bean
    public RouterFunction<ServerResponse> routerFunction(MyUserHandler userHandler) {
        return route()
                .GET("/{user}", ACCEPT_JSON, userHandler::getUser)
                .GET("/{user}/customers", ACCEPT_JSON, userHandler::getUserCustomers)
                .DELETE("/{user}", ACCEPT_JSON, userHandler::deleteUser)
                .build();
    }
}
Kotlin

import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.MediaType
import org.springframework.web.servlet.function.RequestPredicates.accept
import org.springframework.web.servlet.function.RouterFunction
import org.springframework.web.servlet.function.RouterFunctions
import org.springframework.web.servlet.function.ServerResponse
@Configuration(proxyBeanMethods = false)
class MyRoutingConfiguration {
    @Bean
    fun routerFunction(userHandler: MyUserHandler): RouterFunction<ServerResponse> {
        return RouterFunctions.route()
            .GET("/{user}", ACCEPT_JSON, userHandler::getUser)
            .GET("/{user}/customers", ACCEPT_JSON, userHandler::getUserCustomers)
            .DELETE("/{user}", ACCEPT_JSON, userHandler::deleteUser)
            .build()
    }
    companion object {
        private val ACCEPT_JSON = accept(MediaType.APPLICATION_JSON)
    }
}
Java

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.function.ServerRequest;
import org.springframework.web.servlet.function.ServerResponse;
@Component
public class MyUserHandler {
    public ServerResponse getUser(ServerRequest request) {
        ...
        return ServerResponse.ok().build();
    }
    public ServerResponse getUserCustomers(ServerRequest request) {
        ...
        return ServerResponse.ok().build();
    }
    public ServerResponse deleteUser(ServerRequest request) {
        ...
        return ServerResponse.ok().build();
    }
}
Kotlin

import org.springframework.stereotype.Component
import org.springframework.web.servlet.function.ServerRequest
import org.springframework.web.servlet.function.ServerResponse
@Component
class MyUserHandler {
    fun getUser(request: ServerRequest?): ServerResponse {
        return ServerResponse.ok().build()
    }
    fun getUserCustomers(request: ServerRequest?): ServerResponse {
        return ServerResponse.ok().build()
    }
    fun deleteUser(request: ServerRequest?): ServerResponse {
        return ServerResponse.ok().build()
    }
}
To modulate the definition of a router, you can define as many RouterFunction beans as you wish. The beans can be ordered if you want to apply execution order.

Spring MVC Auto-Configuration

Spring Boot provides auto-configuration for Spring MVC, which works fine with most applications.

Auto-configuration adds the following functionality in addition to the default Spring settings:

  • Adding ContentNegotiatingViewResolver and BeanNameViewResolver beans.

  • Support for processing static resources, including support for WebJars.

  • Automatic registration of Converter, GenericConverter beans and Formatter.

  • Support for HttpMessageConverters.

  • Automatic registration MessageCodesResolver.

  • Support for static index.html.

  • Automatic use of the ConfigurableWebBindingInitializer bean.

If you need to save these Spring Boot MVC settings and add additional MVC settings (interceptors, formatters, view controllers and other functions ), then you can add your own class marked with the @Configuration annotation, like WebMvcConfigurer, but without the @EnableWebMvc annotation .

If you need to pass custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter or ExceptionHandlerExceptionResolver and at the same time preserve the Spring Boot MVC settings, then you can declare a bean of type WebMvcRegistrations and use it to pass custom instances of these components.

If you need to fully control the operation of Spring MVC, you can add your own @Configuration , marked with the @EnableWebMvc annotation, or you can add your own DelegatingWebMvcConfiguration class, marked with the @Configuration annotation, as described in the Javadoc for the @EnableWebMvc annotation.

Spring MVC uses a different ConversionService than the one used for value conversion from your application.properties or application.yaml file. This means that the Period, Duration and DataSize converters will not be available and that the @DurationUnit and @ annotations DataSizeUnit will be ignored.

If you need to personalize the settings of the ConversionService service used by Spring MVC, you can provide a WebMvcConfigurer bean with a method addFormatters. From this method you can register any preferred converter or delegate authority to the static methods available to ApplicationConversionService.

HttpMessageConverters

Spring MVC uses the HttpMessageConverter interface for converting HTTP requests and responses. Adequate default settings are provided out of the box. For example, objects can be automatically converted to JSON (using the Jackson library) or to XML (using the Jackson XML extension if available, or using JAXB if the Jackson XML extension is not available). By default, strings are encoded in UTF-8.

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

Java

import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
@Configuration(proxyBeanMethods = false)
public class MyHttpMessageConvertersConfiguration {
    @Bean
    public HttpMessageConverters customConverters() {
        HttpMessageConverter<?> additional = new AdditionalHttpMessageConverter();
        HttpMessageConverter<?> another = new AnotherHttpMessageConverter();
        return new HttpMessageConverters(additional, another);
    }
}
Kotlin

import org.springframework.boot.autoconfigure.http.HttpMessageConverters
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.converter.HttpMessageConverter
@Configuration(proxyBeanMethods = false)
class MyHttpMessageConvertersConfiguration {
    @Bean
    fun customConverters(): HttpMessageConverters {
        val additional: HttpMessageConverter<*> = AdditionalHttpMessageConverter()
        val another: HttpMessageConverter<*> = AnotherHttpMessageConverter()
        return HttpMessageConverters(additional, another)
    }
}

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

MessageCodesResolver

Spring MVC contains an error code generation strategy for printing binding error messages: MessageCodesResolver. If the property is set spring.mvc.message-codes-resolver-format PREFIX_ERROR_CODE or POSTFIX_ERROR_CODE, Spring Boot will create it for you.