Продолжается подписка на наши издания! Вы не забыли подписаться?

Java Server Pages 2.0

Петр Михеев

Java Server Pages (JSP, или в русском переводе серверные страницы Java) являются стандартным расширением платформы Java и построены на основе технологии сервлетов. В свое время технология Java Server Pages была создана Sun как ответ на технологию Active Server Pages (ASP), разработанную Microsoft. JSP и ASP – сходные технологии: обе позволяют добавлять к HTML исполняемый код и способность обращаться к внешним компонентам.

Основное назначение JSP-страниц – это упростить создание и управление динамическим содержанием в World Wide Web. Структура серверной страницы JSP представляет некий компромисс между сервлетом и обычной HTML-страницей. Так, технология JSP позволяет комбинировать разметку на языке HTML или XML с фрагментами кода Java в одном документе. Этот код выполняется на стороне сервера и служит для обработки запросов клиентов и генерации ответов. Фрагменты кода Java, как и обычные HTML-теги, начинаются и заканчиваются угловыми скобками, однако дополнительно к угловым скобкам используются знаки процента. Эти специальные теги предписывают контейнеру JSP использовать этот код для генерации сервлета или его части. Остальные HTML- и XML-фрагменты, а также прочие данные передаются клиенту без обработки на стороне сервера.

На рисунке 1 представлен жизненный цикл, который проходит JSP-страница, прежде чем начать обработку запроса клиента. Сначала разработчик создает JSP-страницу, используя стандартные объекты. К таким объектам относятся объекты, представляющие HTTP-запрос, HTTP-ответ, сеанс взаимодействия, контекст, выходной поток, а также некоторые другие. Затем исходная JSP-страница преобразуется в код представляющего ее сервлета, который компилируется компилятором Java в файл класса сервлета. Активация сервлета, представляющего JSP, происходит так же, как и активация любого другого сервлета Java. Этот сервлет может обрабатывать запросы, генерировать ответы и управлять сеансом.

Современные контейнеры сервлетов могут выполнять эту последовательность действий и в “обратном” порядке. Когда страница JSP в первый раз запрашивается пользователем, генерируется код сервлета, необходимый для выполнения кода, записанного в странице JSP. Затем этот код компилируется и загружается в контейнер сервлетов. Статические части JSP-страницы выводятся непосредственно в виде строк, а динамические части страницы включаются прямо в код сервлета. С этого момента, если только код серверной страницы JSP не изменяется, страница ведет себя как обычный сервлет. При модификации исходного кода страницы JSP она автоматически перекомпилируется и будет перезагружена при последующих обращениях к ней. Вследствие такой поддержки “прозрачности” обновления страницы, в первый раз она будет загружаться в течение довольно длительного времени. Последнее обстоятельство не является большим неудобством, так как обычно серверные страницы используются гораздо чаще, чем изменяются.

Рисунок 1. Жизненный цикл JSP-страниц.

Спецификация JSP 1.1 (выпущенная в начале 2000 года) предоставила возможность расширения системы тегов разметки (tag extension), позволяя определять пользовательские теги и связывать их с классами Java. Используя дополнительные теги, разработчики могут вынести Java-код со страницы в классы-обработчики этих тегов. При этом возможность использования фрагментов Java-кода на странице сохраняется, но потребность в этом почти полностью устраняется. Наличие пользовательских тегов позволяет организовать работу так, чтобы дизайнеры и программисты могли решать свои задачи практически независимо друг от друга. В результате многие производители начали создавать собственные библиотеки тегов. Нередко наборы тегов, реализующие стандартные операции, отличались всего лишь названиями, но разработчикам приходилось изучать, а часто и приобретать новую библиотеку тегов, только для того, чтобы приложение соответствовало требованиям заказчика. Это приводило и к проблеме переносимости приложений: при установке приложения требовалось устанавливать и настраивать новую библиотеку тегов.

Несмотря на огромную популярность языка программирования Java, в последнее время широкое распространение получили шаблонные языки: WebMacro, Velocity, FreeMarker, Tea и другие [7], которые было можно использовать и в составе страниц JSP. Синтаксис шаблонных языков гораздо проще, чем Java, их можно гораздо легче и быстрее изучить, и дизайнеры, несмотря на некоторую функциональную ограниченность шаблонных языков, часто используют именно их. В итоге технология Java Server Pages вместо стандартного средства создания динамических страниц в World Wide Web стала превращаться в набор шаблонных языков. Каждый разработчик выбирал тот язык, который ему больше нравился и лучше подходил для решения тех или иных задач.

Перечисленные выше проблемы разработчики технологии JSP попытались решить в версии 2.0. Сейчас спецификация JSP 2.0 находится на стадии Proposed Final Draft 3, и похоже, что скоро она будет принята практически в таком же виде. В спецификацию JSP 2.0 было добавлено много совершенно новых возможностей. Но обо всем по порядку.

Особенности Java Server Pages 2.0

В отличие от предыдущих версий, JSP 2.0 является неотъемлемой составной частью J2EE 1.4. Разработчики JSP 2.0 улучшили практически все аспекты технологии. Основные нововведения в JSP 2.0 заключаются в следующем:

Таким образом, Java Server Pages 2.0 является значительным шагом вперед на пути создания новых методов и средств, предназначенных для создания динамических страниц в World Wide Web. Дальше мы рассмотрим перечисленные особенности более подробно.

Язык выражений JSP 2.0

Спецификация JSP 2.0. включает простой язык выражений, который позволяет разработчикам уменьшить количество кода Java в JSP-страницах, или даже полностью обойтись без него. Язык выражений JSP 2.0 первоначально входил в состав JSTL. Тогда язык выражений JSP 2.0 называли SPEL (Simplest Possible Expression Language – самый простой язык выражений). Язык выражений JSP 2.0 включает:

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

Примеры использования
языка выражений JSP 2.0

Язык выражений используется в JSP 2.0 внутри конструкции ${ ... }. Подобная конструкция может размещаться либо отдельно, либо в правой части выражения установки атрибута тега. Первый случай показан в следующем примере.

Листинг 1. Фрагмент JSP-страницы, выводящий тираж журнала за 2003 год.
Тираж журнала ${magazine.fullName} за 2003 составляет 
${magazineInfo[magazine.id].circulation}

В этом примере выражение ${magazine.fullName} получает свойство fullName объекта magazine. При этом объект magazine рассматривается как компонент JavaBean, и для получения свойства fullName у него вызывается метод getFullName().

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

Листинг 2. Фрагмент JSP-страницы, выводящий “Наши поздравления!”, если тираж журнала больше 10000.
<%@ taglib prefix="с" uri="http://java.sun.com/jsp/jstl/core"%>
...
<c:if test="${magazineInfo[magazine.id].circulation > 10000}" >
  Наши поздравления!
</c:if> 

В листинге 2 выражение

${magazineInfo[magazine.id].circulation > 10000} 

сравнивает тираж журнала с 10000, и в зависимости от результата выводит или не выводит сообщение “Наши поздравления!”.

Из этих двух фрагментов можно получить представление об основных принципах использования языка выражений. Для более полной информации смотрите [1].

Создание пользовательской функции

Несмотря на то, что язык выражений JSP 2.0 довольно выразителен, разработчику могут потребоваться более мощные возможности, и тогда без использования Java-кода не обойтись. Создатели JSP 2.0 предусмотрели и этот случай. Вместо того, чтобы превращать JSP-страницу в смесь HTML-разметки, языка выражений и высокоуровневого кода Java, нужно определить функцию языка выражений и поместить туда весь необходимый Java-код.

Чтобы создать пользовательскую функцию вы должны написать public-класс, public-метод которого и будет представлять пользовательскую функцию. Следующий пример определяет функцию showTime, которая выводит текущее время.

Листинг 3. Реализация функции showTime, выводящей текущее время.
package tcspkg;
public class tcsfunctions 
{
 ...
  public static String showTime(String greeting) 
  {
    String tm = new Date().toLocaleString();
    return greeting + tm;
  }
} 

Затем нужно задать отображение метода созданного класса на функцию языка выражений. Для этого в файл functions.tld нужно включить следующий фрагмент:

Листинг 4. Отображение функции showTime на метод созданного класса.
<function>
  <name>showTime</name>
  <function-class>tcspkg.tcsfunctions</function-class>
  <function-signature>String showTime(java.lang.String)</function-signature>
</function> 

В результате новую функцию можно использовать в JSP-страницах:

Листинг 5. Использование функции showTime.
<%@ taglib prefix="f" uri="http://java.sun.com/jsp/jstl/functions"%>
...
Current greeting = ${f:showTime(“Привет, ”)}

JSTL – стандартная библиотека
тегов JSP 2.0

После выпуска спецификации JSP 1.1 (позволяющей создавать пользовательские теги) многие производители стали создавать обширные библиотеки дополнительных тегов. В предыдущих версиях JSP также существовал небольшой набор дополнительных тегов (XML-представлений различных элементов JSP), например, jsp:include или jsp:forward. Однако функциональность этого набора тегов была сильно ограничена. Кроме того, при его создании не использовался механизм создания пользовательских тегов JSP 1.1.

Наконец, в версию JSP 2.0 Sun включила собственную полноценную библиотеку тегов – JavaServer Pages Standard Tag Library (JSTL – стандартная библиотека тегов JSP), построенную на основе механизма создания пользовательских тегов JSP 2.0. JSTL включает в себя типичный набор тегов для серверных приложений. Эта стандартизация позволяет разработчику изучить единственный набор тегов и затем использовать их во всех разрабатываемых JSP-страницах.

Таблица 1. Сводная характеристика библиотек
тегов JSTL.

Название Рекомендуемый префикс Функции Теги
Core C Поддержка операций с переменными
remove 
set
Управление потоком выполнения
choose 
  when 
  otherwise 
forEach 
forTokens 
if
Операции с URL
import 
  param 
redirect 
  param 
url 
  param
Утилиты
catch 
out
XML X Основные теги
out 
parse 
set
Управление потоком выполнения
choose 
  when 
  otherwise 
forEach 
if
Теги трансформации
transform 
  param
I18N Fmt Установка региона
setLocale 
requestEncoding
Генерация сообщений
bundle 
message 
  param 
setBundle
Форматирование дат и цифр
formatNumber 
formatDate 
parseDate 
parseNumber 
setTimeZone 
timeZone
Database Sql Установка соединения
setDataSource
Генерация SQL-запросов
query 
  dateParam
  param
transaction 
update
  dateParam 
  param
Functions fn Вычисление длины коллекций
length
Манипуляции со строками
toUpperCase, 
toLowerCase 
substring, 
substringAfter, 
substringBefore 
trim 
replace 
indexOf, 
startsWith, 
endsWith, 
contains, 
containsIgnoreCase 
split, join 
escapeXml

Теги, входящие в состав JSTL, служат для управления потоком выполнения программы (итерации и условные операторы), манипуляций с XML-документами, поддержки интернационализации и доступа к базам данных, использующим SQL. JSTL включает большое количество тегов, которые разбиты на 5 библиотек в соответствии с их назначением. У каждой библиотеки тегов существует собственное пространство имен. Сводная характеристика библиотек тегов и функций, которые они реализуют, приведена в таблице 1. Ниже мы более подробно рассмотрим каждую библиотеку тегов.

Основные теги (Core Tags)

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

Листинг 6. Фрагмент, включающий различные
JSP-страницы в зависимости от тиража журнала.
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
...
<c:choose> 
  <c:when test="${magazineInfo[magazine.id].circulation == 0}">
<jsp:include page="/views/empty.jsp"/>
  </c:when> 
  <c:when test="${magazineInfo[magazine.id].circulation > 0}" >
<jsp:include page="/views/showmag.jsp"/>
  </c:when> 
  <c:otherwise> 
<jsp:include page="/views/empty.jsp"/>
  </c:otherwise> 
</c:choose>  

В этом примере теги JSTL комбинируются с XML-представлением элементов JSP: так тег <c:choose>…</c:choose> соответствует классическому оператору case, тег <jsp:include> представляет XML-вариант директивы include.

Теги для работы с XML (XML Tags)

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

Листинг 7. Фрагмент, выводящий информацию о
журнале в зависимости от его идентификатора.
<%@ taglib prefix="x" uri="http://java.sun.com/jsp/jstl/xml"%>
...
<x:parse doc="/magazines.xml" var="mags" scope="application" />
<x:set var="mag" 
select="$applicationScope.mags/magazines/magazine[@id=$param.magazineId]"/>
<x:out select="$mag/title"/>
<x:out select="$mag/circulation"/>

В листинге 7 тег <x:parse> используется для загрузки XML-документа magazines.xml в переменную mags, затем в переменную mag помещается информация о журнале с заданным идентификатором и, наконец, эта информация выводится с помощью тега <x:out>.

Теги для поддержки интернационализации (I18N Tags)

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

Листинг 8. Фрагмент, выводящий заголовок журнала в зависимости от выбранного языка.

<%@ taglib prefix="x" uri="http://java.sun.com/jsp/jstl/xml"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
...
<c:set var="locale" value="${param.lang}" />
<fmt:setLocale value="${locale}" scope="session"/>
<fmt:setBundle basename="ru.optim.tcs" scope="session"/>
<fmt:message key="title" />

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

Рисунок 2. Иерархия интерфейсов и классов для создания тегов в JSP 2.0
(серый цвет относится и к предыдущим версиям JSP, белый – только JSP 2.0).

Теги для работы с базами данных
(Database Tags)

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

Листинг 9. Фрагмент, выводящий информацию
о журнале в зависимости от его идентификатора
.

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="sql" uri="http://java.sun.com/jsp/jstl/sql"%>
...
<sql:setDataSource dataSource="jdbc/MagsDB" /> 
<c:set var="mid" value="${param.Add}"/>
<sql:query var="mags" > 
  select * from PUBLIC.magazines where id = ?
  <sql:param value="${mid}" />
</sql:query> 
<c:forEach var="mag" begin="0" items="${mags.rows}">
  <h1>${mag.title}</h1>
  <h2>${mag.circulation}</h2>
</c:forEach> 

В листинге 9 тег <sql:setDataSource> используется для установки соединения с базой данных. В качестве источника данных (dataSource) можно задавать имя JNDI или набор параметров класса DriverManager (имя драйвера, хоста и расположенной на нем базы данных, номер порта, а также имя пользователя и пароль). Последующие теги, как можно понять из их названий, выполняют SQL-запрос к базе данных и выводят результат этого запроса.

Функции

Набор встроенных функций JSTL включает функции для вычисления длин произвольных коллекций, а также функции для практически всех возможных операций со строками. Следующий пример определяет длину названия журнала.

Листинг 10. Фрагмент JSP-страницы,
определяющий длину названия журнала.
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="sql" uri="http://java.sun.com/jsp/jstl/sql"%>
...
<c:if test="${fn:length(param.magname) > 0}" >
  <%@include file="storeindb.jsp" %>
</c:if>

В этом примере для вычисления длины имени журнала (param.magname) используется функция length, входящая в состав JSTL. Как видно, функции языка выражений, создание которых было рассмотрено в предыдущем разделе, и функции JSTL создаются и вызываются одинаково. Разработчики JSP 2.0 не стали создавать отдельную библиотеку функций, вместо этого они включили ее в состав JSTL.

Реализация пользовательских тегов в JSP 2.0

Библиотека тегов – это набор тегов, объединенных общим назначением. Библиотека тегов включает Tag Library Descriptor (TLD), который является XML документом, описывающим теги библиотеки и обработчики тегов. До появления JSP 2.0 в качестве обработчика тега мог выступать только класс Java, реализующий функциональность отдельного тега. JSP 2.0 позволяет использовать и JSP-синтаксис для создания пользовательских тегов. Теперь дизайнеры сами могут инкапсулировать повторяющийся код в отдельных тегах, не прибегая к услугам программистов. Кроме того, для программистов JSP 2.0 предоставляет новый набор классов для создания пользовательских тегов

Прежде всего, в объектную модель, предназначенную для создания тегов, добавлен интерфейс JspTag. Этот интерфейс является родительским для интерфейсов Tag и SimpleTag, представляющих собой соответственно вершины иерархий классов для создания тегов в предыдущих версиях JSP и в JSP 2.0.

Интерфейс Tag описывает простейший обработчик тегов, который никак не обрабатывает свое содержимое. Интерфейс Tag в основном определяет протокол вызова между классом, реализующим страницу JSP, и классом, реализующим тег. У него есть два потомка – это интерфейсы IterationTag и BodyTag. Интерфейс IterationTag расширяет интерфейс Tag, позволяя вычислять (в данном случае под вычислением понимается обработка содержимого тега – так как его обрабатывает механизм JSP) свое содержимое произвольное количество раз. Для этого, дополнительно к методам doStartTag() и doEndTag(), IterationTag определяет метод doAfterBody(), который вызывается каждый раз после вычисления содержимого тега и определяет, нужно ли обрабатывать содержимое еще раз или нет. Интерфейс BodyTag расширяет интерфейс IterationTag, позволяя не только вычислять, но и управлять своим содержимым. Например, можно распечатать содержимое тега или произвести с ним любую другую операцию.

На интерфейсах IterationTag и BodyTag основывались классы для создания пользовательских тегов в предыдущих версиях JSP – TagSupport и BodyTagSupport. Для создания пользовательского тега нужно просто наследовать от какого-нибудь из этих классов и переопределить некоторые их методы.

Интерфейс SimpleTag определяет один-единственный метод для обработки тега – doTag(), и в этом его главное отличие от интерфейса Tag, который определяет два метода для обработки начала и окончания тега – doStartTag() и doEndTag(). Для управления содержимым тега используется метод setJspBody(). Контейнер вызывает метод setJspBody(), передавая ему в качестве параметра объект типа JspFragment, который представляет собой содержимое тега. Обработчик тега также может и вычислять содержимое тега произвольное количество раз, вызывая метод invoke() класса JspFragment. Класс SimpleTagSupport реализует интерфейс SimpleTag. Чтобы создать пользовательский тег, нужно наследовать от класса SimpleTagSupport.

Дополнительно к классам для реализации пользовательских тегов JSP 2.0 определяет класс TagAdapter. TagAdapter представляет собой оболочку класса SimpleTag так, чтобы он мог использоваться согласно интерфейсу Tag. Это нужно, чтобы совместно использовать пользовательские теги, построенные на основе интерфейса SimpleTag, и пользовательские теги, построенные на основе интерфейса Tag. Например, поскольку SimpleTag не реализует интерфейс Tag, и метод Tag.setParent() принимает только классы, реализующие интерфейс Tag, обычный пользовательский тег, реализующий интерфейс Tag, не может включать в себя пользовательский тег, реализующий интерфейс SimpleTag. Чтобы реализовать такую возможность, создается адаптер – объект типа TagAdapter, представляющий собой оболочку для объекта типа SimpleTag. Методу Tag.setParent() передается именно этот адаптер. Обработчик пользовательского тега может вызывать метод getAdaptee(), чтобы получить объект, реализующий SimpleTag.

Поскольку возможность реализации пользовательских тегов в JSP существует уже довольно давно (начиная с версии JSP 1.1), на эту тему написано много работ, например [7], и, кроме того, реализация пользовательских тегов на основе интерфейса SimpleTag принципиально не отличается от реализации пользовательских тегов на основе интерфейса Tag, то в этой статье мы не будем рассматривать эту возможность. Давайте лучше посмотрим, как использовать JSP-синтаксис для создания пользовательских тегов.

Следующий пример демонстрирует использование JSP-тега, который выводит приветствие выбранным цветом.

Листинг 11. JSP-тег, выводящий приветствие
различными цветами (файл hello.tag).

<%@ attribute name="color" %>
<P> 
  Привет, <jsp:doBody/>
</P>

В листинге 11 директива <%@ attribute name="color" %> сообщает о том, что у тега есть атрибут – color. Тег <jsp:doBody/> используется для включения содержимого тега на свое место.

Листинг 12. Использование JSP-тега, выводящего
приветствие различными цветами (файл hello.jsp).

<%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %>
<HTML>
<BODY>
  Результат обработки JSP-тега: 
  <tags:hello color="#ff0000">
    JSP 2.0 Technology.
  </tags:hello>
</BODY>
</HTML>

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

Заключение

Java Server Pages 2.0 является значительным шагом вперед по сравнению с предыдущими версиями JSP. В технологию JSP были добавлены многие новые возможности, отвечающие духу времени и позволяющие успешно конкурировать с другими средствами, предназначенными для создания динамических страниц в World Wide Web. К основным отличительным характеристикам JSP 2.0 можно отнести язык выражений, стандартную библиотеку тегов и новый механизм для создания пользовательских тегов. Все это делает Java Server Pages 2.0 практически самой мощной технологией по сравнению с другими подходами для создания динамических страниц на платформе Java.

Ссылки

[1] JavaServer Pages Specification 2.0 – Proposed Final Draft 3.
http://java.sun.com/products/jsp/download.html

[2] Java Servlet 2.4 - Proposed Final Draft 3.

http://java.sun.com/products/servlet/download.html 

[3] Jason Hunter, “Servlet 2.4: What's in store”.

http://www.javaworld.com/javaworld/jw-03-2003/jw-0328-servlet.html 

[4] The J2EE 1.4 Tutorial.

http://java.sun.com/j2ee/1.4/docs/tutorial/doc/index.html

[5] Qusay H. Mahmoud, “Developing Web Applications With JavaServer Pages 2.0”.

http://developer.java.sun.com/developer/technicalArticles/javaserverpages/JSP20/   

[6] Mark Roth, “JSP 2.0 Technology”.

http://www.sys-con.com/java/articlea.cfm?id=2107 

[7] Михеев Петр. Подходы к созданию шаблонов web-страниц в серверных Java-приложениях. “Технология клиент-сервер”, № 1, 2003, с. 32 –40.

 


Copyright © 1994-2016 ООО "К-Пресс"