Опишите жизненный цикл сервлета

Посмотреть в Telegram: @JavaSobes/306
Совсем недавно мы рассмотрели жизненный цикл компонентов Spring-приложения. Поговорим теперь об этапах работы центрального элемента JavaEE web-приложения – сервлета.

События жизненного цикла сервлета можно разделить на две группы. Первая группа – события, относящиеся непосредственно к самому сервлету. Все они объявляются методами из базового интерфейса Servlet. Примерно такие же фазы жизни и у фильтров.

1. Загрузка класса и создание экземпляра сервлета, конструктор. Может быть создано несколько экземпляров, здесь можно располагать только техническую часть инициализации (подробнее).

2. Инициализация – в общем случае метод init. Инициализация гарантированно происходит до первого запроса, один раз на один сервлет (даже если экземпляров несколько). По умолчанию она ленивая, то есть случается непосредственно перед обработкой этого первого запроса. Другой режим, пре-инициализация, включается свойством loadOnStartup.

Если в качестве инициализации нужно всего лишь параметризовать сервлет, вместо реализации метода достаточно передать параметры в свойстве initParams аннотации @WebServlet. Эти параметры, и другую информацию можно прочитать из объекта конфигурации типа ServletConfig. Дефолтная реализация init-метода принимает эту конфигурацию и сохраняется в поле.

3. Обработка запроса – service. В отличие от Spring Framework, в классической JavaEE все запросы вне зависимости от HTTP-метода, пути и набора параметров попадают в единый метод-обработчик. Вся информация о запросе приходит в параметре ServletRequest. У метода нет возвращаемого значения, вместо этого модифицируется второй параметр – ServletResponse. Для фильтров аналогичный метод doFilter, он кроме запроса и ответа принимает третий параметр, FilterChain.

4. Завершение работы – destroy. Здесь всё как обычно с финализациями: вызывается только однажды, только при корректном завершении, после ее вызова service/doFilter уже никогда не будет вызван.
Другая группа событий – то что происходит со связанными с сервлетом сущностями. Таких сущностей три: ServletContext, ServletRequest и HttpSession.

На каждую из этих сущностей есть два интерфейса-слушателя. Интерфейс XListener сообщает о создании и уничтожении сущности через два соответствующих метода. Так, чтобы совершить какое-то действие при открытии новой HTTP-сессии, действие нужно поместить в реализацию метода HttpSessionListener.sessionCreated().

Все три типа позволяют хранить пользовательские данные – атрибуты. Они хранятся по ключам-строкам, как в хэш-таблице. Методы второго интерфейса XAttributeListener дают возможность обработать добавление, изменение и удаление атрибутов. То есть, если где-то в программе в контекст сервлета добавляется значение (вызван ServletContext.setAttribute("myKey", someValue)), то сработает ServletContextAttributeListener.attributeAdded.

• ServletContext – прослойка между отдельными сервлетами и сервлет-контейнером. Есть только один контекст на всё web-приложение. Он в отличие от сервлетов не ленивый, и инициализируется на старте приложения, до инициализации отдельных сервлетов.

• ServletRequest – отдельный запрос. На каждый запрос создается новый такой объект, инициализируется перед основным обработчиком Servlet.service(), уничтожается после.

• HttpSession – HTTP-сессия, которая служит для поддержания состояния между запросами для одного пользователя. Обычно создается вручную внутри обработчика Servlet.service(). Уничтожаться может как тоже вручную в обработчике, так и по истечению срока жизни. Только для сессии есть третий интерфейс, который уведомляет об изменении идентификатора – HttpSessionIdListener.

Чтобы объект под этими интерфейсами начал получать уведомления, его нужно зарегистрировать в контексте. Это делается либо явным вызовом ServletContext.addListener, либо аннотацией @WebListener на класс.