Сервлет — это Java-класс, который обрабатывает HTTP-запросы на стороне сервера и формирует ответ. Он работает внутри контейнера сервлетов (например, Apache Tomcat) и служит точкой входа веб-приложения: принимает запрос от браузера, выполняет логику и возвращает HTML, JSON или другой контент.
Содержание
- Servlet, GenericServlet и HttpServlet: три уровня
- Контейнер сервлетов: кто вызывает ваш код
- Жизненный цикл сервлета: init, service, destroy
- doGet и doPost: чем отличаются
- Минимальный рабочий сервлет
- Типичная ошибка: старый пакет javax
- Развертывание: аннотация или web.xml
- Сервлеты, JSP и Spring MVC: что где
- Выводы
- Где применяется / связь с практикой
- FAQ
Ниже разберем, что описывает интерфейс Servlet, зачем нужен класс HttpServlet, как устроен жизненный цикл (init, service, destroy), чем отличаются методы doGet и doPost, что делает контейнер и как развернуть первый сервлет через аннотацию или web.xml.
Servlet, GenericServlet и HttpServlet: три уровня
Чтобы не путаться, разведем три близких понятия сразу:
Servlet— базовый интерфейс из пакетаjakarta.servlet. Он объявляет методы жизненного цикла (init,service,destroy) и не привязан к протоколу.GenericServlet— абстрактный класс, который реализуетServletи убирает рутину. Он протокол-независим: у него есть один методservice(ServletRequest, ServletResponse).HttpServlet— наследникGenericServlet, заточенный под HTTP. Именно его наследуют на практике: он разбирает HTTP-метод запроса и вызываетdoGet,doPostи другиеdoXxx.
Почти всегда в реальном коде вы расширяете HttpServlet, а не реализуете Servlet напрямую. Интерфейс Servlet полезно знать, чтобы понимать, откуда берутся методы жизненного цикла.
Важное уточнение по версиям. С Jakarta EE 9 (2020 год) пакеты переименованы: раньше это был javax.servlet.*, теперь jakarta.servlet.*. Старые статьи и код с javax.servlet работают только на Tomcat 9 и ниже. Для Tomcat 10 и новее нужен именно jakarta.servlet.
Контейнер сервлетов: кто вызывает ваш код
Сервлет не запускается сам по себе, как программа с методом main. Его жизнью управляет контейнер сервлетов (он же веб-контейнер) — серверная среда, которая принимает TCP-соединения, разбирает HTTP, создает объекты запроса и ответа и вызывает нужные методы вашего класса.
Самый распространенный контейнер — Apache Tomcat. Есть и другие: Jetty, Undertow, а также полноценные серверы приложений (WildFly, Open Liberty, Payara).
Контейнер берет на себя пул потоков, разбор протокола, управление жизненным циклом и маршрутизацию по URL, а ваша задача — описать реакцию на запрос. Один экземпляр сервлета по умолчанию обслуживает много запросов параллельно, каждый в своем потоке. Поэтому поля класса сервлета — общее состояние для всех потоков: хранить в них данные конкретного пользователя нельзя, это источник гонок. Пользовательские данные держат в запросе, ответе или сессии.
Жизненный цикл сервлета: init, service, destroy
Жизненный цикл описан интерфейсом Servlet и состоит из трех методов, которые вызывает контейнер.
| Метод | Когда вызывается | Сколько раз | Зачем |
|---|---|---|---|
init() |
При создании экземпляра сервлета | Один раз | Инициализация ресурсов (пул соединений, конфигурация) |
service() |
На каждый входящий запрос | Много раз, параллельно | Диспетчеризация запроса по HTTP-методу |
destroy() |
Перед выгрузкой сервлета | Один раз | Освобождение ресурсов |
Как это работает по шагам:
- Контейнер загружает класс. По умолчанию — при первом обращении (ленивая загрузка), но можно попросить загрузку при старте через
loadOnStartup. - Контейнер создает единственный экземпляр и вызывает
init(). Тут удобно один раз открыть, например, пул соединений с базой. - На каждый запрос вызывается
service(). ВHttpServletон уже реализован: смотрит HTTP-метод и вызываетdoGet,doPostи так далее. Свою логику вы пишете вdoXxx, аserviceобычно не трогаете. - При остановке приложения контейнер вызывает
destroy(), где нужно закрыть ресурсы.
Методы init() и destroy() вызываются ровно по одному разу за жизнь экземпляра, а service() — на каждый запрос. Уточнение про сборку мусора: сам сервлет не является сборщиком мусора JVM (это распространенная ошибка старых статей); после destroy() экземпляр просто становится недостижимым, и его убирает обычный GC.
doGet и doPost: чем отличаются
HttpServlet разбирает HTTP-метод запроса и вызывает соответствующий обработчик. Чаще всего переопределяют два.
| Метод HTTP | Метод сервлета | Типичное назначение | Где параметры |
|---|---|---|---|
| GET | doGet |
Получение данных, переход по ссылке | В строке URL (query string) |
| POST | doPost |
Отправка формы, изменение данных | В теле запроса |
Практическое правило выбора: GET — для операций чтения, которые безопасно повторять и кэшировать; POST — когда запрос меняет состояние или передает чувствительные данные (пароль в URL светится в логах и истории браузера). Есть и другие обработчики: doPut, doDelete, doHead — под соответствующие HTTP-методы REST-стиля.
Параметры запроса в обоих случаях читаются одинаково — через request.getParameter("имя"), контейнер сам достает их из URL или из тела формы.
Минимальный рабочий сервлет
Начнем с полного примера, который можно собрать и запустить. Это сервлет, отвечающий на GET-запрос по адресу /hello.
package com.example;
import java.io.IOException;
import java.io.PrintWriter;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws IOException {
response.setContentType("text/html; charset=UTF-8");
PrintWriter out = response.getWriter();
String name = request.getParameter("name");
if (name == null || name.isBlank()) {
name = "гость";
}
out.println("<h1>Привет, " + name + "!</h1>");
}
}
Что происходит после развертывания. Открыв в браузере http://localhost:8080/app/hello, вы увидите заголовок «Привет, гость!». Если добавить параметр — http://localhost:8080/app/hello?name=Иван — ответ станет «Привет, Иван!». Часть app в пути — это контекст приложения (имя развернутого war-модуля), hello — путь из аннотации @WebServlet.
Разберем ключевые строки:
@WebServlet("/hello")привязывает сервлет к URL без файлаweb.xml.extends HttpServletдает готовую реализациюservice(), которая направит GET в нашdoGet.response.setContentType("text/html; charset=UTF-8")вызывают до полученияPrintWriter, иначе кодировка ответа может не примениться.request.getParameter("name")возвращает значение илиnull, если параметра нет; проверка наnullобязательна.
Замечание по безопасности: пример подставляет пользовательский ввод в HTML напрямую, что открывает XSS. В реальном коде значение нужно экранировать (например, библиотекой OWASP Java Encoder или средствами шаблонизатора), а не отдавать в страницу как есть.
Типичная ошибка: старый пакет javax
Разберем частый сбой при переезде на новый Tomcat. Возьмем импорт из старого туториала:
import javax.servlet.http.HttpServlet;
Фактический результат на Tomcat 10 или 11: приложение не работает, в логах видно ClassNotFoundException или сервлет просто не находится (ответ 404), потому что контейнер не содержит пакета javax.servlet — он его переименовал.
Исправление — заменить пакет на jakarta:
import jakarta.servlet.http.HttpServlet;
Если переехать сразу нельзя, оставайтесь на Tomcat 9 (там еще javax.servlet) либо прогоните код через инструмент миграции Eclipse Transformer, который переписывает импорты. Смешивать javax и jakarta в одном модуле нельзя.
Развертывание: аннотация или web.xml
Связать сервлет с URL можно двумя способами. Оба рабочие, выбор зависит от того, где удобнее держать конфигурацию.
| Способ | Где описано | Плюс | Когда выбрать |
|---|---|---|---|
Аннотация @WebServlet |
В коде класса | Меньше файлов, все рядом | Новый код, простые правила маршрутизации |
Дескриптор web.xml |
В WEB-INF/web.xml |
Конфигурация отдельно от кода, можно менять без пересборки класса | Внешняя настройка, переопределение, общий обзор маршрутов |
Тот же сервлет без аннотации описывается в web.xml так:
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
version="6.0">
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.example.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
Здесь servlet-name связывает объявление класса и правило URL: тег servlet регистрирует класс под именем, а servlet-mapping привязывает это имя к пути. Файл web.xml лежит в каталоге WEB-INF внутри war-архива. Если используете аннотацию, web.xml можно вовсе не создавать.
Сервлеты, JSP и Spring MVC: что где
Сервлеты редко пишут для генерации HTML вручную — на голом PrintWriter верстать страницу неудобно. Поэтому вокруг них выросли технологии представления и фреймворки.
- JSP (Jakarta Server Pages) — страницы с HTML и вставками Java-кода, которые контейнер сам компилирует в сервлет. JSP удобнее для разметки, но сегодня в новых проектах его берут редко, предпочитая шаблонизаторы вроде Thymeleaf.
- Spring MVC — веб-фреймворк, который работает поверх модели сервлетов. Внутри у него один центральный
DispatcherServlet, а вы пишете контроллеры с аннотациями@GetMappingи@PostMappingвместо ручныхdoGetиdoPost. Диспетчеризацию, разбор параметров, сериализацию JSON фреймворк берет на себя.
Практический вывод: понимать сервлеты нужно даже тем, кто пишет на Spring, потому что весь стек стоит на них. Но продуктовый код в 2026 году чаще пишут на Spring MVC или Spring WebFlux, а чистые сервлеты остаются фундаментом и темой для собеседований.
Выводы
- Сервлет — это Java-класс для обработки HTTP-запросов на сервере; на практике наследуют
HttpServlet, а не реализуют интерфейсServletнапрямую. - Жизненным циклом (
init,service,destroy) управляет контейнер сервлетов, чаще всего Apache Tomcat;initиdestroyвызываются по разу,service— на каждый запрос. doGetобрабатывает чтение (параметры в URL),doPost— отправку и изменение данных (параметры в теле); выбор метода влияет на кэширование и безопасность.- С Jakarta EE 9 пакеты переименованы из
javax.servletвjakarta.servlet; это ключевое отличие для Tomcat 10 и новее. - Развернуть сервлет можно аннотацией
@WebServletили черезweb.xml; JSP и Spring MVC надстроены над той же моделью сервлетов.
Где применяется / связь с практикой
Сервлеты — обязательная часть картины мира Java-веб-разработчика: без них не понять ни Tomcat, ни Spring, ни то, как приложение вообще принимает запросы. На собеседованиях на позиции джуна и мидла жизненный цикл, разница doGet и doPost и роль контейнера — типовые вопросы.
Освойте тему на практике
Разобраться в связке «сервлет — контейнер — Spring» системно и с практикой на реальных приложениях помогает курс Java-разработчик (Spring): там веб-слой проходят от сервлетов до Spring MVC и REST. Оценить формат и уровень занятий заранее можно на открытых уроках Otus — это бесплатные вебинары с разбором тем и ответами преподавателя.
FAQ
Нужно ли учить сервлеты, если сразу планирую писать на Spring?
Да. Spring MVC работает поверх сервлетов, а его DispatcherServlet — это обычный сервлет. Понимание модели помогает читать логи, настраивать фильтры и разбираться, когда что-то ломается на уровне контейнера.
Чем сервлет отличается от фильтра (Filter)?
Сервлет формирует ответ на запрос по конкретному URL. Фильтр (jakarta.servlet.Filter) перехватывает запросы и ответы до и после сервлета — для аутентификации, логирования, сжатия. Фильтр не отдает контент сам, а стоит в цепочке перед сервлетом.
Один сервлет обрабатывает запросы по очереди или параллельно?
Параллельно. Контейнер создает один экземпляр сервлета и вызывает его из разных потоков одновременно. Поэтому изменяемое состояние в полях класса небезопасно; пользовательские данные держат в объектах запроса, ответа или сессии.



