- Что такое «сервлет»?
- В чем заключаются преимущества технологии сервлетов над CGI (Common Gateway Interface)?
- Какова структура веб-проекта?
- Что такое «контейнер сервлетов»?
- Зачем нужны сервера приложений, если есть контейнеры сервлетов?
- Как контейнер сервлетов управляет жизненным циклом сервлета, когда и какие методы вызываются?
- Что такое «дескриптор развертывания»?
- Какие действия необходимо проделать при создании сервлетов?
- В каком случае требуется переопределять метод
service()
? - Есть ли смысл определять для сервлета конструктор? Каким образом лучше инициализировать данные?
- Почему необходимо переопределить только
init()
метод без аргументов? - Какие наиболее распространенные задачи выполняются в контейнере сервлетов?
- Что вы знаете о сервлетных фильтрах?
- Зачем в сервлетах используются различные listener?
- Когда стоит использовать фильтры сервлетов, а когда слушателей?
- Как реализовать запуск сервлета одновременно с запуском приложения?
- Как обработать в приложении исключения, выброшенные другим сервлетом?
- Что представляет собой
ServletConfig
? - Что представляет собой
ServletContext
? - В чем отличия
ServletContext
иServletConfig
? - Для чего нужен интерфейс
ServletResponse
? - Для чего нужен интерфейс
ServletRequest
? - Что такое
Request Dispatcher
? - Как из одного сервлета вызвать другой сервлет?
- Чем отличается
sendRedirect()
отforward()
? - Для чего используются атрибуты сервлетов и как происходит работа с ними?
- Каким образом можно допустить в сервлете deadlock?
- Как получить реальное расположение сервлета на сервере?
- Как получить информацию о сервере из сервлета?
- Как получить IP адрес клиента на сервере?
- Какие классы-обертки для сервлетов вы знаете?
- В чем отличия
GenericServlet
иHttpServlet
? - Почему
HttpServlet
класс объявлен как абстрактный? - Какие основные методы присутствуют в классе
HttpServlet
? - Стоит ли волноваться о многопоточной безопасности работая с сервлетами?
- Какой метод HTTP не является неизменяемым?
- Какие есть методы отправки данных с клиента на сервер?
- В чем разница между методами
GET
иPOST
? - В чем разница между
PrintWriter
иServletOutputStream
? - Можно ли одновременно использовать в сервлете
PrintWriter
иServletOutputStream
? - Расскажите об интерфейсе
SingleThreadModel
. - Что означает URL encoding? Как это осуществить в Java?
- Какие различные методы управления сессией в сервлетах вы знаете?
- Что такое cookies?
- Какие методы для работы с cookies предусмотрены в сервлетах?
- Что такое URL Rewriting?
- Зачем нужны и чем отличаются методы
encodeURL()
иencodeRedirectURL()
? - Что такое «сессия»?
- Как уведомить объект в сессии, что сессия недействительна или закончилась?
- Какой существует эффективный способ удостоверится, что все сервлеты доступны только для пользователя с верной сессией?
- Как мы можем обеспечить transport layer security для нашего веб приложения?
- Как организовать подключение к базе данных, обеспечить журналирование в сервлете?
- Какие основные особенности появились в спецификации Servlet 3?
- Какие способы аутентификации доступны сервлету?
- Что такое Java Server Pages (JSP)?
- Зачем нужен JSP?
- Опишите, как обрабатываются JSP страницы, начиная от запроса к серверу, заканчивая ответом пользователю.
- Расскажите об этапах (фазах) жизненного цикла JSP.
- Расскажите о методах жизненного цикла JSP.
- Какие методы жизненного цикла JSP могут быть переопределены?
- Как можно предотвратить прямой доступ к JSP странице из браузера?
- Какая разница между динамическим и статическим содержимым JSP?
- Как закомментировать код в JSP?
- Какие существуют основные типы тегов JSP?
- Что вы знаете о действиях JSP (Action tag и JSP Action Elements).
- Взаимодействие JSP - сервлет - JSP.
- Какие области видимости переменных существуют в JSP?
- Какие неявные, внутренние объекты и методы есть на JSP странице?
- Какие неявные объекты не доступны в обычной JSP странице?
- Что вы знаете о
PageContext
и какие преимущества его использования? - Как сконфигурировать параметры инициализации для JSP?
- Почему не рекомендуется использовать скриплеты (скриптовые элементы) в JSP?
- Можно ли определить класс внутри JSP страницы?
- Что вы знаете о Языке выражений JSP (JSP Expression Language – EL)?
- Какие типы EL операторов вы знаете?
- Назовите неявные, внутренние объекты JSP EL и их отличия от объектов JSP.
- Как отключить возможность использования EL в JSP?
- Как узнать тип HTTP метода используя JSP EL?
- Что такое JSTL (JSP Standard tag library)?
- Из каких групп тегов состоит библиотека JSTL?
- Какая разница между
<c:set>
и<jsp:useBean>
? - Чем отличается
<c:import>
от<jsp:include>
и директивы<%@include %>
? - Как можно расширить функциональность JSP?
- Что вы знаете о написании пользовательских JSP тегов?
- Приведите пример использования собственных тегов.
- Как сделать перенос строки в HTML средствами JSP?
- Почему не нужно конфигурировать стандартные JSP теги в
web.xml
? - Как можно обработать ошибки JSP страниц?
- Как происходит обработка ошибок с помощью JSTL?
- Как конфигурируется JSP в дескрипторе развертывания.
- Можно ли использовать Javascript на JSP странице?
- Всегда ли создается объект сессии на JSP странице, можно ли отключить его создание?
- Какая разница между
JSPWriter
и сервлетнымPrintWriter
? - Опишите общие практические принципы работы с 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).
- Сервлеты, как платформа и система являются независимыми. Таким образом веб-приложение, написанное с использованием сервлетов может быть запущена в любом контейнере сервлетов, реализующим этот стандарт и в любой операционной системе.
- Использование сервлетов повышает надежность программы, т.к. контейнер сервлетов самостоятельно заботится о жизненном цикле сервлетов (а значит и за утечками памяти), безопасности и сборщике мусора.
- Сервлеты относительно легки в изучении и поддержке, таким образом разработчику необходимо заботиться только о бизнес-логике приложения, а не внутренней реализации веб-технологий.
src/main/java
Исходники приложения/библиотеки
src/main/resources
Ресурсные файлы приложения/библиотеки
src/main/filters
Файлы сервлетных фильтров
src/main/webapp
Исходники веб-приложения
src/test/java
Исходники тестов
src/test/resources
Ресурсные файлы тестов
src/test/filters
Тесты сервлетных фильтров
src/it
Интеграционные тесты
src/assembly
Описание сборки
src/site
Сайт
LICENSE.txt
Лицензия проекта
NOTICE.txt
Замечания и определения библиотек зависимостей.
README.txt
Описание проекта
Контейнер сервлетов — программа, представляющая собой сервер, который занимается системной поддержкой сервлетов и обеспечивает их жизненный цикл в соответствии с правилами, определёнными в спецификациях. Может работать как полноценный самостоятельный веб-сервер, быть поставщиком страниц для другого веб-сервера, или интегрироваться в Java EE сервер приложений.
Контейнер сервлетов обеспечивает обмен данными между сервлетом и клиентами, берёт на себя выполнение таких функций, как создание программной среды для функционирующего сервлета, идентификацию и авторизацию клиентов, организацию сессии для каждого из них.
Наиболее известные реализации контейнеров сервлетов:
- Apache Tomcat
- Jetty
- JBoss
- WildFly
- GlassFish
- IBM WebSphere
- Oracle Weblogic
- Пулы соединений с БД
- Возможность периодического тестирования доступности СУБД и обновления соединения в случае восстановления после сбоев
- Замена прав доступа при подключении
- Балансировка нагрузки между несколькими СУБД, определение доступность или недоступность того или иного узла
- Защита пула соединений от некорректного кода в приложении, которое по недосмотру не возвращает соединения, просто отбирая его после какого-то таймаута.
- JMS
- Доступность сервера очередей сообщений "из-коробки".
- Возможность кластеризации очередей, т.е. доступность построения распределенных очередей, расположенных сразу на нескольких серверах, что существенно увеличивает масштабируемость и доступность приложения
- Возможность миграции очередей - в случае падения одного из серверов, его очереди автоматически перемещаются на другой, сохраняя необработанные сообщения.
- В некоторых серверах приложений поддерживается 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’а, обеспечивающего
- Возможность одновременного запуска нескольких экземпляров сервера
- Просмотр состояния запущенных экземпляров сервера, обработчиков той или иной очереди, на том или ином сервере, количества соединений с той или иной БД
- Присутствие единого центра управления, т.н. 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.
<?xml version="1.0" encoding="UTF-8" ?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<display-name>Display name.</display-name>
<description>Description text.</description>
<servlet>
<servlet-name>ExampleServlet</servlet-name>
<servlet-class>xyz.company.ExampleServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<init-param>
<param-name>configuration</param-name>
<param-value>default</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>ExampleServlet</servlet-name>
<url-pattern>/example</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>ExampleJSP</servlet-name>
<jsp-file>/sample/Example.jsp</jsp-file>
</servlet>
<context-param>
<param-name>myParam</param-name>
<param-value>the value</param-value>
</context-param>
</web-app>
Для веб-приложений дескриптор развертывания должен называться web.xml
и находиться в директории WEB-INF
, в корне веб-приложения. Этот файл является стандартным дескриптором развертывания, определенным в спецификации. Также есть и другие типы дескрипторов, такие, как файл дескриптора развертывания sun-web.xml
, содержащий специфичные для Sun GlassFish Enterprise Server данные для развертывания именно для этого сервера приложений или файл application.xml
в директории META-INF
для приложений J2EE.
Чтобы создать сервлет ExampleServlet
, необходимо описать его в дескрипторе развёртывания:
<servlet-mapping>
<servlet-name>ExampleServlet</servlet-name>
<url-pattern>/example</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>ExampleServlet</servlet-name>
<servlet-class>xyz.company.ExampleServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>default</param-value>
</init-param>
</servlet>
Затем создать класс 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
, который реализует три метода:
void init(FilterConfig config) throws ServletException
;void destroy()
;void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
;
Метод 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
:
<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
:
<filter-mapping>
<filter-name>FilterName</filter-name>
<servlet-name>ServletName</servlet-name>
</filter-mapping>
Для связи фильтра со страницами HTML или группой сервлетов необходимо использовать тег <url-pattern>
:
Подключение фильтра
FilterName
ко всем вызовам .html страниц
<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
; экземпляр класса создается контейнером автоматически в единственном числе и применяется ко всем сессиям; второй: экземпляр класса должен быть создан и закреплён за определённой сессией «вручную», количество экземпляров также регулируется самостоятельно.
Подключение слушателей:
<web-app>
...
<listener>
<listener-class>xyz.company.ExampleListener</listener-class>
</listener>
...
</web-app>
HttpSessionBindingListener
подключается в качестве атрибута непосредственно в сессию, т.е., чтобы его подключить необходимо:
- создать экземпляр класса, реализующего этот интерфейс;
- положить созданный экземпляр в сессию при помощи
setAttribute(String, Object)
.
Следует использовать фильтры, если необходимо обрабатывать входящие или исходящие данные (например: для аутентификации, преобразования формата, компрессии, шифрования и т.д.), в случае, когда необходимо реагировать на события - лучше применять слушателей.
Контейнер сервлетов обычно загружает сервлет по первому запросу клиента.
Если необходимо загрузить сервлет прямо на старте приложения (например если загрузка сервлета происходит длительное время) следует использовать элемент <load-on-startup>
в дескрипторе или аннотацию @loadOnStartup
в коде сервлета, что будет указывать на необходимость загрузки сервлета при запуске.
Если целочисленное значение этого параметра отрицательно, то сервлет будет загружен при запросе клиента. В противном случае - загрузится на старте приложения, при этом, чем число меньше, тем раньше в очереди на загрузку он окажется.
<servlet>
<servlet-name>ExampleServlet</servlet-name>
<servlet-class>xyz.company.ExampleServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
Когда приложение выбрасывет исключение контейнер сервлетов обрабатывает его и создаёт ответ в формате HTML. Это аналогично тому, что происходит при кодах ошибок вроде 404, 403 и т.д.
В дополнении к этому существует возможность написания собственных сервлетов для обработки исключений и ошибок с указанием их в дескрипторе развертывания:
<error-page>
<error-code>404</error-code>
<location>/AppExceptionHandler</location>
</error-page>
<error-page>
<exception-type>javax.servlet.ServletException</exception-type>
<location>/AppExceptionHandler</location>
</error-page>
Основная задача таких сервлетов - обработать ошибку/исключение и сформировать понятный ответ пользователю. Например, предоставить ссылку на главную страницу или же описание ошибки.
Интерфейс javax.servlet.ServletConfig
используется для передачи сервлету конфигурационной информации. Каждый сервлет имеет свой собственный экземпляр объекта ServletConfig
, создаваемый контейнером сервлетов.
Для установки параметров конфигурации используются параметры init-param
в web.xml
:
<servlet>
<servlet-name>ExampleServlet</servlet-name>
<servlet-class>xyz.company.ExampleServlet</servlet-class>
<init-param>
<param-name>exampleParameter</param-name>
<param-value>parameterValue</param-value>
</init-param>
</servlet>
или аннотации @WebInitParam
:
@WebServlet(
urlPatterns = "/example",
initParams = {
@WebInitParam(name = "exampleParameter", value = "parameterValue")
}
)
public class ExampleServlet extends HttpServlet {
//...
}
Для получения ServletConfig
сервлета используется метод getServletConfig()
.
Уникальный (в рамках веб-приложения) объект ServletContext
реализует интерфейс javax.servlet.ServletContext
и предоставляет сервлетам доступ к параметрам этого веб-приложения. Для предоставления доступа используется элемент <context-param>
в web.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
можно изменять.
Интерфейс ServletResponse
используется для отправки данных клиенту. Все методы данного инструмента служат именно этой цели:
String getCharacterEncoding()
- возвращает MIME тип кодировки (к примеру - UTF8), в которой будет выдаваться информация;void setLocale(Locale locale)
/Locale getLocale()
- указывают на язык используемый в документе;ServletOutputStream getOutputStream()
/PrintWriter getWriter()
- возвращают потоки вывода данных;void setContentLength(int len)
- устанавливает значение поля HTTP заголовка Content-Length;void setContentType(String type)
- устанавливает значение поля HTTP заголовка Content-Type.void reset()
- позволяет сбросить HTTP заголовок к значениям по-умолчанию, если он ещё не был отправлен- и др.
Интерфейс ServletRequest
используется для получения параметров соединения, запроса, а также заголовков, входящего потока данных и т.д.
Интерфейс 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
.
forward()
:
- Выполняется на стороне сервера;
- Запрос перенаправляется на другой ресурс в пределах того же сервера;
- Не зависит от протокола клиентского запроса, так как обеспечивается контейнером сервлетов;
- Нельзя применять для внедрения сервлета в другой контекст;
- Клиент не знает о фактически обрабатываемом ресурсе и URL в строке остается прежним;
- Выполняется быстрее метода
sendRedirect()
; - Определён в интерфейсе
RequestDispatcher
.
sendRedirect()
:
- Выполняется на стороне клиента;
- Клиенту возвращается ответ
302 (redirect)
и запрос перенаправляется на другой сервер; - Может использоваться только с клиентами HTTP;
- Разрешается применять для внедрения сервлета в другой контекст;
- URL адрес изменяется на адрес нового ресурса;
- Медленнее
forward()
т.к. требует создания нового запроса; - Определён в интерфейсе
HttpServletResponse
.
Атрибуты сервлетов используются для внутренней коммуникации сервлетов.
В веб-приложении существует возможность работы с атрибутами используя методы setAttribute()
, getAttribute()
, removeAttribute()
, getAttributeNames()
, которые предоставлены интерфейсами ServletRequest
, HttpSession
и ServletContext
(для областей видимости request, session, context соответственно).
Можно получить блокировку, например, допустив циклические вызовы метода doPost()
в методе doGet()
и метода doGet()
в методе doPost()
.
Реальный путь к расположению сервлета на сервере можно получить из объекта ServletContext
:
getServletContext().getRealPath(request.getServletPath())
.
Информацию о сервере можно получить из объекта ServletContext
:
getServletContext().getServerInfo()
.
IP адрес клиента можно получить вызвав request.getRemoteAddr()
.
Собственные обработчики ServletRequest
и ServletResponse
можно реализовать, добавив новые или переопределив существующие методы у классов-обёрток ServletRequestWrapper
(HttpServletRequestWrapper
) и ServletResponseWrapper
(HttpServletRequestWrapper
).
Абстрактный класс GenericServlet
— независимая от используемого протокола реализация интерфейса Servlet
, а абстрактный класс HttpServlet
в свою очередь расширяет GenericServlet
для протокола HTTP..
Класс HTTPServlet
предоставляет лишь общую реализацию сервлета для HTTP протокола. Реализация ключевых методов doGet()
и doPost()
, содержащих основную бизнес-логику перекладывается на разработчика и по умолчанию возвращает HTTP 405 Method Not Implemented error
.
doGet()
- для обработки HTTP запросовGET
;doPost()
- для обработки HTTP запросовPOST
;doPut()
- для обработки HTTP запросовPUT
;doDelete()
- для обработки HTTP запросовDELETE
;doHead()
- для обработки HTTP запросовHEAD
;doOptions()
- для обработки HTTP запросовOPTIONS
;doTrace()
- для обработки HTTP запросовTRACE
.
Методы init()
и destroy()
вызываются один раз за жизненный цикл сервлета — поэтому по поводу них беспокоиться не стоит.
Методы 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, который виден почти всегда. Иногда это преимущество превращается в недостаток - вы не сможете послать данные за кого-то другого. GET
метод является неизменяемым, тогда какPOST
— изменяемый.
PrintWriter
— класс для работы с символьным потоком, экземпляр которого можно получить через метод ServletResponse
getWriter()
;
ServletOutputStream
— класс для работы байтовым потоком. Для получения его экземпляра используется метод ServletResponse
getOutputStream()
.
Так сделать не получится, т.к. при попытке одновременного вызова 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()
. Используя это нововведение стало доступным построение динамической системы, в которой необходимый объект будет создан и вызван только по необходимости. - Асинхронное выполнение. Поддержка асинхронной обработки позволяет передать выполнение запроса в другой поток без удержания всего сервера занятым.
Спецификация сервлетов определяет четыре типа проверки подлинности:
- HTTP Basic Authentication -
BASIC
. При доступе к закрытым ресурсам появится окно, которое попросит ввести данные для аутентификации. - Form Based Login -
FORM
. Используется собственная html форма: - HTTP Digest Authentication -
DIGEST
. Цифровая аутентификация с шифрованием. - HTTPS Authentication -
CLIENT-CERT
. Аутентификация с помощью клиентского сертификата.
<login-config>
<auth-method>FORM</auth-method>
<form-login-config>
<form-login-page>/login.html</form-login-page>
<form-error-page>/error.html</form-error-page>
</form-login-config>
</login-config>
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 и использовать страницу только в качестве представления.
Опишите, как обрабатываются JSP страницы, начиная от запроса к серверу, заканчивая ответом пользователю.
Когда пользователь переходит по ссылке на страницу page.jsp
, он отправляет http-запрос на сервер GET /page.jsp
. Затем, на основе этого запроса и текста самой страницы, сервер генерирует java-класс, компилирует его и выполняет полученный сервлет, формирующий ответ пользователю в виде представления этой страницы, который сервер и перенаправляет обратно пользователю.
Если посмотреть код внутри созданной JSP страницы, то он будет выглядеть как HTML и не будет похож на java класс. Конвертацией JSP страниц в HTML код занимается контейнер, который также создает и сервлет для использования в веб приложении.
Жизненный цикл JSP состоит из нескольких фаз, которыми руководит JSP контейнер:
- Translation – проверка и парсинг кода JSP страницы для создания кода сервлета.
- Compilation – компиляция исходного кода сервлета.
- 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()
.
Возможно переопределить лишь jspInit()
и jspDestroy()
методы.
Прямой доступ к директории /WEB-INF/
из веб-приложения отсутствует. Поэтому JSP-страницы можно расположить внутри этой папки и тем самым запретить доступ к странице из браузера. Однако, по аналогии с описанием сервлетов, будет необходимо настроить дескриптор развертывания:
<servlet>
<servlet-name>Example</servlet-name>
<jsp-file>/WEB-INF/example.jsp</jsp-file>
<init-param>
<param-name>exampleParameter</param-name>
<param-value>parameterValue</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Example</servlet-name>
<url-pattern>/example.jsp</url-pattern>
</servlet-mapping>
Статическое содержимое JSP (HTML, код JavaScript, изображения и т.д.) не изменяется в процессе работы веб приложения.
Динамические ресурсы созданы для того, чтобы отображать свое содержимое в зависимости от пользовательских действий. Обычно они представлены в виде выражений EL (Expression Language), библиотек JSP-тегов и пр.
<!—- HTML комментарий; отображается на странице JSP —->
такие комментарии будут видны клиенту при просмотре кода страницы.<%—- JSP комментарий; не отображается на странице JSP —-%>
такие комментарии описываются в созданном сервлете и не посылаются клиенту. Для любых комментариев по коду или отладочной информации необходимо использовать именно такой тип комментариев.
- Выражение JSP:
<%= expression %>
- выражение, которое будет обработано с перенаправлением результата на вывод; - Скриплет JSP:
<% code %>
- код, добавляемый в методservice()
. - Объявление JSP:
<%! code %>
- код, добавляемый в тело класса сервлета вне методаservice()
. - Директива JSP page:
<%@ page att="value" %>
- директивы для контейнера сервлетов с информацией о параметрах. - Директива JSP include:
<%@ include file="url" %>
- файл в локальной системе, подключаемый при трансляции JSP в сервлет. - Комментарий JSP:
<%-- comment --%>
- комментарий; игнорируется при трансляции 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 - сервлет - JSP» архитектура построения приложений носит название MVC (Model/View/Controller):
-
Model - классы данных и бизнес-логики;
-
View - страницы JSP;
-
Controller - сервлеты.
Область видимости объектов определяется тем контекстом, в который помещается данный объект. В зависимости от той или иной области действия так же определяется время существования объекта.
В JSP предусмотрены следующие области действия переменных (объектов):
request
область действия запроса - объект будет доступен на текущей JSP странице, странице пересылки (при использованииjsp:forward
) или на включаемой странице (при использованииjsp:include
);session
область действия сессии - объект будет помещен в сеанс пользователя, будет доступен на всех JSP страницах и будет существовать пока существует сессия пользователя, или он не будет из нее принудительно удален.application
область действия приложения - объект будет доступен для всех пользователей на всех JSP страницах и будет существовать на протяжении всей работы приложения или пока не будет удален принудительно и контекста приложения.page
область действия страницы - объект будет доступен только на той странице, где он определен. На включаемых (jsp:include
) и переадресуемых (jsp:forward
) страницах данный объект уже не будет доступен.
Таким образом, чтобы объект был доступен всем JSP страницам, необходимо указать область видимости application
или session
, в зависимости от того требуется ли доступ к объекту всем пользователям или только текущему.
Для указания требуемой области действия при определении объекта на JSP странице используется атрибут scope тега jsp:useBean
:
<jsp:useBean id="myBean" class="ru.javacore.MyBean" scope="session"/>
Если не указывать атрибут scope
, то по умолчанию задается область видимости страницы page
JSP implicit objects (неявные объекты) создаются контейнером при конвертации JSP страницы в код сервлета для помощи разработчикам. Эти объекты можно использовать напрямую в скриптлетах для передачи информации в сервис методы, однако мы не можем использовать неявные объекты в JSP Declaration, т.к. такой код пойдет на уровень класса.
Существует 9 видов неявных объектов, которые можно использовать прямо на JSP странице. Семь из них объявлены как локальные переменные в начале _jspService()
метода, а два оставшихся могут быть использованы как аргументы метода _jspService()
.
out Object
:
<strong>Current Time is</strong>: <% out.print(new Date()); %><br>
request Object
:
<strong>Request User-Agent</strong>: <%=request.getHeader("User-Agent") %><br>
response Object
:
<strong>Response</strong>: <%response.addCookie(new Cookie("Test","Value")); %>
config Object
:
<strong>User init param value</strong>: <%=config.getInitParameter("User") %><br>
application Object
:
<strong>User context param value</strong>: <%=application.getInitParameter("User") %><br>
session Object
:
<strong>User Session ID</strong>: <%=session.getId() %><br>
pageContext Object
:
<% pageContext.setAttribute("Test", "Test Value"); %>
<strong>PageContext attribute</strong>: {Name="Test",Value="<%=pageContext.getAttribute("Test") %>"}<br>
page Object
:
<strong>Generated Servlet Name</strong>: <%=page.getClass().getName() %>
exception Object
:
<strong>Exception occured</strong>: <%=exception %><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 страницы:
<servlet>
<servlet-name>Example</servlet-name>
<jsp-file>/WEB-INF/example.jsp</jsp-file>
<init-param>
<param-name>exampleParameter</param-name>
<param-value>parameterValue</param-value>
</init-param>
</servlet>
JSP страницы используются в основном для целей отображения представления (view), а вся бизнес-логика (controller) и модель (model) должны быть реализованы в сервлетах или классах-моделях. Обязанность JSP страницы - создание HTML ответа из переданных через атрибуты параметров. Большая часть JSP содержит HTML код, а для того, чтобы помочь верстальщикам понять JSP код страницы предоставляется функционал элементов action, JSP EL, JSP Standart Tag Library. Именно их и необходимо использовать вместо скриптлетов для создания моста между (JSP)HTML и (JSP)Java частями.
Определить класс внутри JSP страницы можно, но это считается плохой практикой:
<%!
private static class ExampleOne {
//...
}
%>
<%
private class ExampleTwo {
//...
}
%>
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.
Для игнорирования выполнения языка выражений на странице существует два способа:
- использовать директиву
<%@ page isELIgnored = «true» %>
, - настроить
web.xml
(лучше подходит для отключения EL сразу на нескольких страницах):
<jsp-config>
<jsp-property-group>
<url-pattern>*.jsp</url-pattern>
<el-ignored>true</el-ignored>
</jsp-property-group>
</jsp-config>
${pageContext.request.method}
.
JavaServer Pages Standard Tag Library, JSTL, Стандартная библиотека тегов JSP — расширение спецификации JSP (конечный результат JSR 52), добавляющее библиотеку JSP тегов для общих нужд, таких как разбор XML данных, условная обработка, создание циклов и поддержка интернационализации.
JSTL является альтернативой такому виду встроенной в JSP логики, как скриплеты (прямые вставки Java кода). Использование стандартизованного множества тегов предпочтительнее, поскольку получаемый код легче поддерживать и проще отделять бизнес-логику от логики отображения.
Для использования JSTL тегов необходимо:
- подключить зависимости, например в
pom.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 странице код:
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/sql" prefix="sql" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/xml" prefix="x" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
Группы тегов JSTL согласно их функциональности:
- 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 указав необходимое пространство имен.
/WEB-INF/exampleTag.tld
<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.1" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_1.xsd">
<tlib-version>1.0</tlib-version>
<short-name>example</short-name>
<uri>/WEB-INF/exampleTag</uri>
<tag>
<name>exampleTag</name>
<tag-class>xyz.company.ExampleTag</tag-class>
<body-content>empty</body-content>
<info>The example tag displays Hello World!</info>
</tag>
</taglib>
xyz.company.ExampleServlet.java
package xyz.company;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;
public class ExampleTag extends TagSupport{
private static final long serialVersionUID = 1L;
@Override
public int doStartTag() throws JspException {
try {
pageContext.getOut().print("Hello World!");
} catch(IOException ioException) {
throw new JspException("Error: " + ioException.getMessage());
}
return SKIP_BODY;
}
}
exampleTag.jsp
<%@ taglib uri="/WEB-INF/exampleTag.tld" prefix="example"%>
<%@ page session="false" pageEncoding="UTF-8"%>
<html>
<head>
<title>Example Tag</title>
</head>
<body>
<h1>Example Tag</h1>
<p><example:exampleTage /><p>
</body>
</html>
Также в пользовательских тегах существует возможность задать входные параметры. Например, существует необходимость отформатировать каким-либо стилем очень длинное число. Для этого можно использовать собственный тег по типу:
<mytags:formatNumber number="123456.789" format="#,## #.00"/>
Используя входные параметры, число должно быть преобразовано на 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 и появится возможность передавать собственные, более информативные сообщения об ошибках клиенту. При этом настройка дескриптора развертывания выглядит так:
<error-page>
<error-code>404</error-code>
<location>/error.jsp</location>
</error-page>
<error-page>
<exception-type>java.lang.Throwable</exception-type>
<location>/error.jsp</location>
</error-page>
Для перехвата и обработки исключений в служебных методах класса служат JSTL Core Tags c:catch
и c:if
.
Тег
c:catch
перехватывает исключение и обертывает его в переменнуюexception
, доступную для обработки в тегеc:if
:
<c:catch var ="exception">
<% int x = 42/0;%>
</c:catch>
<c:if test = "${exception ne null}">
<p>Exception is : ${exception} <br />
Exception Message: ${exception.message}</p>
</c:if>
Обратите внимание что используется язык выражений JSP EL в теге c:if
.
Для настройки различных параметров JSP страниц используется элемент jsp-config
, который отвечает за:
- управление элементами скриптлетов на странице;
- управления выполнением в языке выражений;
- определение шаблона URL для encoding;
- определение размера буфера, который используется для объектов на странице;
- обозначение групп ресурсов, соответствующих шаблону URL, которые должны быть обработаны как XML документ.
<jsp-config>
<taglib>
<taglib-uri>http://company.xyz/jsp/tlds/customtags</taglib-uri>
<taglib-location>/WEB-INF/exampleTag.tld</taglib-location>
</taglib>
</jsp-config>
Да, это возможно. Несмотря на то, что JSP это серверная технология, на выходе она всё равно создает HTML
страницу, на которую можно добавлять Javascript и CSS.
Jsp-страница, по умолчанию, всегда создает сессию. Используя директиву page
с атрибутом session
можно изменить это поведение:
<%@ page session ="false" %>
PrintWriter
является объектом отвечающим за запись содержания ответа на запрос. JspWriter
использует объект PrintWriter
для буферизации. Когда буфер заполняется или сбрасывается, JspWriter
использует объект PrintWriter
для записи содержания в ответ.
Хорошей практикой работы с технологией JSP является соблюдение следующих правил:
- Следует избегать использования элементов скриптлетов на странице. Если элементы action, JSTL, JSP EL не удовлетворяют потребностям, то желательно написать собственный тег.
- Рекомендуется использовать разные виды комментариев: так JSP комментарии необходимы для уровня кода и отладки, т.к. они не будут показаны клиенту.
- Не стоит размещать какой-либо бизнес логики внутри JSP страницы. Страницы должны использоваться только для создания ответов клиенту.
- Для повышения производительности лучше отключать создание сессии на странице, когда это не требуется.
- Директивы
taglib
,page
в начале JSP страницы улучшают читабельность кода. - Следует правильно использовать директиву
include
и элементjsp:include action
. Первая используется для статических ресурсов, а второй для динамических ресурсов времени выполнения. - Обработку исключений нужно производить с помощью страниц ошибок. Это помогает избегать запуска специальных служебных методов и может повысить производительность.
- Использующиеся CSS и JavaScript должны быть разнесены в разные файлы и подключаться в начале страницы.
- В большинстве случаев JSTL должно хватать для всех нужд. Если это не так, то в начале следует проанализировать логику своего приложения, и попробовать перенести выполнения кода в сервлет, а далее с помощью установки атрибутов использовать на JSP странице только результат.