Для сервлет-приложений 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 не переопределяет представление по умолчанию для обработки ошибок. Вместо этого следует использовать кастомные страницы ошибок.