«1. Обзор

В этом руководстве мы концептуально поймем, что такое сервлеты и контейнеры сервлетов и как они работают.

Мы также увидим их в контексте запроса, ответа, объектов сеанса, общих переменных и многопоточности.

2. Что такое сервлеты и их контейнеры

Сервлеты — это компонент среды JEE, используемый для веб-разработки. По сути, это Java-программы, работающие внутри границ контейнера. В целом они отвечают за прием запроса, его обработку и отправку ответа. Введение в сервлеты Java обеспечивает хорошее базовое понимание предмета.

Чтобы использовать их, сервлеты должны быть сначала зарегистрированы, чтобы контейнер, основанный на JEE или Spring, мог подобрать их при запуске. Вначале контейнер создает экземпляр сервлета, вызывая его метод init().

После завершения инициализации сервлет готов принимать входящие запросы. Впоследствии контейнер направляет эти запросы на обработку в методе service() сервлета. После этого он далее делегирует запрос соответствующему методу, такому как doGet() или doPost(), в зависимости от типа HTTP-запроса.

С помощью destroy() контейнер разрывает сервлет и больше не может принимать входящие запросы. Мы называем этот цикл init-service-destroy жизненным циклом сервлета.

Теперь давайте посмотрим на это с точки зрения контейнера, такого как Apache Tomcat или Jetty. При запуске он создает объект ServletContext. Задача ServletContext состоит в том, чтобы функционировать как память сервера или контейнера и запоминать все сервлеты, фильтры и прослушиватели, связанные с веб-приложением, как описано в его web.xml или эквивалентных аннотациях. Пока мы не остановим или не завершим контейнер, ServletContext останется с ним.

Однако здесь важную роль играет параметр загрузки при запуске сервлета. Если этот параметр имеет значение больше нуля, только тогда сервер инициализирует его при запуске. Если этот параметр не указан, то функция init() сервлета вызывается при первом запросе.

3. Запрос, ответ и сеанс

В предыдущем разделе мы говорили об отправке запросов и получении ответов, что является краеугольным камнем любого клиент-серверного приложения. Теперь давайте подробно рассмотрим их применительно к сервлетам.

В этом случае запрос будет представлен HttpServletRequest, а ответ — HttpServletResponse.

Всякий раз, когда клиент, такой как браузер или команда curl, отправляет запрос, контейнер создает новый объект HttpServletRequest и HttpServletResponse. Затем он передает эти новые объекты сервисному методу сервлета. На основе атрибута метода HttpServletRequest этот метод определяет, какой из методов doXXX следует вызывать.

Помимо информации о методе, объект запроса также содержит другую информацию, такую ​​как заголовки, параметры и тело. Точно так же объект HttpServletResponse также содержит заголовки, параметры и тело — мы можем настроить их в методе doXXX нашего сервлета.

Эти объекты недолговечны. Когда клиент получает ответ, сервер помечает объекты запроса и ответа для сборки мусора.

Как бы мы тогда поддерживали состояние между последующими клиентскими запросами или подключениями? HttpSession — ответ на эту загадку.

Это в основном связывает объекты с сеансом пользователя, так что информация, относящаяся к конкретному пользователю, может сохраняться в нескольких запросах. Обычно это достигается с помощью концепции файлов cookie с использованием JSESSIONID в качестве уникального идентификатора для данного сеанса. Мы можем указать тайм-аут для сеанса в web.xml:

<session-config>
    <session-timeout>10</session-timeout>
</session-config>

Это означает, что если наш сеанс бездействовал в течение 10 минут, сервер отбросит его. Любой последующий запрос создаст новый сеанс.

4. Как сервлеты обмениваются данными

Существуют различные способы обмена данными между сервлетами в зависимости от требуемой области.

«Как мы видели в предыдущих разделах, разные объекты имеют разное время жизни. Объекты HttpServletRequest и HttpServletResponse существуют только между одним вызовом сервлета. HttpSession существует, пока он активен и не истекло время ожидания.

Срок службы ServletContext самый длинный. Он рождается вместе с веб-приложением и уничтожается только тогда, когда само приложение закрывается. Поскольку экземпляры сервлета, фильтра и прослушивателя привязаны к контексту, они также существуют, пока запущено и работает веб-приложение.

Следовательно, если наше требование состоит в том, чтобы обмениваться данными между всеми сервлетами, скажем, если мы хотим подсчитать количество посетителей нашего сайта, тогда мы должны поместить переменную в ServletContext. Если нам нужно обмениваться данными в сеансе, мы сохраняем их в области сеанса. В этом случае примером может служить имя пользователя.

Наконец, есть область запроса, относящаяся к данным для одного запроса, например полезная нагрузка запроса.

5. Обработка многопоточности

Несколько объектов HttpServletRequest совместно используют сервлеты друг для друга, так что каждый запрос работает со своим собственным потоком экземпляра сервлета.

Что это фактически означает с точки зрения безопасности потоков, так это то, что мы не должны назначать данные области запроса или сеанса в качестве переменной экземпляра сервлета.

Например, давайте рассмотрим этот фрагмент:

public class ExampleThree extends HttpServlet {
    
    private String instanceMessage;

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
      throws ServletException, IOException {
        String message = request.getParameter("message");
        instanceMessage = request.getParameter("message");
        request.setAttribute("text", message);
        request.setAttribute("unsafeText", instanceMessage);
        request.getRequestDispatcher("/jsp/ExampleThree.jsp").forward(request, response);
    }
}

В этом случае все запросы в сеансе совместно используют instanceMessage, тогда как сообщение уникально для данного объекта запроса. Следовательно, в случае одновременных запросов данные в instanceMessage могут быть несогласованными.

6. Заключение

В этом руководстве мы рассмотрели некоторые понятия, связанные с сервлетами, их контейнерами и несколькими важными объектами, вокруг которых они вращаются. Мы также увидели, как сервлеты обмениваются данными и как на них влияет многопоточность.

Как всегда, исходный код доступен на GitHub.