Лекции 2025. Java. Белая / Ответы на билеты. Java
.pdf
Важные соображения:
Доверие заголовкам: HTTP-заголовки, такие как
, могут быть подделаны клиентом. Поэтому, если безопасность критична, вы должны доверять этим заголовкам только от доверенных прокси-серверов (например, если ваш сервер приложений настроен так, чтобы принимать эти заголовки только от вашего собственного фронтенд-прокси/балансировщика). Некоторые веб-серверы или балансировщики нагрузки можно настроить так, чтобы они перезаписывали или устанавливали эти заголовки надежным образом.
Порядок проверки: Порядок проверки заголовков важен. Обычно 
или
проверяются первыми.
"unknown": Некоторые прокси могут устанавливать значение "unknown" в эти заголовки, если IP-адрес клиента неизвестен. Это нужно учитывать. Локальная разработка: При локальной разработке без прокси
обычно возвращает
или 
(IPv6 loopback).
Конфигурация веб-сервера/прокси: Убедитесь, что ваши прокси-серверы и балансировщики нагрузки правильно настроены для передачи IP-адреса клиента в одном из стандартных заголовков. Например, Tomcat имеет
, который может помочь правильно извлекать IP клиента из заголовков.
Вывод:
— это базовый способ. Для более точного определения IPадреса клиента в средах с прокси-серверами необходимо анализировать HTTPзаголовки, такие как
и
, помня о вопросах доверия этим заголовкам.
111. Почему HttpServlet класс объявлен как абстрактный?
Класс
(или
в Jakarta EE) объявлен как абстрактный (abstract) по нескольким ключевым причинам, связанным с его назначением и дизайном Servlet API:
1. Предназначен для наследования и специализации:
предоставляет базовую реализацию для обработки HTTPзапросов. Его основная задача — разобрать HTTP-запрос, определить тип HTTP-метода (GET, POST, PUT, DELETE и т.д.) и вызвать соответствующий метод-обработчик
(например,
,
).
Он сам по себе не предназначен для непосредственного использования
(нельзя создать
, если бы он не был абстрактным, и ожидать от него полезной работы без переопределения методов
). Его
смысл в том, чтобы разработчики наследовали от него и переопределяли методы
,
и т.д., чтобы реализовать специфическую логику обработки для своего приложения.
Объявление класса абстрактным формально подчеркивает, что он является шаблоном или основой для конкретных HTTP-сервлетов.
2.Отсутствие осмысленной реализации для всех
методов по умолчанию:

предоставляет пустые или базовые реализации для большинства методов
. Например:
,
,
,
по умолчанию обычно возвращают клиенту ошибку HTTP
(или 
/
в некоторых старых версиях/ реализациях), если они не переопределены.
обычно вызывает
(но без отправки тела ответа).
и
имеют базовые реализации.
Если бы
не был абстрактным, разработчик мог бы создать его экземпляр, развернуть, и он бы не делал ничего полезного для большинства HTTP-методов, а просто возвращал бы ошибки. Абстрактность заставляет разработчика создать подкласс и реализовать хотя бы один из методов
, чтобы сервлет имел смысл.
3. Принуждение к реализации специфичного для HTTP поведения:
Делая
абстрактным, разработчиков подталкивают к тому, чтобы они думали в терминах HTTP-методов и реализовывали соответствующее поведение. Если бы он был конкретным, можно было бы забыть переопределить нужные методы.
4. Соответствие принципам ООП:
Это хороший пример использования абстрактных классов в объектноориентированном проектировании: предоставление общей структуры и некоторой базовой функциональности (
, который диспетчеризует вызовы), при этом оставляя специфическую реализацию (методы
) для подклассов.
Что было бы, если
не был бы абстрактным?
Теоретически, он мог бы быть конкретным классом, но тогда:
Разработчики могли бы создавать его экземпляры, которые были бы бесполезны без переопределения.
Это не так четко отражало бы его роль как базового класса для специализации. Возможно, потребовались бы другие механизмы, чтобы убедиться, что разработчики предоставляют необходимую логику.
Абстрактность
— это простое и ясное дизайнерское решение, которое направляет разработчиков на правильный путь использования этого класса: наследовать и переопределять методы для обработки конкретных HTTP-запросов.
Он не содержит абстрактных методов, которые обязательно нужно было бы переопределить для компиляции (как в случае с интерфейсами или классами с
методами), но его общая семантика и пустые/ошибочные реализации
делают его непригодным для прямого использования без наследования. Поэтому модификатор
здесь служит для указания на его концептуальную абстрактность и незавершенность как самостоятельной единицы.
112. Какие основные методы присутствуют в классе HttpServlet?
Класс
расширяет
и предоставляет функциональность, специфичную для обработки HTTP-запросов. Вот его основные методы (некоторые унаследованы, некоторые переопределены или добавлены):
Методы жизненного цикла (унаследованы от
и косвенно от
):
1. 
Вызывается контейнером один раз для инициализации сервлета.
переопределяет этот метод, чтобы сохранить
. Если вы переопределяете
, вы должны вызвать
.
2. 
Удобный метод, который можно переопределить для инициализации без необходимости напрямую работать с
(он будет доступен через
).
вызывает этот метод из своего
.
3. 
Вызывается контейнером один раз перед уничтожением сервлета для освобождения ресурсов.
4. 
Возвращает объект
для этого сервлета.
5. 
Возвращает
, к которому принадлежит сервлет.
6. 
Возвращает строку с информацией о сервлете (автор, версия и т.д.). По умолчанию возвращает пустую строку. Можно переопределить.
7. 
Возвращает имя сервлета, как оно определено в дескрипторе развертывания или аннотации.
Метод диспетчеризации запросов:
8. 
Это ключевой метод, который
переопределяет из
.
Он получает HTTP-запрос, определяет тип HTTP-метода (GET, POST, PUT, DELETE и т.д.) и делегирует обработку соответствующему методу
.
Обычно разработчики не переопределяют этот метод напрямую, а переопределяют конкретные
методы. Переопределение
может потребоваться для очень специфической логики предварительной или последующей обработки всех HTTP-запросов до их диспетчеризации по
, но это делается с осторожностью.
9. 
Этот метод унаследован от
. Реализация в
проверяет, являются ли
и
экземплярами
и
соответственно. Если да, он вызывает перегруженный
. В противном
.
Методы для обработки конкретных HTTP-запросов (предназначены для переопределения разработчиком):
Эти методы по умолчанию в
обычно возвращают ошибку HTTP 
(или
/
в зависимости от метода и реализации).
10.
Обрабатывает HTTP GET-запросы. Обычно используется для получения данных.
11.
Обрабатывает HTTP POST-запросы. Обычно используется для отправки данных на сервер с целью их обработки или создания/обновления ресурсов.
12.
Обрабатывает HTTP HEAD-запросы. Поведение по умолчанию — вызывает
и отправляет только заголовки ответа, без тела.
13.
Обрабатывает HTTP PUT-запросы. Обычно используется для создания нового ресурса или полного обновления существующего ресурса по указанному URI.
14.
Обрабатывает HTTP DELETE-запросы. Обычно используется для удаления ресурса по указанному URI.
15.
Обрабатывает HTTP OPTIONS-запросы. Позволяет клиенту определить, какие HTTP-методы поддерживаются для данного ресурса. Реализация по умолчанию обычно вычисляет поддерживаемые методы на основе переопределенных
методов.
16.
Обрабатывает HTTP TRACE-запросы. Возвращает клиенту полученный запрос (используется для диагностики). Реализация по умолчанию обычно это и делает.
Вспомогательный метод:
17. 
Возвращает время последнего изменения ресурса, запрошенного клиентом (в миллисекундах с эпохи). Реализация по умолчанию возвращает -1 (означает, что время неизвестно).
Используется для поддержки условных GET-запросов (с заголовками 
). Если сервлет переопределяет этот метод и
, то базовая реализация
может автоматически обрабатывать условные GET и возвращать
, если ресурс не изменился.
Разработчик сервлета, наследующего
, в основном фокусируется на переопределении одного или нескольких методов
,
,
,
для реализации бизнес-логики, специфичной для этих HTTPметодов. Методы
и
используются для управления ресурсами сервлета.
113. Стоит ли волноваться о многопоточной безопасности работая с сервлетами?
Да, абсолютно стоит (и даже необходимо) волноваться о многопоточной безопасности при работе с сервлетами.
Почему это важно:
По умолчанию контейнер сервлетов придерживается следующей модели для обработки запросов:
1.Один экземпляр сервлета: Для каждого объявления сервлета (в
или через аннотацию
) контейнер обычно создает только один экземпляр этого сервлета. Этот единственный экземпляр используется для обработки всех поступающих запросов, сопоставленных с этим сервлетом.
2.Многопоточная обработка запросов: Контейнер сервлетов использует пул потоков для обработки входящих клиентских запросов. Каждый новый запрос (или запрос от нового клиента, или параллельный запрос от того же клиента) может быть обработан отдельным потоком из этого пула.
3.Параллельный доступ к экземпляру сервлета: Это означает, что несколько
потоков могут одновременно вызывать методы (например,
,
) одного и того же экземпляра сервлета.
Последствия для многопоточной безопасности:
Если ваш сервлет имеет поля экземпляра (instance variables), которые изменяются во время обработки запроса, и к этим полям одновременно обращаются несколько потоков, это может привести к состояниям гонки (race conditions) и другим проблемам многопоточности, таким как:
Повреждение данных: Неконсистентное или неправильное состояние полей экземпляра.
Непредсказуемое поведение: Результаты работы сервлета могут стать непредсказуемыми.
Исключения: Например,
или
, если изменяется коллекция.
Пример небезопасного сервлета:
Если несколько клиентов одновременно обратятся к
, значение
может быть некорректным, так как операция
не является атомарной (она состоит из чтения, инкремента и записи, между которыми может вклиниться другой поток).
Как обеспечить потокобезопасность сервлета:
1.Избегайте использования полей экземпляра для хранения состояния, специфичного для запроса:
Это самый лучший и простой способ.
Всю информацию, специфичную для конкретного запроса, храните в локальных переменных методов (
,
и т.д.) или в объектах
и
. Локальные переменные создаются в стеке каждого потока и поэтому потокобезопасны.
Если нужно передать данные между методами в рамках обработки одного запроса, передавайте их как параметры методов.
2.Синхронизация доступа к разделяемым ресурсам (если поля экземпляра неизбежны):
Если вам абсолютно необходимо использовать поля экземпляра, которые могут изменяться, вы должны синхронизировать доступ к ним.

блоки или методы:
классы: Для простых счетчиков или флагов можно использовать
,
,
и т.д.
Классы из
: Например,
для более гибкого управления блокировками.
Недостаток синхронизации: Может снизить производительность и масштабируемость, так как потоки будут ожидать освобождения блокировки. Старайтесь минимизировать размер критических секций (кода внутри
блоков).
3. Использование потокобезопасных коллекций:
Если поля экземпляра являются коллекциями, используйте потокобезопасные аналоги из пакета
(например,
,
).
4. Модель
(УСТАРЕЛА И НЕ РЕКОМЕНДУЕТСЯ):
Ранее существовал интерфейс
. Если сервлет его реализовывал, контейнер гарантировал, что метод
не будет вызываться одновременно несколькими потоками для одного экземпляра. Это достигалось либо созданием пула экземпляров сервлета, либо синхронизацией доступа к единственному экземпляру.
Этот интерфейс был объявлен устаревшим (deprecated) в Servlet API 2.4 и
не решает всех проблем потокобезопасности (например, с доступом к статическим полям или внешним ресурсам), а также может серьезно снижать производительность. Не используйте
.
Что не требует синхронизации в сервлете (потокобезопасно по своей природе):
Локальные переменные методов: Они создаются в стеке каждого потока. Объекты
и
: Контейнер создает новые экземпляры этих объектов для каждого запроса.
Объекты
и
: Доступ к их методам (например,
,
) обычно потокобезопасен со стороны
контейнера. Однако, если вы изменяете атрибуты
из нескольких потоков, вам может потребоваться синхронизация этих операций.
Чтение
полей экземпляра: Если поля экземпляра инициализированы в конструкторе или
и объявлены как
, их чтение потокобезопасно. Статические поля: Доступ к статическим полям также требует синхронизации, если они изменяются, так как они общие для всех экземпляров сервлета (и, следовательно, для всех потоков).
Вывод:
Да, при разработке сервлетов критически важно учитывать многопоточную безопасность. Лучшая стратегия — проектировать сервлеты без изменяемого состояния на уровне экземпляра. Если это невозможно, необходимо применять адекватные механизмы синхронизации для защиты разделяемых данных.
114. Какой метод HTTP не является неизменяемым?
Термин "неизменяемый" (immutable) в контексте HTTP-методов обычно относится к
идемпотентности и безопасности метода.
Безопасный (Safe) метод: HTTP-метод считается безопасным, если он не изменяет состояние ресурса на сервере. Такие методы предназначены только для получения информации. Примеры:
,
,
,
. Идемпотентный (Idempotent) метод: HTTP-метод считается идемпотентным, если
многократное выполнение одного и того же запроса с этим методом приводит к тому же самому состоянию ресурса на сервере, как и однократное выполнение. То есть, повторные идентичные запросы не должны иметь дополнительных побочных эффектов после первого успешного выполнения.
Все безопасные методы (
,
,
,
) также являются идемпотентными.
является идемпотентным: многократное обновление ресурса с теми же данными приведет к тому же состоянию.
является идемпотентным: многократное удаление одного и того же ресурса приведет к тому, что ресурс будет удален (первый раз) или уже не будет существовать (последующие разы), но состояние сервера (в контексте этого ресурса) не изменится дополнительно.
Метод HTTP, который НЕ является ни безопасным, ни идемпотентным (и, следовательно, "изменяемым" в контексте этого вопроса):
Почему
не является "неизменяемым" (ни безопасным, ни идемпотентным):
1.Небезопасный: Метод
обычно используется для отправки данных на
сервер с целью их обработки, что часто приводит к изменению состояния
