Для сервлет-додатків Spring Boot передбачає підтримку вбудованих серверів Tomcat, Jetty, і Undertow. Більшість розробників використовують відповідний "стартер" для отримання повністю налаштованого екземпляра. За замовчуванням вбудований сервер прослуховує HTTP-запити через порт 8080.

Сервлети, фільтри та слухачі

При використанні вбудованого контейнера сервлетів можна зареєструвати сервлети, фільтри та будь-які слухачі (такі як HttpSessionListener) зі специфікації сервлетів, або використовуючи біни Spring, або здійснюючи сканування компонентів сервлетів.

Реєстрація сервлетів, фільтрів та слухачів як бінів Spring

Будь-який екземпляр Servlet, Filter або *Listener сервлета, який є біном Spring, реєструється у вбудованому контейнері. Це може бути особливо зручно, якщо потрібно послатися на значення з application.properties під час конфігурування.

За замовчуванням, якщо контекст містить лише один сервлет, він відображається на /. У разі кількох сервлет-бінів ім'я біна використовується як префікс шляху. Фільтри відображаються на /*.

Якщо відображення на основі угод недостатньо гнучке, то можна використовувати класи ServletRegistrationBean, FilterRegistrationBean та ServletListenerRegistrationBean для повного контролю.

Зазвичай бін-фільтр можна не впорядковувати. Якщо потрібно певне впорядкування, необхідно анотувати Filter за допомогою анотації @Order або реалізувати його як клас Ordered. Конфігурувати порядок Filter, анотувавши його метод біна за допомогою анотації @Order, не можна. Якщо не можна змінити клас Filter, щоб додати анотацію @Order або реалізувати клас Ordered, необхідно визначити FilterRegistrationBean для Filter та встановити порядок реєстраційного біна за допомогою методу setOrder(int). Уникай конфігурування фільтра, який читає тіло запиту Ordered.HIGHEST_PRECEDENCE, оскільки це може суперечити конфігурації кодування символів вашої програми. Якщо фільтр сервлета обгортає запит, його налаштований порядок виконання повинен бути меншим або дорівнювати OrderedFilter.REQUEST_WRAPPER_FILTER_MAX_ORDER.

Щоб побачити порядок виконання кожного Filter у програмі, активуй логування на рівні налагодження для групи вебжурналювання (logging.level.web=debug). Детальна інформація про зареєстровані фільтри, включно з їхнім порядком і шаблонами URL-адрес, буде реєструватися при початковому запуску. Будь уважним при реєстрації бінів Filter, оскільки їхні екземпляри створюються на дуже ранніх стадіях життєвого циклу програми. Якщо необхідно зареєструвати Filter, який взаємодіє з іншими бінами, використовуй замість нього DelegatingFilterProxyRegistrationBean.

Ініціалізація контексту сервлетів

Вбудовані контейнери сервлетів не виконують безпосередньо інтерфейс servlet 3.0+ під назвою javax.servlet.ServletContainerInitializer або інтерфейс org.springframework.web.WebApplicationInitializer зі Spring. Це навмисне проєктне рішення, покликане знизити ризик того, що сторонні бібліотеки, розроблені для виконання всередині war-файлу, можуть зламати програми Spring Boot.

Якщо потрібно виконати ініціалізацію контексту сервлетів у додатку Spring Boot, необхідноо зареєструвати бін, який реалізує інтерфейс org.springframework.boot.web.servlet.ServletContextInitializer. Єдиний метод onStartup забезпечує доступ до ServletContext і, в разі необхідності, може бути легко використаний як адаптер до існуючого WebApplicationInitializer.

Сканування на предмет сервлетів, фільтрів та слухачів

При використанні вбудованого контейнера автоматичну реєстрацію класів, позначених анотаціями @WebServlet, @WebFilter та @ WebListener можна активувати за допомогою анотації @ServletComponentScan.

@ServletComponentScan не працює в автономному контейнері, де замість неї використовуються вбудовані механізми виявлення контейнера.

The ServletWebServerApplicationContext

З точки зору внутрішнього влаштування Spring Boot використовує інший тип ApplicationContext для підтримки вбудованого контейнера сервлетів. ServletWebServerApplicationContext — це особливий тип WebApplicationContext, який самозавантажується шляхом пошуку єдиного біна ServletWebServerFactory. Зазвичай автоматично конфігуруються TomcatServletWebServerFactory, JettyServletWebServerFactory або UndertowServletWebServerFactory.

Найчастіше знати про ці класи реалізації не потрібно. Більшість програм конфігуруються автоматично, а відповідні ApplicationContext та ServletWebServerFactory створюються без твоєї участі.

У конфігурації вбудованого контейнера ServletContext встановлюється як елемент запуску сервера, який відбувається під час ініціалізації контексту програми. У зв'язку з цим біни в ApplicationContext не можна надійно ініціалізувати за допомогою ServletContext. Один зі способів обходу цього обмеження — впровадження ApplicationContext як залежності від біну та звернення до ServletContext лише в разі необхідності. Інший спосіб — використовувати зворотний виклик відразу після запуску сервера. Це можна зробити за допомогою ApplicationListener, який прослуховує ApplicationStartedEvent таким чином:


import javax.servlet.ServletContext;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.web.context.WebApplicationContext;
public class MyDemoBean implements ApplicationListener<ApplicationStartedEvent> {
    private ServletContext servletContext;
    @Override
    public void onApplicationEvent(ApplicationStartedEvent event) {
        ApplicationContext applicationContext = event.getApplicationContext();
        this.servletContext = ((WebApplicationContext) applicationContext).getServletContext();
    }
}

Персоналізація налаштувань вбудованих контейнерів сервлетів

Загальні параметри контейнера сервлетів можна конфігурувати за допомогою властивостей Environment зі Spring. Зазвичай властивості визначаються у файлі application.properties або application.yaml.

Загальні параметри сервера включають:

  • Параметри мережі: порт для прослуховування вхідних HTTP-запитів (server.port), адреса інтерфейсу для прив'язки до server.address, тощо.

  • Параметри сесії: чи зберігається сесія тривало (server.servlet.session.persistent), час очікування сесії (server.servlet.session.timeout), розташування даних сесії (server.servlet.session.store-dir) та cookie-конфігурація сесії (server.servlet.session.cookie.*).

  • Керування помилками: розташування сторінки помилок (server.error.path) тощо.

  • SSL

  • HTTP-стискання

Spring Boot вживає всіх необхідних заходів, щоб відкривати доступ до якомога більшої кількості загальних налаштувань, але це не завжди можливо. У таких випадках спеціалізованими просторами імен передбачені специфічні для сервера налаштування (див. server.tomcat та server.undertow). Наприклад, журнали доступу можна налаштувати з урахуванням специфічних особливостей вбудованого контейнера сервлетів.

Повний список див. ServerProperties.

Атрибут SameSite для файлів cookie

Веббраузери можуть використовувати атрибут SameSite для файлів cookie, щоб керувати тим, чи передаються файли cookie взагалі при міжсайтових запитах і яким чином. Атрибут особливо актуальний для сучасних веббраузерів, які почали змінювати значення за замовчуванням, яке використовується за відсутності атрибута.

Якщо ти хочеш змінити атрибут SameSite твого сесійного файлу cookie, можеш використовувати властивість server.servlet.session.cookie.same-site. Ця властивість підтримується автоматично налаштованими серверами Tomcat, Jetty і Undertow. Він також використовується для налаштування бінів SessionRepository на основі сервлетів Сесія в Spring.

Наприклад, якщо потрібно, щоб сесійний файл cookie мав атрибут SameSite, значення якого одно None, то можна додати наступний код до файлу application.properties або application.yaml:

Properties
server.servlet.session.cookie.same-site=none
Yaml
server:
    servlet:
        session:
            cookie:
                same-site: "none"

Якщо ти хочеш змінити атрибут SameSite для інших файлів cookie, доданих у HttpServletResponse, то можеш використовувати CookieSameSiteSupplier. CookieSameSiteSupplier передається Cookie і може повернути значення SameSite або null.

Існує низка допоміжних фабричних та фільтраційних методів, які можна використовувати для швидкого пошуку певних файлів cookie. Наприклад, додавання наступного біна автоматично застосує SameSite з Lax для всіх файлів cookie з ім'ям, що відповідає регулярному виразу myapp.*.

Java
import org.springframework.boot.web.servlet.server.CookieSameSiteSupplier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class MySameSiteConfiguration {
    @Bean
    public CookieSameSiteSupplier applicationCookieSameSiteSupplier() {
        return CookieSameSiteSupplier.ofLax().whenHasNameMatching("myapp.*");
    }
}
Kotlin
            import org.springframework.boot.web.servlet.server.CookieSameSiteSupplier
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration(proxyBeanMethods = false)
class MySameSiteConfiguration {
    @Bean
    fun applicationCookieSameSiteSupplier(): CookieSameSiteSupplier {
        return CookieSameSiteSupplier.ofLax().whenHasNameMatching("myapp.*")
    }
}
        

Програмна персоналізація налаштувань

Якщо необхідно програмно налаштувати вбудований контейнер сервлетів, то можна зареєструвати бін Spring, який реалізує інтерфейс WebServerFactoryCustomizer. WebServerFactoryCustomizer надає доступ до фабрики ConfigurableServletWebServerFactory, яка містить безліч сеттерів. У цьому прикладі показано програмне налаштування порту:

Java

import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.stereotype.Component;
@Component
public class MyWebServerFactoryCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
    @Override
    public void customize(ConfigurableServletWebServerFactory server) {
        server.setPort(9000);
    }
}
Kotlin
            import org.springframework.boot.web.server.WebServerFactoryCustomizer
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory
import org.springframework.stereotype.Component
@Component
class MyWebServerFactoryCustomizer : WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
    override fun customize(server: ConfigurableServletWebServerFactory) {
        server.setPort(9000)
    }
}
        

TomcatServletWebServerFactory, JettyServletWebServerFactory та UndertowServletWebServerFactory – це спеціальні варіанти ConfigurableServletWebServerFactory, які мають додаткові сеттери для Tomcat, Jetty та Undertow відповідно. У цьому прикладі показано, як налаштувати TomcatServletWebServerFactory, який надає доступ до пов'язаних з Tomcat параметрів конфігурації:

Java
import java.time.Duration;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.stereotype.Component;
@Component
public class MyTomcatWebServerFactoryCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
    @Override
    public void customize(TomcatServletWebServerFactory server) {
        server.addConnectorCustomizers((connector) -> connector.setAsyncTimeout(Duration.ofSeconds(20).toMillis()));
    }
}>
Kotlin
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory
import org.springframework.boot.web.server.WebServerFactoryCustomizer
import org.springframework.stereotype.Component
import java.time.Duration
@Component
class MyTomcatWebServerFactoryCustomizer : WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
    override fun customize(server: TomcatServletWebServerFactory) {
        server.addConnectorCustomizers({ connector -> connector.asyncTimeout = Duration.ofSeconds(20).toMillis() })
    }
}

Пряма персоналізація налаштувань ConfigurableServletWebServerFactory

У більш складних випадках застосування, що вимагають розширення з ServletWebServerFactory, можна самостійно відкрити бін такого типу.

Для багатьох параметрів конфігурації передбачені сетери. Також передбачено кілька "перехоплювачів" для захищених методів, якщо потрібно зробити щось неординарніше.

Автоматично налаштовані модулі налаштування все ще застосовуються до кастомної фабрики, тому використовувати цю опцію слід обережно.

Обмеження JSP

При запуску програми Spring Boot, яка використовує вбудований контейнер сервлетів (і упаковано як виконуваний архів), існують деякі обмеження засобів підтримки JSP.

  • З Jetty і Tomcat все має працювати, якщо ти використовуєш упаковку до war-файлу. war-файл, що виконується, буде працювати при запуску через java-jar, а також буде розгортатися в будь-якому стандартному контейнері. Сторінки JSP не підтримуються під час використання jar-файлу, що виконується.

  • Undertow не підтримує сторінки JSP.

  • Створення кастомної сторінки error.jsp не перевизначає стандартне подання для обробки помилок. Натомість слід використовувати кастомні сторінки помилок.