Все ли в порядке в Web-приложениях, которые хранят свое состояние в сеансе пользователя?

Исходники

Несмотря на то, что в мире Java имеется много Web-инфраструктур, все они, прямо или косвенно, основаны на инфраструктуре Java Servlets. Java Servlets API предоставляет набор полезных возможностей, включая управление состоянием с помощью объектов HttpSession и ServletContext, которые позволяют приложению сохранять данные на протяжении нескольких запросов от пользователя. Однако для Web-приложений существует ряд тонких (и по большей части недокументированных) правил, регулирующих работу с совместно используемыми объектами, из-за которых во многих приложениях появляются трудноуловимые ошибки. В результате во многих Web-приложениях, хранящих свое состояние в сеансе пользователя, возникают запутанные ситуации и проблемы.

Контейнеры с ограниченным сроком жизни

Экземпляры классов ServletContext, HttpSession и HttpRequest в спецификации Java Servlets называются контейнерами с ограниченным сроком жизни (scoped containers). У каждого из них есть методы getAttribute() и setAttribute(), которые позволяют приложению записывать и считывать используемые им данные. Различие между этими контейнерами заключается в продолжительности их жизненного цикла. Например, в случае HttpRequest данные хранятся только во время обработки запроса; в HttpSession данные сохраняются только пока существует сеанс между пользователем и приложением; в случае ServletContext данные существуют на протяжении всего жизненного цикла приложения.

Поскольку протокол HTTP не обеспечивает сохранение состояния между запросами пользователя (stateless), контейнеры с ограниченным сроком жизни незаменимы при создании Web-приложений, обеспечивающих хранение состояния; при этом сервлет-контейнер отвечает за управление состоянием приложения и жизненным циклом использующихся в нем данных. Хотя, в спецификации это четко не оговаривается, контейнеры, ограниченные длительностью сеанса пользователя или длительностью жизни всего приложения, должны быть потокобезопасными, так как методы getAttribute() и setAttribute() могут вызываться в любое время из различных потоков. (Спецификация напрямую не требует, чтобы реализации этих контейнеров были потокобезопасными, но этого требует сама природа служб, обеспечиваемых этими объектами.)

Контейнеры с ограниченным сроком жизни также предоставляют Web-приложениям еще одно потенциальное преимущество: контейнер может управлять репликацией и восстанавливать состояние приложения после сбоя прозрачно для приложения.

Сеанс

Сеанс (session) — это последовательность запросов, отправляемых конкретным клиентом серверу, и ответов, полученных от Web-сервера. Пользователи ожидают, что Web-сайты запомнят их аутентифицирующие данные, содержимое их покупательской корзины и информацию, введенную в различные формы во время прошлых запросов, но сам по себе протокол HTTP не поддерживает хранение состояния, так что вся информация о запросе должна содержаться в нем самом. Поэтому чтобы организовать продуктивное взаимодействие с пользователем, которое бы длилось дольше, чем время жизни разового запроса, состояние сеанса должно храниться в каком-нибудь другом месте. Инфраструктура Java Servlets позволяет ассоциировать каждый запрос с сеансом и предоставляет интерфейс HttpSession для хранения данных, относящихся к этому сеансу (данные хранятся в виде пар ключ/значение). В листинге 1 приведен стандартный фрагмент исходного кода, сохраняющий корзину покупок покупателя в объект HttpSession: