Вопрос-ответ

How do servlets work? Instantiation, sessions, shared variables and multithreading

Как работают сервлеты? Создание экземпляра, сеансы, общие переменные и многопоточность

Предположим, у меня есть веб-сервер, который содержит множество сервлетов. Для передачи информации между этими сервлетами я устанавливаю переменные сеанса и экземпляра.

Теперь, если 2 или более пользователей отправят запрос на этот сервер, что произойдет с переменными сеанса?
Будут ли они общими для всех пользователей или они будут разными для каждого пользователя?
Если они разные, то как сервер мог различать разных пользователей?

Еще один похожий вопрос, если есть n пользователи, обращающиеся к определенному сервлету, то этот сервлет создается только при первом обращении к нему первого пользователя или он создается для всех пользователей отдельно?
Другими словами, что происходит с переменными экземпляра?

Переведено автоматически
Ответ 1

ServletContext

Когда контейнер сервлетов (например, Apache Tomcat) запускается, он развертывает и загружает все свои веб-приложения. При загрузке веб-приложения контейнер сервлетов один раз создает экземпляр ServletContext и сохраняет его в памяти сервера. web.xml И все включенные web-fragment.xml файлы веб-приложения анализируются, и каждый найденный <servlet>, <filter> и <listener> @WebServlet (или каждый класс, помеченный @WebFilter, @WebListener и ServletContext соответственно) будет создан один раз и также будет храниться в памяти сервера, регистрируясь через,,. Для каждого созданного фильтра его init() метод вызывается с экземпляром FilterConfig в качестве аргумента, который, в свою очередь, содержит задействованный ServletContext.

Когда a Servlet имеет значение <servlet><load-on-startup> or @WebServlet(loadOnStartup), равное 0 или больше, то его init() метод также вызывается во время запуска. Эти сервлеты инициализируются в том же порядке, который указан этим значением. Если одно и то же значение указано более чем для одного сервлета, то каждый из этих сервлетов загружается в том же порядке, в каком они отображаются в web.xml, web-fragment.xml или @WebServlet classloading . В случае, если значение "загрузка при запуске" отсутствует или отрицательное, init() метод будет вызываться всякий раз, когда HTTP-запрос попадает в этот сервлет в самый первый раз. Существует два init() метода, один из которых принимает экземпляр ServletConfig в качестве аргумента, который, в свою очередь, содержит задействованный ServletContext, и другой, который не принимает никаких аргументов, но ServletContext доступен по наследству getServletContext() метод.

Когда контейнер сервлета завершит выполнение всех описанных выше шагов инициализации, то ServletContextListener#contextInitialized() будет вызван с ServletContextEvent аргументом, который, в свою очередь, содержит задействованный ServletContext. Это даст разработчику возможность программно зарегистрировать еще один Servlet, Filter или Listener.

Когда контейнер сервлетов завершает работу, он выгружает все веб-приложения, вызывает destroy() метод всех своих инициализированных сервлетов и фильтров, и все Servlet, Filter и Listener ServletContext экземпляры, зарегистрированные через,, уничтожаются. Наконец, ServletContextListener#contextDestroyed() будет вызван и ServletContext сам будет удален.

HttpServletRequest и HttpServletResponse

Контейнер сервлета подключен к веб-серверу, который прослушивает HTTP-запросы по определенному номеру порта (порт 8080 обычно используется при разработке, а порт 80 в производстве). Когда клиент (например, пользователь с веб-браузером или использующий URLConnection программно) отправляет HTTP-запрос, контейнер сервлетов создает новые экземпляры HttpServletRequest и HttpServletResponse и передает их через любой определенный Filter в цепочке и, в конечном итоге, через Servlet экземпляр.

В случае с фильтрами вызывается doFilter() метод. Когда вызывается код контейнера сервлета chain.doFilter(request, response), запрос и ответ переходят к следующему фильтру или попадают в сервлет, если оставшихся фильтров нет.

В случае с сервлетами вызывается service() метод. По умолчанию этот метод определяет, какой из doXxx() методов вызывать на основе request.getMethod(). Если определенный метод отсутствует в сервлете, то в ответе возвращается ошибка HTTP 405.

Объект request предоставляет доступ ко всей информации о HTTP-запросе, такой как его URL, заголовки, строка запроса и тело. Объект response предоставляет возможность управлять HTTP-ответом и отправлять его так, как вы хотите, например, позволяя вам задавать заголовки и тело (обычно с сгенерированным HTML-содержимым из файла JSP). Когда HTTP-ответ зафиксирован и завершен, объекты request и response перерабатываются и становятся доступными для повторного использования.

HttpSession

Когда клиент впервые посещает веб-приложение и / или HttpSession впервые получается с помощью request.getSession(), контейнер сервлета создает новый HttpSession объект, генерирует длинный и уникальный идентификатор (который вы можете получить с помощью session.getId()) и сохраняет его в памяти сервера. Контейнер сервлета также устанавливает Cookie в Set-Cookie заголовке HTTP-ответа с JSESSIONID в качестве имени и уникального идентификатора сеанса в качестве значения.

Согласно спецификации HTTP cookie (контракту, которого должны придерживаться любой приличный веб-браузер и веб-сервер), клиент (веб-браузер) обязан отправлять этот cookie обратно в последующих запросах в Cookie заголовке до тех пор, пока cookie действителен (т. Е. Уникальный идентификатор должен относиться к не закончившемуся сеансу, а домен и путь указаны правильно). Используя встроенный в ваш браузер монитор HTTP-трафика, вы можете проверить, действителен ли файл cookie (нажмите F12 в Chrome / Edge / Firefox 23+ / IE9+ и перейдите на вкладку Net / Сеть). Контейнер сервлета проверяет Cookie заголовок каждого входящего HTTP-запроса на наличие файла cookie с именем JSESSIONID и использует его значение (идентификатор сеанса) для получения связанного с ним файла HttpSession из памяти сервера.

HttpSession Остается активным до тех пор, пока не простаивает (т. Е. Не используется в запросе) дольше, чем значение тайм-аута, указанное в <session-timeout>, параметр в web.xml. Значение таймаута по умолчанию зависит от контейнера сервлета и обычно составляет 30 минут. Итак, когда клиент не посещает веб-приложение дольше указанного времени, контейнер сервлетов уничтожает сеанс. Каждый последующий запрос, даже с указанным файлом cookie, больше не будет иметь доступа к тому же сеансу; контейнер сервлета создаст новый сеанс.

На стороне клиента файл cookie сеанса сохраняется до тех пор, пока экземпляр браузера запущен (в обычном режиме). Если браузер не настроен на восстановление последнего сеанса браузера, когда клиент закрывает экземпляр браузера (все вкладки / окна), сеанс теряется на стороне клиента. В новом экземпляре браузера файл cookie, связанный с сеансом, не будет существовать, поэтому он больше не будет отправляться. Это приводит к созданию совершенно нового HttpSession с использованием совершенно нового файла cookie сеанса.

В двух словах


  • ServletContext Работает столько же, сколько веб-приложение. Он используется совместно для всех запросов во всех сеансах.

  • HttpSession Работает до тех пор, пока клиент взаимодействует с веб-приложением с помощью того же экземпляра браузера, и время ожидания сеанса на стороне сервера не истекло. Он является общим для всех запросов в одном сеансе.

  • HttpServletRequest И HttpServletResponse работают с момента получения сервлетом HTTP-запроса от клиента до получения полного ответа (веб-страницы). Это не доступно где-либо еще.

  • Все Servlet, Filter и Listener экземпляры живут столько же, сколько живет веб-приложение. Они являются общими для всех запросов во всех сеансах.

  • Любое, attribute что определено в ServletContext, HttpServletRequest и HttpSession будет жить столько, сколько живет рассматриваемый объект. Сам объект представляет "область видимости" в фреймворках управления bean, таких как JSF, CDI, Spring и т.д. Эти фреймворки хранят свои компоненты с ограниченной областью видимости как attribute ближайшую соответствующую область видимости.

Потокобезопасность

Тем не менее, возможно, вас больше всего беспокоит потокобезопасность. Теперь вы должны знать, что сервлеты и фильтры являются общими для всех запросов. Самое приятное в Java то, что она многопоточна, и разные потоки (читай: HTTP-запросы) могут использовать один и тот же экземпляр. В противном случае было бы слишком дорого воссоздавать init() и destroy() их для каждого отдельного запроса.

Вы также должны понимать, что вам никогда не следует назначать какие-либо данные запроса или области сеанса в качестве переменной экземпляра сервлета или фильтра. Он будет использоваться совместно со всеми другими запросами в других сеансах. Это не потокобезопасно! Приведенный ниже пример иллюстрирует это:

public class ExampleServlet extends HttpServlet {

private Object thisIsNOTThreadSafe;

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Object thisIsThreadSafe;

thisIsNOTThreadSafe = request.getParameter("foo"); // BAD!! Shared among all requests!
thisIsThreadSafe = request.getParameter("foo"); // OK, this is thread safe.
}
}

Смотреть также:

Ответ 2

Сеансы

введите описание изображения здесь
введите описание изображения здесь

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

Создание экземпляра сервлета

Если load-on-startup имеет значение false:

введите описание изображения здесь
введите описание изображения здесь

Если загрузка при запуске имеет значение true:

введите описание изображения здесь
введите описание изображения здесь

Как только он перейдет в сервисный режим и в groove, тот же сервлет будет работать с запросами от всех других клиентов.

введите описание изображения здесь

Why isn't it a good idea to have one instance per client? Think about this: Will you hire one pizza guy for every order that came? Do that and you'd be out of business in no time.

It comes with a small risk though. Remember: this single guy holds all the order information in his pocket: so if you're not cautious about thread safety on servlets, he may end up giving the wrong order to a certain client.

Ответ 3

Session in Java servlets is the same as session in other languages such as PHP. It is unique to the user. The server can keep track of it in different ways such as cookies, url rewriting etc. This Java doc article explains it in the context of Java servlets and indicates that exactly how session is maintained is an implementation detail left to the designers of the server. The specification only stipulates that it must be maintained as unique to a user across multiple connections to the server. Check out this article from Oracle for more information about both of your questions.

Редактировать Здесь есть отличное руководство о том, как работать с сеансом внутри сервлетов. И здесь есть глава от Sun о Java-сервлетах, что это такое и как их использовать. Между этими двумя статьями вы должны быть в состоянии ответить на все ваши вопросы.

Ответ 4

При запуске контейнера сервлетов (например, Apache Tomcat) он будет считывать данные из web.xml файл (только один для каждого приложения), если что-то пойдет не так или появится ошибка в консоли на стороне контейнера, в противном случае он развернет и загрузит все веб-приложения с помощью web.xml (так назван дескриптор развертывания).

На этапе создания экземпляра сервлета экземпляр сервлета готов, но он не может выполнить запрос клиента, поскольку в нем отсутствуют две части информации:
1: информация о контексте
2: информация о начальной конфигурации

Движок сервлетов создает объект интерфейса ServletConfig, инкапсулируя в него вышеупомянутую недостающую информацию. Движок сервлетов вызывает init() сервлета, предоставляя ссылки на объекты ServletConfig в качестве аргумента. Как только init() будет полностью выполнен, сервлет готов обслуживать запрос клиента.

В) Сколько раз за время существования сервлета происходит создание экземпляра и инициализация??

A) только один раз (для каждого клиентского запроса создается новый поток) только один экземпляр сервлета обслуживает любое количество клиентских запросов, т.е. После обслуживания одного клиентского запроса сервер не умирает. Он ожидает других клиентских запросов, т. е. ограничение CGI (для каждого клиентского запроса создается новый процесс) преодолевается с помощью сервлета (внутренний движок сервлета создает поток).

Вопрос) Как работает концепция сеанса?

A)всякий раз, когда getSession() вызывается для объекта HttpServletRequest

Шаг 1: объект запроса оценивается на предмет идентификатора входящего сеанса.

Шаг 2: если идентификатор недоступен, создается совершенно новый объект HttpSession и генерируется соответствующий ему идентификатор сеанса (т. Е. хэш-таблицы) идентификатор сеанса сохраняется в объекте ответа httpservlet, а ссылка на объект HttpSession возвращается сервлету (doGet / doPost).

Шаг 3: если доступный идентификатор нового объекта сеанса не создан, идентификатор сеанса выбирается из запроса, поиск объекта производится в коллекции сеансов с использованием идентификатора сеанса в качестве ключа.

После успешного поиска идентификатор сеанса сохраняется в HttpServletResponse, а существующие ссылки на объекты сеанса возвращаются в doGet() или doPost() UserDefineservlet .

Примечание:

1) когда управление переходит от кода сервлета к клиенту, не забывайте, что объект сеанса удерживается контейнером сервлета, т. е. механизмом сервлета

2) многопоточность оставлена разработчикам сервлетов для реализации, т.е. обрабатывать множественные запросы клиента, не беспокоясь о многопоточном коде

В краткой форме:

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

java multithreading servlets