+ [В чем заключаются преимущества технологии сервлетов над CGI (Common Gateway Interface)?](#В-чем-заключаются-преимущества-технологии-сервлетов-над-cgi-common-gateway-interface)
+ [Какова структура веб-проекта?](#Какова-структура-веб-проекта)
+ [Что такое _«контейнерсервлетов»_?](#Что-такое-контейнер-сервлетов)
+ [Как контейнер сервлетов управляет жизненным циклом сервлета, когда и какие методы вызываются?](#Как-контейнер-сервлетов-управляет-жизненным-циклом-сервлета-когда-и-какие-методы-вызываются)
+ [Что такое _«дескрипторразвертывания»_?](#Что-такое-дескриптор-развертывания)
+ [Какие действия необходимо проделать при создании сервлетов?](#Какие-действия-необходимо-проделать-при-создании-сервлетов)
+ [В каком случае требуется переопределять метод `service()`?](#В-каком-случае-требуется-переопределять-метод-service)
+ [Есть ли смысл определять для сервлета конструктор? Каким образом лучше инициализировать данные?](#Есть-ли-смысл-определять-для-сервлета-конструктор-Каким-образом-лучше-инициализировать-данные)
+ [Почему необходимо переопределить только `init()` метод без аргументов?](#Почему-необходимо-переопределить-только-init-метод-без-аргументов)
+ [Какие наиболее распространенные задачи выполняются в контейнере сервлетов?](#Какие-наиболее-распространенные-задачи-выполняются-в-контейнере-сервлетов)
+ [Что вы знаете о _сервлетных фильтрах_?](#Что-вы-знаете-о-сервлетных-фильтрах)
+ [Зачем в сервлетах используются различные _listener_?](#Зачем-в-сервлетах-используются-различные-listener)
+ [Когда стоит использовать фильтры сервлетов, а когда слушателей?](#Когда-стоит-использовать-фильтры-сервлетов-а-когда-слушателей)
+ [Как реализовать запуск сервлета одновременно с запуском приложения?](#Как-реализовать-запуск-сервлета-одновременно-с-запуском-приложения)
+ [Как обработать в приложении исключения, выброшенные другим сервлетом?](#Как-обработать-в-приложении-исключения-выброшенные-другим-сервлетом)
+ [Что представляет собой `ServletConfig`?](#Что-представляет-собой-servletconfig)
+ [Что представляет собой `ServletContext`?](#Что-представляет-собой-servletcontext)
+ [В чем отличия `ServletContext` и `ServletConfig`?](#В-чем-отличия-servletcontext-и-servletconfig)
+ [Для чего нужен интерфейс `ServletResponse`?](#Для-чего-нужен-интерфейс-servletresponse)
+ [Для чего нужен интерфейс `ServletRequest`?](#Для-чего-нужен-интерфейс-servletrequest)
+ [Что такое `Request Dispatcher`?](#Что-такое-request-dispatcher)
+ [Как из одного сервлета вызвать другой сервлет?](#Как-из-одного-сервлета-вызвать-другой-сервлет)
+ [Чем отличается `sendRedirect()` от `forward()`?](#Чем-отличается-sendredirect-от-forward)
+ [Для чего используются атрибуты сервлетов и как происходит работа с ними?](#Для-чего-используются-атрибуты-сервлетов-и-как-происходит-работа-с-ними)
+ [Каким образом можно допустить в сервлете deadlock?](#Каким-образом-можно-допустить-в-сервлете-deadlock)
+ [Как получить реальное расположение сервлета на сервере?](#Как-получить-реальное-расположение-сервлета-на-сервере)
+ [Как получить информацию о сервере из сервлета?](#Как-получить-информацию-о-сервере-из-сервлета)
+ [Как получить IP адрес клиента на сервере?](#Как-получить-ip-адрес-клиента-на-сервере)
+ [Какие классы-обертки для сервлетов вы знаете?](#Какие-классы-обертки-для-сервлетов-вы-знаете)
+ [В чем отличия `GenericServlet` и `HttpServlet`?](#В-чем-отличия-genericservlet-и-httpservlet)
+ [Почему `HttpServlet` класс объявлен как абстрактный?](#Почему-httpservlet-класс-объявлен-как-абстрактный)
+ [Какие основные методы присутствуют в классе `HttpServlet`?](#Какие-основные-методы-присутствуют-в-классе-httpservlet)
+ [Стоит ли волноваться о многопоточной безопасности работая с сервлетами?](#Стоит-ли-волноваться-о-многопоточной-безопасности-работая-с-сервлетами)
+ [Какой метод HTTP не является неизменяемым?](#Какой-метод-http-не-является-неизменяемым)
+ [Какие есть методы отправки данных с клиента на сервер?](#Какие-есть-методы-отправки-данных-с-клиента-на-сервер)
+ [В чем разница между методами `GET` и `POST`?](#В-чем-разница-между-методами-get-и-post)
+ [В чем разница между `PrintWriter` и `ServletOutputStream`?](#В-чем-разница-между-printwriter-и-servletoutputstream)
+ [Можно ли одновременно использовать в сервлете `PrintWriter` и `ServletOutputStream`?](#Можно-ли-одновременно-использовать-в-сервлете-printwriter-и-servletoutputstream)
+ [Расскажите об интерфейсе `SingleThreadModel`.](#Расскажите-об-интерфейсе-singlethreadmodel)
+ [Что означает _URL encoding_? Как это осуществить в Java?](#Что-означает-url-encoding-Как-это-осуществить-в-java)
+ [Какие различные методы управления сессией в сервлетах вы знаете?](#Какие-различные-методы-управления-сессией-в-сервлетах-вы-знаете)
+ [Что такое _cookies_?](#Что-такое-cookies)
+ [Какие методы для работы с cookies предусмотрены в сервлетах?](#Какие-методы-для-работы-с-cookies-предусмотрены-в-сервлетах)
+ [Что такое _URL Rewriting_?](#Что-такое-url-rewriting)
+ [Зачем нужны и чем отличаются методы `encodeURL()` и `encodeRedirectURL()`?](#Зачем-нужны-и-чем-отличаются-методы-encodeurl-и-encoderedirecturl)
+ [Что такое _«сессия»_?](#Что-такое-сессия)
+ [Как уведомить объект в сессии, что сессия недействительна или закончилась?](#Как-уведомить-объект-в-сессии-что-сессия-недействительна-или-закончилась)
+ [Какой существует эффективный способ удостоверится, что все сервлеты доступны только для пользователя с верной сессией?](#Какой-существует-эффективный-способ-удостоверится-что-все-сервлеты-доступны-только-для-пользователя-с-верной-сессией)
+ [Как мы можем обеспечить _transport layer security_ для нашего веб приложения?](#Как-мы-можем-обеспечить-transport-layer-security-для-нашего-веб-приложения)
+ [Как организовать подключение к базе данных, обеспечить журналирование в сервлете?](#Как-организовать-подключение-к-базе-данных-обеспечить-журналирование-в-сервлете)
+ [Какие основные особенности появились в спецификации _Servlet 3_?](#Какие-основные-особенности-появились-в-спецификации-servlet-3)
+ [Что такое _Java Server Pages (JSP)_?](#Что-такое-java-server-pages-jsp)
+ [Зачем нужен JSP?](#Зачем-нужен-jsp)
+ [Опишите, как обрабатываются JSP страницы, начиная от запроса к серверу, заканчивая ответом пользователю.](#Опишите-как-обрабатываются-jsp-страницы-начиная-от-запроса-к-серверу-заканчивая-ответом-пользователю)
+ [Расскажите об этапах (фазах) жизненного цикла JSP.](#Расскажите-об-этапах-фазах-жизненного-цикла-jsp)
+ [Расскажите о методах жизненного цикла JSP.](#Расскажите-о-методах-жизненного-цикла-jsp)
+ [Какие методы жизненного цикла JSP могут быть переопределены?](#Какие-методы-жизненного-цикла-jsp-могут-быть-переопределены)
+ [Как можно предотвратить прямой доступ к JSP странице из браузера?](#Как-можно-предотвратить-прямой-доступ-к-jsp-странице-из-браузера)
+ [Какая разница между _динамическим_ и _статическим_ содержимым JSP?](#Какая-разница-между-динамическим-и-статическим-содержимым-jsp)
+ [Как закомментировать код в JSP?](#Как-закомментировать-код-в-jsp)
+ [Какие существуют основные типы тегов JSP?](#Какие-существуют-основные-типы-тегов-jsp)
+ [Что вы знаете о действиях JSP (_Action tag_ и _JSP Action Elements_).](#Что-вы-знаете-о-действиях-jsp-action-tag-и-jsp-action-elements)
+ [Какие области видимости переменных существуют в JSP?](#Какие-области-видимости-переменных-существуют-в-jsp)
+ [Какие неявные, внутренние объекты и методы есть на JSP странице?](#Какие-неявные-внутренние-объекты-и-методы-есть-на-jsp-странице)
+ [Какие неявные объекты не доступны в обычной JSP странице?](#Какие-неявные-объекты-не-доступны-в-обычной-jsp-странице)
+ [Что вы знаете о `PageContext` и какие преимущества его использования?](#Что-вы-знаете-о-pagecontext-и-какие-преимущества-его-использования)
+ [Как сконфигурировать параметры инициализации для JSP?](#Как-сконфигурировать-параметры-инициализации-для-jsp)
+ [Почему не рекомендуется использовать скриплеты (скриптовые элементы) в JSP?](#Почему-не-рекомендуется-использовать-скриплеты-скриптовые-элементы-в-jsp)
+ [Можно ли определить класс внутри JSP страницы?](#Можно-ли-определить-класс-внутри-jsp-страницы)
+ [Что вы знаете о Языке выражений JSP (JSP Expression Language – EL)?](#Что-вы-знаете-о-Языке-выражений-jsp-jsp-expression-language--el)
+ [Какие типы EL операторов вы знаете?](#Какие-типы-el-операторов-вы-знаете)
+ [Назовите неявные, внутренние объекты JSP EL и их отличия от объектов JSP.](#Назовите-неявные-внутренние-объекты-jsp-el-и-их-отличия-от-объектов-jsp)
+ [Как отключить возможность использования EL в JSP?](#Как-отключить-возможность-использования-el-в-jsp)
+ [Как узнать тип HTTP метода используя JSP EL?](#Как-узнать-тип-http-метода-используя-jsp-el)
+ [Что такое _JSTL (JSP Standard tag library)_?](#Что-такое-jstl-jsp-standard-tag-library)
+ [Из каких групп тегов состоит библиотека _JSTL_?](#Из-каких-групп-тегов-состоит-библиотека-jstl)
+ [Какая разница между `<c:set>` и `<jsp:useBean>`?](#Какая-разница-между-cset-и-jspusebean)
+ [Чем отличается `<c:import>` от `<jsp:include>` и директивы `<%@include %>`?](#Чем-отличается-cimport-от-jspinclude-и-директивы-include-)
+ [Как можно расширить функциональность JSP?](#Как-можно-расширить-функциональность-jsp)
+ [Что вы знаете о написании пользовательских JSP тегов?](#Что-вы-знаете-о-написании-пользовательских-jsp-тегов)
+ [Приведите пример использования собственных тегов.](#Приведите-пример-использования-собственных-тегов)
+ [Как сделать перенос строки в HTML средствами JSP?](#Как-сделать-перенос-строки-в-html-средствами-jsp)
+ [Почему не нужно конфигурировать стандартные JSP теги в `web.xml`?](#Почему-не-нужно-конфигурировать-стандартные-jsp-теги-в-webxml)
+ [Как можно обработать ошибки JSP страниц?](#Как-можно-обработать-ошибки-jsp-страниц)
+ [Как происходит обработка ошибок с помощью JSTL?](#Как-происходит-обработка-ошибок-с-помощью-jstl)
+ [Как конфигурируется JSP в дескрипторе развертывания.](#Как-конфигурируется-jsp-в-дескрипторе-развертывания)
+ [Можно ли использовать Javascript на JSP странице?](#Можно-ли-использовать-javascript-на-jsp-странице)
+ [Всегда ли создается объект сессии на JSP странице, можно ли отключить его создание?](#Всегда-ли-создается-объект-сессии-на-jsp-странице-можно-ли-отключить-его-создание)
+ [Какая разница между `JSPWriter` и сервлетным `PrintWriter`?](#Какая-разница-между-jspwriter-и-сервлетным-printwriter)
+ [Опишите общие практические принципы работы с JSP.](#Опишите-общие-практические-принципы-работы-с-jsp)
__Сервлет__ является интерфейсом, реализация которого расширяет функциональные возможности сервера. Сервлет взаимодействует с клиентами посредством принципа запрос-ответ. Хотя сервлеты могут обслуживать любые запросы, они обычно используются для расширения веб-серверов.
Большинство необходимых для создания сервлетов классов и интерфейсов содержатся в пакетах `javax.servlet` и `javax.servlet.http`.
Основные методы сервлета:
+ `public void init(ServletConfig config) throws ServletException` запускается сразу после загрузки сервлета в память;
+ `public ServletConfig getServletConfig()` возвращает ссылку на объект, который предоставляет доступ к информации о конфигурации сервлета;
+ `public String getServletInfo()` возвращает строку, содержащую информацию о сервлете, например: автор и версия сервлета;
+ `public void service(ServletRequest request, ServletResponse response) throws ServletException, java.io.IOException` вызывается для обработки каждого запроса;
+ `public void destroy()` выполняется перед выгрузкой сервлета из памяти.
Текущая спецификация - Servlet 3.1 описана в JSR-340 и принята в 2013 году.
+ Сервлеты предоставляют лучшую производительность обработки запросов и более эффективное использование памяти за счет использования преимущество многопоточности (на каждый запрос создается новая нить, что быстрее выделения памяти под новый объект для каждого запроса, как это происходит в CGI).
+ Сервлеты, как платформа и система являются независимыми. Таким образом веб-приложение написанное с использованием сервлетов может быть запущена в любом контейнере сервлетов, реализующим этот стандарт и в любой операционной системе.
+ Использование сервлетов повышает надежность программы, т.к. контейнер сервлетов самостоятельно заботится о жизненном цикле сервлетов (а значит и за утечками памяти), безопасности и сборщике мусора.
+ Сервлеты относительно легки в изучении и поддержке, таким образом разработчику необходимо заботиться только о бизнес-логике приложения, а не внутренней реализации веб-технологий.
__Контейнерсервлетов__ — программа, представляющая собой сервер, который занимается системной поддержкой сервлетов и обеспечивает их жизненный цикл в соответствии с правилами, определёнными в спецификациях. Может работать как полноценный самостоятельный веб-сервер, быть поставщиком страниц для другого веб-сервера, или интегрироваться в Java EE сервер приложений.
Контейнер сервлетов обеспечивает обмен данными между сервлетом и клиентами, берёт на себя выполнение таких функций, как создание программной среды для функционирующего сервлета, идентификацию и авторизацию клиентов, организацию сессии для каждого из них.
Наиболее известные реализации контейнеров сервлетов:
## Зачем нужны сервера приложений, если есть контейнеры сервлетов?
+ __Пулы соединений с БД__
+ Возможность периодического тестирования доступности СУБД и обновления соединения в случае восстановления после сбоев
+ Замена прав доступа при подключении
+ Балансировка нагрузки между несколькими СУБД, определение доступность или недоступность того или иного узла
+ Защита пула соединений от некорректного кода в приложении, которое по недосмотру не возвращает соединения, просто отбирая его после какого-то таймаута.
+ Возможность кластеризации очередей, т.е. доступность построения распределенных очередей, расположенных сразу на нескольких серверах, что существенно увеличивает масштабируемость и доступность приложения
+ Возможность миграции очередей - в случае падения одного из серверов, его очереди автоматически перемещаются на другой, сохраняя необработанные сообщения.
+ В некоторых серверах приложений поддерживается _Unit-of-Order_ - гарантированный порядок обработки сообщений, удовлетворяющих некоторым критериям.
+ __JTA__ Встроенная поддержка распределенных транзакций для обеспечения согласованности данных в разные СУБД или очереди.
+ __Безопасность__
+ Наличие множества провайдеров безопасности и аутентификации:
+ во встроенном или внешнем _LDAP-сервере_
+ в базе данных
+ в различных _Internet-directory_ (специализированных приложениях для управления правами доступа)
+ Доступность _Single-Sign-On_ (возможности разделения пользовательской сессии между приложениями) посредством _Security Assertion Markup Language (SAML) 1/2_ или _Simple and Protected Negotiate (SPNEGO)_ и _Kerberos_: один из серверов выступает в роли базы для хранения пользователей, все другие сервера при аутентификации пользователя обращаются к этой базе.
+ Возможность авторизации посредством протокола _eXtensible Access Control Markup Language (XACML)_, позволяющего описывать довольно сложные политики (например, приложение доступно пользователю только в рабочее время).
+ Кластеризация всего вышеперечисленного
+ __Масштабируемость и высокая доступность__ Для контейнера сервлетов обычно так же возможно настроить кластеризацию, но она будет довольно примитивной, так как в случае его использования имееются следующие ограничения:
+ Сложность передачи пользовательской сессии из одного _центра обработки данных (ЦоД)_ в другой через Интернет
+ Отсутствие возможности эффективно настроить репликации сессий на большом (состоящем из 40-50 экземпляров серверов) кластере
+ Невозможность обеспечения миграции экземпляров приложения на другой сервер
+ Недоступность механизмов автоматического мониторинга и реакции на ошибки
+ __Управляемость__
+ Присутствие единого центра управления, т.н. _AdminServer_ и аналога _NodeManager_’а, обеспечивающего
+ Возможность одновременного запуска нескольких экземпляров сервера
+ Просмотр состояния запущенных экземпляров сервера, обработчиков той или иной очереди, на том или ином сервере, количества соединений с той или иной БД
+ __Административный канал и развертывание в промышленном режиме__ Некоторые сервера приложений позволяют включить так называемый "административный канал" - отдельный порт, запросы по которому имеют приоритет.
+ Просмотр состояния (выполняющихся транзакций, потоков, очередей) в случае недоступности ("зависания") сервера
+ Обновление приложений "на-лету", без простоя:
+ добавление на сервер новой версии приложения в "закрытом" режиме, пока пользователи продолжают работать со предыдущей
+ тестирование корректности развертывания новой версии
+ "скрытый" перевод на использование новой версии всех пользователей
Контейнер сервлетов управляет четырьмя фазами жизненного цикла сервлета:
+ Загрузка класса сервлета — когда контейнер получает запрос для сервлета, то происходит загрузка класса сервлета в память и вызов его конструктора без параметров.
+ Инициализация класса сервлета — после того как класс загружен контейнер инициализирует объект `ServletConfig` для этого сервлета и внедряет его через `init()` метод. Это и есть место где сервлет класс преобразуется из обычного класса в сервлет.
+ Обработка запросов — после инициализации сервлет готов к обработке запросов. Для каждого запроса клиента сервлет контейнер порождает новый поток и вызывает метод `service()` путем передачи ссылки на объекты ответа и запроса.
+ Удаление - когда контейнер останавливается или останавливается приложение, то контейнер сервлетов уничтожает классы сервлетов путем вызова `destroy()` метода.
Таким образом, сервлет создаётся при первом обращении к нему и живёт на протяжении всего времени работы приложения (в отличии от объектов классов, которые уничтожаются сборщиком мусора после того как они уже не используются) и весь жизненный цикл сервлета можно описать как последовательность вызова методов:
+ `public void init(ServletConfig config)`– используется контейнером для инициализации сервлета. Вызывается один раз за время жизни сервлета.
+ `public void service(ServletRequest request, ServletResponse response)`– вызывается для каждого запроса. Метод не может быть вызван раньше выполнения `init()` метода.
+ `public void destroy()`– вызывается для уничтожения сервлета (один раз за время жизни сервлета).
Дескриптор развертывания — это конфигурационный файл артефакта, который будет развернут в контейнере сервлетов. В спецификации Java Platform, Enterprise Edition дескриптор развертывания описывает то, как компонент, модуль или приложение (такое, как веб-приложение или приложение предприятия) должно быть развернуто.
Этот конфигурационный файл указывает параметры развертывания для модуля или приложения с определенными настройками, параметры безопасности и описывает конкретные требования к конфигурации. Для синтаксиса файлов дескриптора развертывания используется язык XML.
Для веб-приложений дескриптор развертывания должен называться `web.xml` и находиться в директории `WEB-INF`, в корне веб-приложения. Этот файл является стандартным дескриптором развертывания, определенным в спецификации. Также есть и другие типы дескрипторов, такие, как файл дескриптора развертывания `sun-web.xml`, содержащий специфичные для _Sun GlassFish Enterprise Server_ данные для развертывания именно для этого сервера приложений или файл `application.xml` в директории `META-INF` для приложений _J2EE_.
Затем создать класс `xyz.company.ExampleServlet` путём наследования от `HttpServlet` и реализовать логику его работы в методе `service()` или методах `doGet()`/`doPost()`.
Метод `service()` переопределяется, когда необходимо, чтобы сервлет обрабатывал все запросы (и `GET`, и `POST`) в одном методе.
Когда контейнер сервлетов получает запрос клиента, то происходит вызов метода `service()`, который в зависимости от поступившего запроса вызывает или метод `doGet()` или метод `doPost()`.
Большого смысла определять для сервлета конструктор нет, т.к. инициализировать данные лучше не в конструкторе, а переопределив метод `init()`, в котором имеется возможность доступа к параметрам инициализации сервлета через использование объекта `ServletConfig`.
Метод `init()` переопределяется, если необходимо инициализировать какие-то данные до того как сервлет начнет обрабатывать запросы.
При переопределении метода `init(ServletConfig config)`, первым должен быть вызван метод `super(config)`, который обеспечит вызов метода `init(ServletConfig config)` суперкласса. `GenericServlet` предоставляет другой метод `init()` без параметров, который будет вызываться в конце метода `init(ServletConfig config)`.
Необходимо использовать переопределенный метод `init()` без параметров для инициализации данных во избежание каких-либо проблем, например ошибку, когда вызов `super()` не указан в переопределенном `init(ServletConfig config)`.
+ Поддержка обмена данными. Контейнер сервлетов предоставляет легкий способ обмена данными между веб клиентом (браузером) и сервлетом. Благодаря контейнеру нет необходимости создавать слушателя сокета на сервере для отслеживания запросов от клиента, а так же разбирать запрос и генерировать ответ. Все эти важные и комплексные задачи решаются с помощью контейнера и разработчик может сосредоточиться на бизнес логике приложения.
+ Управление жизненным циклом сервлетов и ресурсов. Начиная от загрузки сервлета в память, инициализации, внедрения методов и заканчивая уничтожением сервлета. Контейнер так же предоставляет дополнительные утилиты, например JNDI, для управления пулом ресурсов.
+ Поддержка многопоточности. Контейнер самостоятельно создает новую нить для каждого запроса и предоставляет ей запрос и ответ для обработки. Таким образом сервлет не инициализируется заново для каждого запроса и тем самым сохраняет память и уменьшает время до обработки запроса.
+ Поддержка JSP. JSP классы не похожи на стандартные классы джавы, но контейнер сервлетов преобразует каждую JSP в сервлет и далее управляется контейнером как обычным сервлетом.
+ Различные задачи. Контейнер сервлетов управляет пулом ресурсов, памятью приложения, сборщиком мусора. Предоставляются возможности настройки безопасности и многое другое.
__Сервлетный фильтр__ - это Java-код, пригодный для повторного использования и позволяющий преобразовать содержание HTTP-запросов, HTTP-ответов и информацию, содержащуюся в заголовках HTML. Сервлетный фильтр занимается предварительной обработкой запроса, прежде чем тот попадает в сервлет, и/или последующей обработкой ответа, исходящего из сервлета.
Сервлетные фильтры могут:
+ перехватывать инициацию сервлета прежде, чем сервлет будет инициирован;
+ определить содержание запроса прежде, чем сервлет будет инициирован;
+ модифицировать заголовки и данные запроса, в которые упаковывается поступающий запрос;
+ модифицировать заголовки и данные ответа, в которые упаковывается получаемый ответ;
+ перехватывать инициацию сервлета после обращения к сервлету.
Сервлетный фильтр может быть конфигурирован так, что он будет работать с одним сервлетом или группой сервлетов. Основой для формирования фильтров служит интерфейс `javax.servlet.Filter`, который реализует три метода:
Метод `init()` вызывается прежде, чем фильтр начинает работать,и настраивает конфигурационный объект фильтра. Метод `doFilter()` выполняет непосредственно работу фильтра. Таким образом, сервер вызывает `init()` один раз, чтобы запустить фильтр в работу, а затем вызывает `doFilter()` столько раз, сколько запросов будет сделано непосредственно к данному фильтру. После того, как фильтр заканчивает свою работу, вызывается метод `destroy()`.
Интерфейс `FilterConfig` содержит метод для получения имени фильтра, его параметров инициации и контекста активного в данный момент сервлета. С помощью своего метода `doFilter()` каждый фильтр получает текущий запрос `request` и ответ `response`, а также `FilterChain`, содержащий список фильтров, предназначенных для обработки. В`doFilter()` фильтр может делать с запросом и ответом всё, что ему захочется - собирать данные или упаковывать объекты для придания им нового поведения. Затем фильтр вызывает `chain.doFilter()`, чтобы передать управление следующему фильтру. После возвращения этого вызова фильтр может по окончании работы своего метода `doFilter()` выполнить дополнительную работу над полученным ответом. К примеру, сохранить регистрационную информацию об этом ответе.
После того, как класс-фильтр откомпилирован, его необходимо установить в контейнер и _«приписать» (map)_ к одному или нескольким сервлетам. Объявление и подключение фильтра отмечается в дескрипторе развёртывания `web.xml` внутри элементов `<filter>` и `<filter-mapping>`. Для подключение фильтра к сервлету необходимо использовать вложенные элементы `<filter-name>` и `<servlet-name>`.
> Объявление класс-фильтра `FilterConnect` с именем `FilterName`:
```xml
<filter>
<filter-name>FilterName</filter-name>
<filter-class>FilterConnect</filter-class>
<init-param>
<!--- фильтр имеет параметр инициализации `active`, которому присваивается значение `true`. -->
<param-name>active</param-name>
<param-value>true</param-true>
</init-param>
</filter>
```
> Подключение фильтра `FilterName` к сервлету `ServletName`:
```xml
<filter-mapping>
<filter-name>FilterName</filter-name>
<servlet-name>ServletName</servlet-name>
</filter-mapping>
```
Для связи фильтра со страницами HTML или группой сервлетов необходимо использовать тег `<url-pattern>`:
> Подключение фильтра `FilterName` ко всем вызовам .html страниц
```xml
<filter-mapping>
<filter-name>FilterName</filter-name>
<url-pattern>*.html</url-pattern>
</filter-mapping>
```
Порядок, в котором контейнер строит цепочку фильтров для запроса определяется следующими правилами:
+ цепочка, определяемая `<url-pattern>`, выстраивается в том порядке, в котором встречаются соответствующие описания фильтров в `web.xml`;
+ последовательность сервлетов, определенных с помощью `<servlet-name>`, также выполняется в той последовательности, в какой эти элементы встречаются в дескрипторе развёртывания `web.xml`.
__Listener (слушатель)__ работает как триггер, выполняя определённые действия при наступлении какого-либо события в жизненном цикле сервлета.
Слушатели, разделённые по области видимости (scope):
+ _Request_:
+ `ServletRequestListener` используется для того, чтобы поймать момент создания и уничтожения запроса;
+ `ServletRequestAttributeListener` используется для прослушивании событий происходящих с атрибутами запроса.
+ _Context_:
+ `ServletContextListener` позволяет поймать момент, когда контекст инициализируется либо уничтожается;
+ `ServletContextAttributeListener` используется для прослушивании событий происходящих с атрибутами в контексте.
+ _Session_:
+ `HttpSessionListener` позволяет поймать момент создания и уничтожения сессии;
+ `HttpSessionAttributeListener` используется при прослушивании событий происходящих с атрибутами в сессии;
+ `HttpSessionActivationListener` используется в случае, если происходит миграция сессии между различными JVM в распределённых приложениях;
+ `HttpSessionBindingListener` так же используется для прослушивания событий происходящих с атрибутами в сессии. Разница между `HttpSessionAttributeListener` и `HttpSessionBindingListener` слушателями: первый декларируется в `web.xml`; экземпляр класса создается контейнером автоматически в единственном числе и применяется ко всем сессиям; второй: экземпляр класса должен быть создан и закреплён за определённой сессией «вручную», количество экземпляров также регулируется самостоятельно.
Следует использовать фильтры, если необходимо обрабатывать входящие или исходящие данные (например: для аутентификации, преобразования формата, компрессии, шифрования и т.д.), в случае, когда необходимо реагировать на события - лучше применять слушателей.
Контейнер сервлетов обычно загружает сервлет по первому запросу клиента.
Если необходимо загрузить сервлет прямо на старте приложения (например если загрузка сервлета происходит длительное время) следует использовать элемент `<load-on-startup>` в дескрипторе или аннотацию `@loadOnStartup` в коде сервлета, что будет указывать на необходимость загрузки сервлета при запуске.
Если целочисленное значение этого параметра отрицательно, то сервлет будет загружен при запросе клиента. В противном случае - загрузится на старте приложения, при этом, чем число меньше, тем раньше в очереди на загрузку он окажется.
Когда приложение выбрасывет исключение контейнер сервлетов обрабатывает его и создаёт ответ в формате HTML. Это аналогично тому что происходит при кодах ошибок вроде 404, 403 и т.д.
В дополнении к этому существует возможность написания собственных сервлетов для обработки исключений и ошибок с указанием их в дескрипторе развертывания:
Основная задача таких сервлетов - обработать ошибку/исключение и сформировать понятный ответ пользователю. Например, предоставить ссылку на главную страницу или же описание ошибки.
Интерфейс `javax.servlet.ServletConfig` используется для передачи сервлету конфигурационной информации. Каждый сервлет имеет свой собственный экземпляр объекта `ServletConfig`, создаваемый контейнером сервлетов.
Для установки параметров конфигурации используются параметры `init-param` в `web.xml`:
Уникальный (в рамках веб-приложения) объект `ServletContext` реализует интерфейс `javax.servlet.ServletContext` и предоставляет сервлетам доступ к параметрам этого веб-приложения. Для предоставления доступа используется элемент `<context-param>` в `web.xml`:
```xml
<web-app>
...
<context-param>
<param-name>exampleParameter</param-name>
<param-value>parameterValue</param-value>
</context-param>
...
</web-app>
```
Объект `ServletContext` можно получить с помощью метода `getServletContext()`у интерфейса `ServletConfig`. Контейнеры сервлетов так же могут предоставлять контекстные объекты, уникальные для группы сервлетов. Каждая из групп будет связана со своим набором URL-путей хоста. В спецификации Servlet 3 `ServletContext` был расширен и теперь предоставляет возможности программного добавления слушателей и фильтров в приложение. Так же у этого интерфейса имеется множество полезных методов таких как `getServerInfo()`, `getMimeType()`, `getResourceAsStream()` и т.д.
+ `ServletConfig` уникален для сервлета, а`ServletContext` - для приложения;
+ `ServletConfig` используется для предоставления параметров инициализации конкретному сервлету, а`ServletContext` для предоставления параметров инициализации для всех сервлетов приложения;
+ для `ServletConfig` возможности модифицировать атрибуты отсутствуют, атрибуты в объекте `ServletContext` можно изменять.
Интерфейс `RequestDispatcher` используется для передачи запроса другому ресурсу, при этом существует возможность добавления данных полученных из этого ресурса к собственному ответу сервлета. Так же этот интерфейс используется для внутренней коммуникации между сервлетами в одном контексте.
+ `void forward(ServletRequest var1, ServletResponse var2)` — передает запрос из сервлета к другому ресурсу (сервлету, JSP или HTML файлу) на сервере.
+ `void include(ServletRequest var1, ServletResponse var2)` — включает контент ресурса (сервлет, JSP или HTML страница) в ответ.
Доступ к интерфейсу можно получить с помощью метода интерфейса `ServletContext` - `RequestDispatcher getRequestDispatcher(String path)`, где путь начинающийся с`/`, интерпретируется относительно текущего корневого пути контекста.
Для вызова сервлета из того же приложения необходимо использовать механизм внутренней коммуникации сервлетов (_inter-servlet communication mechanisms_) через вызовы методов `RequestDispatcher`:
+ `forward()` - передаёт выполнение запроса в другой сервлет;
+ `include()` - предоставляет возможность включить результат работы другого сервлета в возвращаемый ответ.
Если необходимо вызывать сервлет принадлежащий другому приложению, то использовать `RequestDispatcher` уже не получится, т.к. он определен только для текущего приложения. Для подобных целей необходимо использовать метод `ServletResponse` - `sendRedirect()` которому предоставляется полный URL другого сервлета. Для передачи данных между сервлетами можно использовать `cookies`.
Атрибуты сервлетов используются для внутренней коммуникации сервлетов.
В веб-приложении существует возможность работы с атрибутами используя методы `setAttribute()`, `getAttribute()`, `removeAttribute()`, `getAttributeNames()`, которые предоставлены интерфейсами `ServletRequest`, `HttpSession` и `ServletContext` (для областей видимости _request_, _session_, _context_ соответственно).
Собственные обработчики `ServletRequest` и `ServletResponse` можно реализовать добавив новые или переопределив существующие методы у классов-обёрток `ServletRequestWrapper` (`HttpServletRequestWrapper`) и `ServletResponseWrapper` (`HttpServletRequestWrapper`).
Абстрактный класс `GenericServlet` — независимая от используемого протокола реализация интерфейса `Servlet`, а абстрактный класс `HttpServlet` в свою очередь расширяет `GenericServlet` для протокола HTTP..
Класс `HTTPServlet` предоставляет лишь общую реализацию сервлета для HTTP протокола. Реализация ключевых методов `doGet()` и `doPost()`, содержащих основную бизнес-логику перекладывается на разработчика и по умолчанию возвращает `HTTP 405 Method Not Implemented error`.
Методы `doGet()`, `doPost()`, `service()` вызываются на каждый запрос клиента и т.к. сервлеты используют многопоточность, то здесь задумываться о потокобезопасной работе обязательно. При этом правила использования многопоточности остаются теми же: локальные переменные этих методов будут созданы отдельно для каждого потока, а при использовании глобальных разделяемых ресурсов необходимо использовать синхронизацию или другие приёмы многопоточного программирования.
HTTP метод называется неизменяемым, если он на один и тот же запрос всегда возвращает одинаковый результат. HTTP методы `GET`, `PUT`, `DELETE`, `HEAD` и `OPTIONS` являются неизменяемыми, поэтому необходимо реализовывать приложение так, чтобы эти методы возвращали одинаковый результат постоянно. К изменяемым методам относится метод `POST`, который и используется для реализации чего-либо, что изменяется при каждом запросе.
К примеру, для доступа к статической HTML странице используется метод `GET`, т.к. он всегда возвращает одинаковый результат. При необходимости сохранять какую-либо информацию, например в базе данных, нужно использовать `POST` метод.
+ `GET` - используется для запроса содержимого указанного ресурса, изображения или гипертекстового документа. Вместе с запросом могут передаваться дополнительные параметры как часть URI, значения могут выбираться из полей формы или передаваться непосредственно через URL. При этом запросы кэшируются и имеют ограничения на размер. Этот метод является основным методом взаимодействия браузера клиента и веб-сервера.
+ `POST` - используется для передачи пользовательских данных в содержимом HTTP-запроса на сервер. Пользовательские данные упакованы в тело запроса согласно полю заголовка Content-Type и/или включены в URI запроса. При использовании метода POST под URI подразумевается ресурс, который будет обрабатывать запрос.
+ `GET` передает данные серверу используя URL, тогда как `POST` передает данные, используя тело HTTP запроса. Длина URL ограничена 1024 символами, это и будет верхним ограничением для данных, которые можно отослать через `GET`. `POST` может отправлять гораздо большие объемы данных. Лимит устанавливается web-server и составляет обычно около 2 Mb.
+ Передача данных методом `POST` более безопасна, чем методом `GET`, так как секретные данные (например пароль) не отображаются напрямую в web-клиенте пользователя, в отличии от URL, который виден почти всегда. Иногда это преимущество превращается в недостаток - вы не сможете послать данные за кого-то другого.
Так сделать не получится, т.к. при попытке одновременного вызова `getWriter()` и `getOutputStream()` будет выброшено исключение `java.lang.IllegalStateException`с сообщением, что уже был вызван другой метод.
Интерфейс `SingleThreadModel` является маркерным - в нем не объявлен ни один метод, однако, если сервлет реализует этот интерфейс, то метод `service()` этого сервлета гарантированно не будет одновременно выполняться в двух потоках. Контейнер сервлетов либо синхронизирует обращения к единственному экземпляру, либо обеспечивает поддержку пула экземпляров и перенаправление запроса свободному сервлету.
Другими словами, контейнер гарантирует отсутствие конфликтов при одновременном обращении к переменным или методам экземпляра сервлета. Однако существуют также и другие разделяемые ресурсы, которые даже при использовании этого интерфейса, остаются всё так же доступны обработчикам запросов в других потоках. Т.о. пользы от использования этого интерфейса немного и в спецификации Servlet 2.4 он был объявлен `deprecated`.
__URL Encoding__ — процесс преобразования данных в форму CGI (Common Gateway Interface), не содержащую пробелов и нестандартных символов, которые заменяются в процессе кодирования на специальные escape-символы. В Java для кодирования строки используется метод `java.net.URLEncoder.encode(String str, String unicode)`. Обратная операция декодирования возможна через использование метода `java.net.URLDecoder.decode(String str, String unicode)`.
> `Hello мир!` преобразовывается в `Hello%20%D0%BC%D0%B8%D1%80!`.
При посещении клиентом Web-ресурса и выполнении вариантов запросов, контекстная информация о клиенте не хранится. В протоколе HTTP нет возможностей для сохранения и изменения информации о предыдущих посещениях клиента. Сеанс (сессия) – соединение между клиентом и сервером, устанавливаемое на определенное время, за которое клиент может отправить на сервер сколько угодно запросов. Сеанс устанавливается непосредственно между клиентом и Web-сервером. Каждый клиент устанавливает с сервером свой собственный сеанс. Сеансы используются для обеспечения хранения данных во время нескольких запросов Web-страницы или на обработку информации, введенной в пользовательскую форму в результате нескольких HTTP-соединений (например, клиент совершает несколько покупок в интернет-магазине; студент отвечает на несколько тестов в системе дистанционного обучения).
Существует несколько способов обеспечения уникального идентификатора сессии:
+ __User Authentication__– Предоставление учетных данных самим пользователем в момент аутентификации. Переданная таким образом информация в дальнейшем используется для поддержания сеанса. Это метод не будет работать, если пользователь вошёл в систему одновременно из нескольких мест.
+ __HTML Hidden Field__– Присвоение уникального значения скрытому полю HTML страницы, в момент когда пользователь начинает сеанс. Этот метод не может быть использован со ссылками, потому что нуждается в подтверждении формы со скрытым полем каждый раз во время формирования запроса. Кроме того, это не безопасно, т.к. существует возможность простой подмены такого идентификатора.
+ __URL Rewriting__– Добавление идентификатора сеанса как параметра URL. Достаточно утомительная операция, потому что требует постоянного отслеживания этого идентификатора при каждом запросе или ответе.
+ __Cookies__– Использование небольших фрагментов данных, отправленных web-сервером и хранимых на устройстве пользователя. Данный метод не будет работать, если клиент отключает использование cookies.
+ __Session Management API__– Использование специального API для отслеживания сеанса, построенный на основе и на методах описанных выше и который решает частные проблемы перечисленных способов:
+ Чаще всего недостаточно просто отслеживать сессию, необходимо ещё и сохранять какие-либо дополнительные данные о ней, которые могут потребоваться при обработке последующих запросов. Осуществление такого поведения требует много дополнительных усилий.
+ Все вышеперечисленные методы не являются универсальными: для каждого из них можно подобрать конкретный сценарий, при котором они не будут работать.
__Сookies («куки»)__ — небольшой фрагмент данных, отправленный web-сервером и хранимый на устройстве пользователя. Всякий раз при попытке открыть страницу сайта, web-клиент пересылает соответствующие этому сайту cookies web-серверу в составе HTTP-запроса. Применяется для сохранения данных на стороне пользователя и на практике обычно используется для:
+ аутентификации пользователя;
+ хранения персональных предпочтений и настроек пользователя;
+ отслеживания состояния сеанса доступа пользователя;
Servlet API предоставляет поддержку cookies через класс `javax.servlet.http.Cookie`:
+ Для получения массива cookies из запроса необходимо воспользоваться методом `HttpServletRequest.getCookies()`. Методов для добавления cookies в `HttpServletRequest` не предусмотрено.
+ Для добавления cookie в ответ используется `HttpServletResponse.addCookie(Cookie c)`. Метода получения cookies в `HttpServletResponse` отсутствует.
__URL Rewriting__ - специальная перезапись (перекодирование) оригинального URL. Данный механизм может использоваться для управления сессией в сервлетах, когда _cookies_ отключены.
`HttpServletResponse.encodeURL()` предоставляет способ преобразования URL в HTML гиперссылку с преобразованием спецсимволов и пробелов, а так же добавления _session id_ к URL. Такое поведение аналогично `java.net.URLEncoder.encode()`, но с добавлением дополнительного параметра `jsessionid` в конец URL.
Метод `HttpServletResponse.encodeRedirectURL()` преобразует URL для последующего использования в методе `sendRedirect()`.
Таким образом для HTML гиперссылок при _URL rewriting_ необходимо использовать `encodeURL()`, а для URL при перенаправлении - `encodeRedirectUrl()`.
__Сессия__ - это сеанс связи между клиентом и сервером, устанавливаемый на определенное время. Сеанс устанавливается непосредственно между клиентом и веб-сервером в момент получения первого запроса к веб-приложению. Каждый клиент устанавливает с сервером свой собственный сеанс, который сохраняется до окончания работы с приложением.
Чтобы быть уверенным в том, что объект будет оповещён о прекращении сессии, нужно реализовать интерфейс `javax.servlet.http.HttpSessionBindingListener`. Два метода этого интерфейса: `valueBound()` и `valueUnbound()` используются при добавлении объекта в качестве атрибута к сессии и при уничтожении сессии соответственно.
Сервлет фильтры используются для перехвата всех запросов между контейнером сервлетов и сервлетом. Поэтому логично использовать соответствующий фильтр для проверки необходимой информации (например валидности сессии) в запросе.
Для обеспечения _transport layer security_ необходимо настроить поддержку SSL сервлет контейнера. Как это сделать зависит от конкретной реализации сервлет-контейнера.
При работе с большим количеством подключений к базе данных рекомендуется инициализировать их в _servlet context listener_, а также установить в качестве атрибута контекста для возможности использования другими сервлетами.
Журналирование подключается к сервлету стандартным для логгера способом (например для _log4j_ это может быть property-файл или XML-конфигурация) , а далее эта информация используется при настройке соответствующего _context listener_.
+ __Servlet Annotations__. До Servlet 3 вся конфигурация содержалась в `web.xml`, что приводило к ошибкам и неудобству при работе с большим количестве сервлетов. Примеры аннотаций: `@WebServlet`, `@WebInitParam`, `@WebFilter`, `@WebListener`.
+ __Web Fragments__. Одностраничное веб приложение может содержать множество модулей: все модули прописываются в `fragment.xml` в папке `META-INF\`. Это позволяет разделять веб приложение на отдельные модули, собранные как .jar-файлы в отдельной `lib\` директории.
+ __Динамическое добавление веб компонентов__. Появилась возможность программно добавлять фильтры и слушатели, используя `ServletContext` объект. Для этого применяются методы `addServlet()`, `addFilter()`, `addListener()`. Используя это нововведение стало доступным построение динамической системы, в которой необходимый объект будет создан и вызван только по необходимости.
+ __Асинхронное выполнение__. Поддержка асинхронной обработки позволяет передать выполнение запроса в другой поток без удержания всего сервера занятым.
__JSP (JavaServer Pages)__ — платформонезависимая переносимая и легко расширяемая технология разработки веб-приложений, позволяющая веб-разработчикам создавать содержимое, которое имеет как статические, так и динамические компоненты. Страница JSP содержит текст двух типов: статические исходные данные, которые могут быть оформлены в одном из текстовых форматов HTML, SVG, WML, или XML, и _JSP-элементы_, которые конструируют динамическое содержимое. Кроме этого могут использоваться _библиотеки JSP-тегов_, а также _EL (Expression Language)_, для внедрения Java-кода в статичное содержимое JSP-страниц.
Код JSP-страницы транслируется в Java-код сервлета с помощью компилятора JSP-страниц _Jasper_, и затем компилируется в байт-код JVM.
JSP-страницы загружаются на сервере и управляются Java EE Web Application. Обычно такие страницы упакованы в файловые архивы .war и .ear.
JSP расширяет технологию сервлетов обеспечивая возможность создания динамических страницы с HTML подобным синтаксисом.
Хотя создание представлений поддерживается и в сервлетах, но большая часть любой веб-страницы является статической, поэтому код сервлета в таком случае получается чересчур перегруженным, замусоренным и поэтому при его написании легко допустить ошибку.
Еще одним преимуществом JSP является горячее развертывание - возможность заменить одну страницу на другую непосредственно в контейнере без необходимости перекомпилировать весь проект или перезапускать сервер.
Однако рекомендуется избегать написания серьёзной бизнес-логики в JSP и использовать страницу только в качестве представления.
Когда пользователь переходит по ссылке на страницу `page.jsp`, он отправляет http-запрос на сервер `GET /page.jsp`. Затем, на основе этого запроса и текста самой страницы, сервер генерирует java-класс, компилирует его и выполняет полученный сервлет, формирующий ответ пользователю в виде представления этой страницы, который сервер и перенаправляет обратно пользователю.
Если посмотреть код внутри созданной JSP страницы, то он будет выглядеть как HTML и не будет похож на java класс. Конвертацией JSP страниц в HTML код занимается контейнер, который так же создает и сервлет для использования в веб приложении.
Жизненный цикл JSP состоит из нескольких фаз, которыми руководит JSP контейнер:
+ __Translation__– проверка и парсинг кода JSP страницы для создания кода сервлета.
+ __Class Loading__– загрузка скомпилированного класса в память.
+ __Instantiation__– внедрение конструктора без параметра загруженного класса для инициализации в памяти.
+ __Initialization__– вызов `init()` метода объекта JSP класса и инициализация конфигурации сервлета с первоначальными параметрами, которые указаны в дескрипторе развертывания (`web.xml`). После этой фазы JSP способен обрабатывать запросы клиентов. Обычно эти фазы происходят после первого запроса клиента (т.е. ленивая загрузка), но можно настроить загрузку и инициализацию JSP на старте приложения по аналогии с сервлетами.
+ __Request Processing__– длительный жизненный цикл обработки запросов клиента JSP страницей. Обработка является многопоточной и аналогична сервлетам — для каждого запроса создается новый поток, объекты `ServletRequest` и `ServletResponse`, происходит выполнение сервис методов.
+ __Destroy__– последняя фаза жизненного цикла JSP, на которой её класс удаляется из памяти. Обычно это происходит при выключении сервера или выгрузке приложения.
Контейнер сервлетов (например, Tomcat, GlassFish) создает из JSP-страницы класс сервлета, наследующего свойства интерфейса `javax.servlet.jsp.HttpJspBase` и включающего следующие методы:
+ `jspInit()` — метод объявлен в JSP странице и реализуется с помощью контейнера. Этот метод вызывается один раз в жизненном цикле JSP для того, чтобы инициализировать конфигурационные параметры указанные в дескрипторе развертывания. Этот метод можно переопределить с помощью определения элемента _JSP scripting_ и указания необходимых параметров для инициализации;
+ `_jspService()` — метод переопределяется контейнером автоматически и соответствует непосредственно коду JSP, описанному на странице. Этот метод определен в интерфейсе `HttpJspPage`, его имя начинается с нижнего подчеркивания и он отличается от других методов жизненного цикла тем, что его невозможно переопределить;
+ `jspDestroy()` — метод вызывается контейнером для удаления объекта из памяти (на последней фазе жизненного цикла JSP - Destroy). Метод вызывается только один раз и доступен для переопределения, предоставляя возможность освободить ресурсы, которые были созданы в `jspInit()`.
Прямой доступ к директории `/WEB-INF/` из веб-приложения отсутствует. Поэтому JSP-страницы можно расположить внутри этой папки и тем самым запретить доступ к странице из браузера. Однако, по аналогии с описанием сервлетов, будет необходимо настроить дескриптор развертывания:
Статическое содержимое JSP (HTML, код JavaScript, изображения и т.д.) не изменяется в процессе работы веб приложения.
Динамические ресурсы созданы для того, чтобы отображать свое содержимое в зависимости от пользовательских действий. Обычно они представлены в виде выражений EL (Expression Language), библиотек JSP-тегов и пр.
+ `<!—- HTML комментарий; отображается на странице JSP —->` такие комментарии будут видны клиенту при просмотре кода страницы.
+ `<%—- JSP комментарий; не отображается на странице JSP —-%>` такие комментарии описываются в созданном сервлете и не посылаются клиенту. Для любых комментариев по коду или отладочной информации необходимо использовать именно такой тип комментариев.
__Action tag__ и __JSP Action Elements__ предоставляют методы работы с Java Beans, подключения ресурсов, проброса запросов и создания динамических XML элементов. Такие элементы всегда начинаются с записи `jsp:` и используются непосредственно внутри страницы JSP без необходимости подключения сторонних библиотек или дополнительных настроек.
Наиболее часто используемыми JSP Action Elements являются:
+ `jsp:include`: `<jsp:include page="относительный URL" flush="true"/>` - подключить файл при запросе страницы. Если необходимо, чтобы файл подключался в процессе трансляции страницы, то используется директива `page` совместно с атрибутом `include`;
+ `jsp:useBean`: `<jsp:useBean att=значение*/>` или `<jsp:useBean att=значение*>...</jsp:useBean>` - найти или создать Java bean;
+ `jsp:setProperty`: `<jsp:setProperty att=значение*/>` - установить свойства Java bean, или явно, или указанием на соответствующее значение, передаваемое при запросе;
+ `jsp:forward`: `<jsp:forward page="относительный URL"/>` - передать запрос другой странице;
+ `jsp:plugin`: `<jsp:plugin attribute="значение"*>...</jsp:plugin>` - сгенерировать (в зависимости от типа браузера) тэги `OBJECT` или `EMBED` для апплета, использующего технологию Java Plugin.
Область видимости объектов определяется тем контекстом в который помещается данный объект. В зависимости от той или иной области действия так же определяется время существования объекта.
В JSP предусмотрены следующие области действия переменных (объектов):
+ `request` область действия запроса - объект будет доступен на текущей JSP странице, странице пересылки (при использовании `jsp:forward`) или на включаемой странице (при использовании `jsp:include`);
+ `session` область действия сессии - объект будет помещен в сеанс пользователя, будет доступен на всех JSP страницах и будет существовать пока существует сессия пользователя, или он не будет из нее принудительно удален.
+ `application` область действия приложения - объект будет доступен для всех пользователей на всех JSP страницах и будет существовать на протяжении всей работы приложения или пока не будет удален принудительно и контекста приложения.
+ `page` область действия страницы - объект будет доступен только на той странице, где он определен. На включаемых (`jsp:include`) и переадресуемых (`jsp:forward`) страницах данный объект уже не будет доступен.
Таким образом, чтобы объект был доступен всем JSP страницам, необходимо указать область видимости `application` или `session`, в зависимости от того требуется ли доступ к объекту всем пользователям или только текущему.
Для указания требуемой области действия при определении объекта на JSP странице используется атрибут scope тега `jsp:useBean`:
__JSP implicit objects (неявные объекты)__ создаются контейнером при конвертации JSP страницы в код сервлета для помощи разработчикам. Эти объекты можно использовать напрямую в скриптлетах для передачи информации в сервис методы, однако мы не можем использовать неявные объекты в JSP Declaration, т.к. такой код пойдет на уровень класса.
Существует 9 видов неявных объектов, которые можно использовать прямо на JSP странице. Семь из них объявлены как локальные переменные в начале `_jspService()` метода, а два оставшихся могут быть использованы как аргументы метода `_jspService()`.
+ `out Object` :
```
<strong>Current Time is</strong>: <% out.print(new Date()); %><br>
Неявный объект исключений JSP недоступен в обычных JSP страницах и используется на страницах ошибок JSP (_errorpage_) только для того, чтобы перехватить исключение, выброшенное JSP страницей и далее предоставить какую-либо полезную информацию клиенту.
Неявный объект JSP - экземпляр класса `javax.servlet.jsp.PageContext` предоставляет доступ ко всем пространствам имён, ассоциированным с JSP-страницей, а также к различным её атрибутам.
Остальные неявные объекты добавляются к `pageContext` автоматически.
Класс `PageContext` это абстрактный класс, аего экземпляр можно получить через вызов метода `JspFactory.getPageContext()`, и освободить через вызов метода `JspFactory.releasePageContext()`.
`PageContext` обладает следующим набором особенностей и возможностей:
+ единый API для обслуживания пространств имён различных областей видимости;
+ несколько удобных API для доступа к различным `public`-объектам;
+ механизм получения `JspWriter` для вывода;
+ механизм обслуживания использования сессии страницей;
+ механизм экспонирования («показа») атрибутов директивы `page` среде скриптинга;
+ механизмы направления или включения текущего запроса в другие компоненты приложения;
+ механизм обработки процессов исключений на страницах ошибок _errorpage_;
Параметры инициализации для JSP задаются в `web.xml` файле аналогично сервлетам - элементами `servlet` и `servlet-mapping`. Единственным отличием будет указание местонахождения JSP страницы:
JSP страницы используются в основном для целей отображения представления (_view_), а вся бизнес-логика (_controller_) и модель (_model_) должны быть реализованы в сервлетах или классах-моделях. Обязанность JSP страницы - создание HTML ответа из переданных через атрибуты параметров. Большая часть JSP содержит HTML код а для того, чтобы помочь верстальщикам понять JSP код страницы предоставляется функционал элементов _action_, _JSP EL_, _JSP Standart Tag Library_. Именно их и необходимо использовать вместо скриптлетов для создания моста между (JSP)HTML и (JSP)Java частями.
__JSP Expression Language (EL)__ — скриптовый язык выражений, который позволяет получить доступ к Java компонентам (JavaBeans) из JSP. Начиная с JSP 2.0 используется внутри JSP тегов для отделения Java кода от JSP для обеспечения лёгкого доступа к Java компонентам, уменьшая при этом количество кода Java в JSP-страницах, или даже полностью исключая его.
Развитие EL происходило с целью сделать его более простым для дизайнеров, которые имеют минимальные познания в языке программирования Java. До появления языка выражений, JSP имел несколько специальных тегов таких как скриптлеты (англ.), выражения и т. п. которые позволяли записывать Java код непосредственно на странице. С использованием языка выражений веб-дизайнер должен знать только то, как организовать вызов соответствующих java-методов.
Язык выражений JSP 2.0 включает:
+ Создание и изменение переменных.
+ Управление потоком выполнения программы: ветвление, выполнение различных типов итераций и т.д.
+ Упрощенное обращение к встроенным JSP-объектам.
+ Возможность создавать собственные функции.
Язык выражений используется внутри конструкции `${ ... }`. Подобная конструкция может размещаться либо отдельно, либо в правой части выражения установки атрибута тега.
Операторы в EL поддерживают наиболее часто используемые манипуляции данными.
Типы операторов:
+ Стандартные операторы отношения: `==` (или `eq`), `!=` (или `neq`), `<` (или `lt`), `>` (или `gt`), `<=` (или `le`), `>=` (или `ge`).
+ Арифметические операторы: `+`, `–`, `*`, `/` (или `div`), `%` (или `mod`).
+ Логические операторы: `&&` (или `and`), `||` (или `or`), `!` (или `not`).
+ Оператор `empty`– используется для проверки переменной на `null`, или «пустое значение», который зависит от типа проверяемого объекта. Например, нулевая длина для строки или нулевой размер для коллекции.
Язык выражений JSP предоставляет множество неявных объектов, которые можно использовать для получения атрибутов в различных областях видимости (scopes) и для значений параметров. Важно отметить, что они отличаются от неявных объектов JSP и содержат атрибуты в заданной области видимости. Наиболее часто использующийся implicit object в JSP EL и JSP page — это объект pageContext. Ниже представлена таблица неявных объектов JSP EL.
__JavaServer Pages Standard Tag Library, JSTL, Стандартная библиотека тегов JSP__ — расширение спецификации JSP (конечный результат _JSR 52_), добавляющее библиотеку JSP тегов для общих нужд, таких как разбор XML данных, условная обработка, создание циклов и поддержка интернационализации.
JSTL является альтернативой такому виду встроенной в JSP логики, как _скриплеты_ (прямые вставки Java кода). Использование стандартизованного множества тегов предпочтительнее, поскольку получаемый код легче поддерживать и проще отделять бизнес-логику от логики отображения.
Для использования JSTL тегов необходимо:
+ подключить зависимости, например в `pom.xml`:
```xml
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
```
+ указать пространство имен основных тегов JSTL через указание на JSP странице код:
+ _Core Tags_ предоставляют возможности итерации, обработки исключений, URL, _forward_, _redirect response_ и т.д.
+ _Formatting Tags_ и _Localization Tags_ предоставляют возможности по форматированию чисел, дат и поддержки _i18n_ локализации и _resource bundles_.
+ _SQL Tags_– поддержка работы с базами данных.
+ _XML Tags_ используются для работы с XML документами: парсинга, преобразования данных, выполнения выражений _XPath_ и т.д..
+ _JSTL Functions Tags_ предоставляет набор функций, которые позволяют выполнять различные операции со строками и т.п. Например, по конкатенации или разбиению строк.
Оба тега создают и помещают экземпляры в заданную область видимости, но `<jsp:useBean>` только создаёт экземпляр конкретного типа, а`<c:set>`, создав экземпляр, позволяет дополнительно извлекать значение, например, из параметров запроса, сессии и т. д.
По сравнению с action-тегом `<jsp:include>` и директивой `<%@include %>` тег `<c:import>` обеспечивает более совершенное включение динамических ресурсов, т.к. получает доступ к источнику, чтение информации из которого происходит непосредственно без буферизации и контент включается в исходную JSP построчно.
JSP можно расширить с помощью создания собственных тегов с необходимой функциональностью, которые можно добавить в библиотеку тегов на страницу JSP указав необходимое пространство имен.
Также в пользовательских тегах существует возможность задать входные параметры. Например, существует необходимость отформатировать каким-либо стилем очень длинное число. Для этого можно использовать собственный тег по типу:
Используя входные параметры, число должно быть преобразовано на JSP странице в таком виде `123,456.79` согласно шаблону. Т.к. JSTL не предоставляет такой функциональности, необходимо создать пользовательский тег для получения необходимого результата.
Для переноса строки можно использовать тег `c:out` и атрибут `escapeXml`, который отключает обработку HTML элементов. В этом случае браузер получит следующий код в виде строки и обработает элемент `<br>` как требуется:
`<c:out value="<br> creates a new line in HTML" escapeXml="true"></c:out>`
Стандартные теги JSP не конфигурируются в `web.xml`, потому что tld файлы уже находятся внутри каталога `/META-INF/` в jar файлах JSTL.
Когда контейнер загружает веб-приложение и находит tld файлы в в jar файле в директории `/META-INF/`, то он автоматически настраивает их для непосредственного использования на JSP страницах. Остается только задать пространство имен на JSP странице.
Для обработки исключений выброшенных на JSP странице достаточно лишь задать страницу ошибки JSP и при её создании установить значение _page directive attribute_`isErrorPage` в значение `true`. Таким образом будет предоставлен доступ к неявным объектам исключений в JSP и появится возможность передавать собственные, более информативные сообщения об ошибках клиенту. При этом настройка дескриптора развертывания выглядит так:
Да, это возможно. Несмотря на то, что JSP это серверная технология, на выходе она всё равно создает `HTML` страницу, на которую можно добавлять Javascript и CSS.
`PrintWriter` является объектом отвечающим за запись содержания ответа на запрос. `JspWriter` использует объект `PrintWriter` для буферизации. Когда буфер заполняется или сбрасывается, `JspWriter` использует объект `PrintWriter` для записи содержания в ответ.
Хорошей практикой работы с технологией JSP является соблюдение следующих правил:
+ Следует избегать использования элементов скриптлетов на странице. Если элементы _action_, _JSTL_, _JSP EL_ не удовлетворяют потребностям, то желательно написать собственный тег.
+ Рекомендуется использовать разные виды комментариев: так JSP комментарии необходимы для уровня кода и отладки, т.к. они не будут показаны клиенту.
+ Не стоит размещать какой-либо бизнес логики внутри JSP страницы. Страницы должны использоваться только для создания ответов клиенту.
+ Для повышения производительности лучше отключать создание сессии на странице, когда это не требуется.
+ Директивы `taglib`, `page` в начале JSP страницы улучшают читабельность кода.
+ Следует правильно использовать директиву `include` и элемент `jsp:include action`. Первая используется для статических ресурсов, а второй для динамических ресурсов времени выполнения.
+ Обработку исключений нужно производить с помощью страниц ошибок. Это помогает избегать запуска специальных служебных методов и может повысить производительность.
+ Использующиеся CSS и JavaScript должны быть разнесены в разные файлы и подключаться в начале страницы.
+ В большинстве случаев JSTL должно хватать для всех нужд. Если это не так, то в начале следует проанализировать логику своего приложения, и попробовать перенести выполнения кода в сервлет, а далее с помощью установки атрибутов использовать на JSP странице только результат.