Spring Boot чудово підходить для розробки вебдодатків. Ти можеш створити автономний HTTP-сервер за допомогою вбудованого Tomcat, Jetty, Undertow або Netty. Більшість вебдодатків використовують модуль spring-boot-starter-web для швидкого виконання. Ти також можеш компілювати реактивні вебдодатки за допомогою модуля spring-boot-starter-webflux.

Якщо ти хочеш створювати вебдодатки на основі сервлетів, то можеш скористатися можливостями автоконфігурації Spring Boot для Spring MVC або Jersey.

Фреймворк Spring Web MVC

Фреймворк Spring Web MVC (часто згадується як Spring MVC) — це повноцінний вебфреймворк за схемою "модель-подання-контролер". Spring MVC дозволяє створювати спеціальні біни з анотаціями @Controller або @RestController для обробки вхідних HTTP-запитів. Методи в контролері відображаються на протокол HTTP за допомогою анотацій @RequestMapping.

У наступному коді показано типову анотацію @RestController, яка працює з даними у форматі JSON:

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)
    }
}

"WebMvc.fn", функціональний варіант, відокремлює конфігурацію маршрутизації від фактичної обробки запитів, як показано в наступному прикладі:

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()
    }
}
Щоб модулювати визначення маршрутизатора, можна визначати стільки бінів RouterFunction, скільки забажаєш. Біни можна впорядкувати, якщо потрібно застосовувати черговість виконання.

Автоконфігурація Spring MVC

Spring Boot передбачає автоконфігурацію для Spring MVC, яка відмінно працює з більшістю додатків.

Автоконфігурація додає наступні функції до налаштувань Spring за замовчуванням:

  • Додати біни ContentNegotiatingViewResolver і BeanNameViewResolver.

  • Підтримка обробки статичних ресурсів, включно з підтримкою WebJars.

  • Автоматична реєстрація бінів Converter, GenericConverter та Formatter.

  • Підтримка HttpMessageConverters.

  • Автоматична реєстрація MessageCodesResolver.

  • Підтримка статичного index.html.

  • Автоматичне використання біна ConfigurableWebBindingInitializer.

Якщо необхідно зберегти ці налаштування Spring Boot MVC і внести додаткові налаштування MVC (перехоплювачі, форматувальники, контролери представлення та інші функції), то можна додати власний клас, позначений анотацією @Configuration, типу WebMvcConfigurer, але без анотації @EnableWebMvc .

Якщо необхідно передати кастомні екземпляри RequestMappingHandlerMapping, RequestMappingHandlerAdapter або ExceptionHandlerExceptionResolver і водночас зберегти налаштування Spring Boot MVC, то можна оголосити бін типу WebMvcRegistrations і використовувати його для передачі кастомних екземплярів цих компонентів.

Якщо необхідно повністю контролювати роботу Spring MVC, можна додати власну @Configuration, позначену анотацією @EnableWebMvc, або ж додати свій власний клас DelegatingWebMvcConfiguration, позначений анотацією @Configuration, як описано в Javadoc для анотації @ EnableWebMvc.

Spring MVC використовує ConversionService, відмінний від того, який використовується для перетворення значень з файлу application.properties або application.yaml. Це означає, що перетворювачі Period, Duration та DataSize будуть недоступні і що анотації @DurationUnit та @ DataSizeUnit буде проігноровано.

Якщо необхідно персоналізувати налаштування служби ConversionService, що використовується Spring MVC, то можна передбачити бін WebMvcConfigurer з методом addFormatters. З цього методу можна реєструвати будь-який перетворювач або делегувати повноваження статичним методам, доступним для ApplicationConversionService.

HttpMessageConverters

Spring MVC використовує інтерфейс HttpMessageConverter для перетворення HTTP-запитів та відповідей. Адекватні параметри за замовчуванням передбачені "з коробки". Наприклад, об'єкти можуть бути автоматично перетворені в JSON (за допомогою бібліотеки Jackson) або в XML (за допомогою розширення Jackson XML, якщо воно доступне, або за допомогою JAXB, якщо розширення Jackson XML недоступне). За замовчуванням рядки кодуються в UTF-8.

Якщо необхідно додати або налаштувати перетворювачі, можна використовувати клас HttpMessageConverters для Spring Boot, як показано в наступному лістингу:

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)
    }
}

Будь-який бін HttpMessageConverter, присутній у контексті, додається до списку перетворювачів. Так само можна перевизначати перетворювачі за замовчуванням.

MessageCodesResolver

Spring MVC містить стратегію генерації кодів помилок для виведення повідомлень про помилки прив'язки: MessageCodesResolver.Якщо встановлено властивість spring.mvc.message-codes-resolver-format PREFIX_ERROR_CODE або POSTFIX_ERROR_CODE, Spring Boot створить його для тебе.