JSTL: Шаблоны для разработки веб-приложений в java. Часть 1

June 9, 2008

Эта статья является логическим развитием материалов посвященных средствам отображения информации (слой View в ставшей уже классической связке Model-View-Controller). Чтобы вы понимали место, которое занимают JSTL и Velocity нужно рассказать о Страшной Ошибке постигшей разработчиков jsp. Как вы наверняка слышали, много-много лет назад java-программисты хотевшие создать веб-приложение не имели в своем распоряжении ничего кроме сервлетов. Сервлет это был класс со специальным методом (doGet или doPost), который вызывался из браузера и должен был сгенерировать html-страницу. Очевидно, что сначала нужно было на основании пришедших от клиента данных (параметры ссылки или содержимое полей формы) выполнить расчет какой-то информации, подготовить набор переменных, массивов, списков и, второй шаг, каким-то образом надо это визуализировать, т.е. перемещать html-теги и те самые подготовленные данные. И было это ужасно:
  1. OutputStream out = response.getOutputStream();
  2. out.println("<html>");
  3. out.println("<body>");
  4. out.println("<h1> Привет, " +  vasyanoFIO +</h1>);
  5. out.println("</body>");
  6. out.println("</html>");
Потом придумали jsp. Сделали его, похоже, для того, чтобы пишущие на asp (тогда еще не было asp.net) или php, не могли тыкать в java-программистов пальцами и тихо хихикать в кулачок. Действительно типовая страница характеризуется значительным превышением количества html-кода над динамической информацией (взятой из файлов, баз данных, вычисленных веб-приложением). Поэтому логично было не встраивать html-код внутрь java-кода, а наоборот встроить java-код внутрь html-шаблона.
  1. <%@ page contentType="text/html;charset=UTF-8" language="java"%>
  2. <body>
  3. <%
  4.     String vasuanoFIO = "Бла-бла-бла";
  5.     if ( 1 < 2)
  6.         vasuanoFIO = "Бум-бам-тарарам";
  7. %>
  8. <h1><%= vasuanoFIO %></h1>
  9. </body>
Выглядит ужасно. Конечно, если нужно сделать одну страничку и быстро, то это наилучший выход. Вот только “пихать” логику (в примере сравнение, расчет чему же будет равна переменная vasyanoFIO) это не кошерно. По мере роста количества расчетов и усложнения верстки код превращается в винегрет. Большой код не читаем. Код, где и html и логика, не читаем вдвойне. Очень скоро будет продублирована часть логики, и размазана по тысяче файлов, так что понять, откуда что взялось не возможно. Где-то тут еще “повесился” дизайнер, когда понял то, сколько ему нужно потратить здоровья, для того чтобы заменить размер шрифта заголовка h1 на h2. Вообще-то в стандарте jsp рекомендуемой практикой является вынос логики обработки запроса в объект bean. Например, задачу с приветствиями можно оформить в виде отдельного класса, добавить к нему поля (управляющие расчетом итогового значения).
  1. public class HelloMachineBean {
  2.     public String fio;
  3.     public String age;
  4.     public String sex;
  5.  
  6.     public String getFio() {
  7.         return fio;
  8.     }
  9.  
  10.     public void setFio(String fio) {
  11.         this.fio = fio;
  12.     }
  13.  
  14.     public String getAge() {
  15.         return age;
  16.     }
  17.  
  18.     public void setAge(String age) {
  19.         this.age = age;
  20.     }
  21.  
  22.     public String getSex() {
  23.         return sex;
  24.     }
  25.  
  26.     public void setSex(String sex) {
  27.         this.sex = sex;
  28.     }
  29.  
  30.     public String getResult (){
  31.         if ("male".equalsIgnoreCase(sex))
  32.             return "Это дядя " + fio + " его возраст "+age;
  33.         return "Это тетя "+fio+ " ее возраст "+age;
  34.     }
  35. }
Обратите внимание на метод getResult – именно он содержит логику расчета сообщения, которое будет показано пользователю. Теперь нужно создать jsp-файл, в котором создается объект bean, наполняются его свойства значениями и в нужном месте извлекается расчет значения (getResult).
  1. // создаем объект Логики, указываем его имя и java-класс 
  2. <jsp:useBean id="helloMachine" class="testi.HelloMachineBean" />
  3. // теперь начинаем заполнять значениями поля этого класса-бина, синтаксис
  4. // property=”*” означает, что все пришедшие параметры запроса должны быть помещены внутрь bean-а.
  5. <jsp:setProperty name="helloMachine" property="*" />
  6. // а вот еще вариант, когда указывается конкретное имя свойства, которое нужно заполнить информацией и имя поля из http-запроса
  7. <jsp:setProperty name="helloMachine" property="fio" param="fio" />
  8. // в конце-концов, можно присвоить атрибуту значение в виде константы
  9. <jsp:setProperty name="helloMachine" property="sex" value="female" />
  10. // а теперь использование, как будто бы в составе класса helloMachine есть свойство result.
  11. // на самом деле для обращения к свойствам используются методы getter-ы, 
  12. // так что фактическое существование поля класса с именем result не существенно.
  13. <h2>
  14.     Hello <jsp:getProperty name="helloMachine" property="result" />
  15. </h2>
Такой подход имеет слишком много подводных камней (я не про то, что инициализация всех нужных полей должна быть выполнена до использования bean-а, или то, что поступающие на вход данные нужно проверить, экранировать и прочая и прочая – все это решается без проблем. Например, если создать jsp-страницу как наследник от некоторого класса сервлета, который выполняет столь нужные но рутинные действия. Основных проблем две: программисты могут (а значат, будут) смешивать java-код и html, если бы были запрещены такие теги как:
  1. <% 
  2.  If ( bla-bla)
  3.  Else
  4. %>
То у нас не было бы никакого выбора, и мы должны были бы уже писать так код, чтобы четко разделить логику (bean-ы) и внешний вид (jsp). Но тут подкралась другая проблема: реальный сайт оперирует данными не только скалярными (переменная со строковым значением fio), но и списками, массивами. Так что для их обработки нам нужны циклы, условные конструкции (если сумма денег на счету меньше чем 100 баков, то вывести ее красной, иначе зеленой). Можно было бы перенести циклы и условия внутрь bean-ов, но фактически это означает, что мы должны будем внутрь этих bean-ов перетащить также и генерацию html-кода (а к чему приводит такое решение, мы уже видели на примере сервлетов). В общем, как ни крутись, но хоть какая-то примитивная обработка условий и циклов на странице должна быть, иначе мы получим гораздо большее количество проблем.
  1. <%
  2.     String vasuanoFIO = "Нет. Ты не сможешь победить силы Зла";
  3.     if ( 1 < 2)
  4.         vasuanoFIO = "Твоя борьба против каши в JSP обречена на провал, Ха-ха-ха";
  5. %>
На далеком диком западе, в стране непуганых дизайнеров, существует страшное поверье, что если дизайнер увидит код java в странице jsp, то он сойдет с ума и ему не поможет даже волшебное зелье “Гамбургер с кока-колой”. Поэтому придумали пользовательские теги. Т.е. вы создаете java-класс, который умеет делать что-то, и внедряете его в страницу, примерно, так (детали как создавать теги, описывать их с помощью tld-файлов я пропускаю, они есть в любом уважающем себя учебнике):
  1. <showcalendar />
  2. <mailsend to=vasyano@mail.ru />
Это очень удобно, ведь теперь дизайнер не пугается тегов, он знает, что что-то заключенное в угловые скобочки никак не может ему навредить (собственно, он может не отличать тег H1 от придуманного вами тега ). И началось время, когда все стали создавать собственные библиотеки “очень нужных” тегов. Признаюсь, я сам на начальных этапах занимался изобретением тегов вида:
  1. <my:if test=”bla == bla”>
  2.    Bla-bla-bla
  3. </my:if>
В любом случае такой подход не способствовал накоплению знаний, библиотек тегов было слишком много, их делали все кому ни лень, страдало качество. Так что появление каких-то стандартов, принятых наборов тегов и их качественная реализация с последующим клеймением позором тех, кто продолжал использовать собственные “уникально-неповторимые” поделки была предопределена. И так появились JSTL – тема сегодняшнего материала.
 1. Загрузить с сайта  архив с библиотекой jstl (я использую версию 1.2).
 2. Добавить эту библиотеку в папку WEB-INF/lib вашего веб-приложения.
 3. Подключить в начале jsp-файла с помощью специальных директив taglib те библиотеки тегов, которые хотите использовать:
Основные теги позволяющие делать циклы, условия, выводить информацию на экран:
  1. <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
Теги для работы с xml-документами:
  1. <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/xml" %>
Теги для работы с базами данных (Ну зачем вы это сюда вставили …):
  1. <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/sql" %>
Теги для форматирования информации перед выводом и работы с i10n и i18n:
  1. <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/fmt" %>
Сначала разберем теги из библиотеки http://java.sun.com/jsp/jstl/core. Начнем мы с самого простого: нужно вывести на экран браузера некоторый текст.
  1. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
  2.  
  3. <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
  4.  
  5. <html>
  6.   <head><title>Simple jsp page</title></head>
  7.   <body>
  8.     <c:out value="hello, Vasuano" />
  9.   </body>
  10. </html>
Вот, правда, смысла в такой конструкции мало, ведь простой, статический текст мы можем выводить и без использования тегов jstl. А вот если нужно вывести на экран какие-то динамически рассчитываемые величины, то c:out уже нужен:
  1. <c:out value="12+56*2" />
Запустили, проверили, что получилось? Да ничего не получилось: на экран вывелся сам текст формулы, а не результат вычисления 12+56*2. Дело в том, что если мы хотим вывести некоторую информацию вычисляемую “на-лету”, то эту часть значения атрибута value тега c:out нужно поместить внутрь фигурных скобок.
  1. <c:out value="${12+56*2}" />
Уже лучше: на экране мы увидели цифру 124. Однако вывод на экран просто статических формул не слишком частое занятие, а вот вывести значение полученной от пользователя или html-формы переменной уже лучше:
  1. <c:out value="${param.fio}" />
Теперь если я введу в адресную строку браузера нечто вроде:
  http://center:8080/velo/a2.jsp?fio=%C2%E0%F1%FF (Эти кракозюбры – слово ВАСЯ)
То увижу на страницу … какую-то белиберду.
 Âàñÿ
Собственно, другого не ожидалось: для того чтобы заставить tomcat нормально раскодировать входящие переменные нужно еще постараться. Как, что, и почему я писал в другой своей статье посвященной Русским буквами и java.

Внимание, перед тем как написать имя переменной (fio) я должен указать контекст (место, где нужно искать эту переменную). Есть список предопределенных контекстов:
Контекст Комментарий
pageScope Контекст страницы (т.е. переменные объявленные на этой странице и доступные только для этой страницы).
requestScope Доступ к таким переменным имеют все страницы, сервлеты обслуживающие один, текущий, вот этот самый, запрос пользователя.
sessionScope Доступ к переменным сохраняется на протяжении всего сеанса пользователя (пока не закроет браузер или не истечет предельное время бездействия).
applicationScope Доступ к переменным сохраняется изо всех страниц размещенных внутри веб-приложения (самый глобальный контекст).
param В этом контексте находятся все переменные, полученные страницей от пользователя либо как параметры адресной строки, либо как поля html-формы.
paramValues Список значений тех переменных, которые были переданы в страницу пользователем, правда, формат отличен от предыдущего случая. Если там param фактически имел тип HashMap, то здесь HashMap.
header В этом объекте хранится информация об http-заголовках которые были переданы от браузера клиента вашему веб-серверу.
headerValues Список значений http-заголовков.
initParam Конфигурационные параметры, указанные для вашей страницы, сервлета в файле web.xml
cookie Список переменных помещенных внутрь cookie.
pageContext Ссылка на объект pageContext (см. описание служебных объектов автоматически создаваемых внутри jsp-страницы).
Для того чтобы обратиться к переменной находящейся внутри какого-нибудь из контекстов нужно написать имя контекста, затем символ точки, и наконец имя переменной. Возможен и альтернативный синтаксис:
  1. <c:out value="${param.fio}" />
  2. <c:out value="${param['fio']}" />
У тега c:out есть еще несколько атрибутов:
  1. <c:out value="${param.fio}" default="NO DATA" escapeXml="true" />
В этом примере, в случае если на вход странице не была подана переменная fio, то будет выведена фраза “NO DATA”. Атрибут же escapeXML служит для того чтобы экранировать (заменить специальные символы xml: знаки больше, меньше, кавычки …) на их entities (т.е. < > …)

Научившись выводить переменные самое время задуматься над тем как эти переменные создавать. Помимо очевидных вариантов: из параметров запроса, из объекта java bean с логикой, из сессии есть еще способ самим внутри страницы положить какую-нибудь переменную внутрь одного и указанных выше контекстов. Для этого мы используем тег c:set. В качестве его атрибутов указывается название контекста, куда мы хотим положить переменную, имя переменной и значение:
  1. <c:set var="vasyano" scope="session" value="Вася Тапкин" />
  2.  
  3.   <c:set var="petyano" scope="page">
  4.       Петька Козлов на странице
  5.   </c:set>
  6.   <c:set var="petyano" scope="request">
  7.       Петька Козлов в запросе
  8.   </c:set>
  9.   <c:set var="petyano" scope="session">
  10.       Петька Козлов в сессии
  11.   </c:set>
  12.   <c:set var="petyano" scope="application">
  13.       Петька Козлов в приложении
  14.   </c:set>
  15.   vasyano: <c:out value="${sessionScope.vasyano}" />
  16.   <br />
  17.   petyano: <c:out value="${petyano}" />
Здесь я показываю несколько хитрых моментов в работе jstl. Во-первых, обратите внимание, как отличаются имена контекстов при операции положить переменную (set) и операции извлечь переменную (out). В первом случае я должен писать слово session, а во втором случае – sessionScope. Также посмотрите, что я создал переменную petyan сразу во всех четырех контекстах. Когда же я вывожу эту переменную на экран, то название контекста, к которому принадлежит переменная, я не указал. В этом случае работает правило поиска нужного контекста: ищем переменную внутри pageScope, если не найдено внутри requestScope, если опять не найдено внутри sessionScope и, наконец, внутри applicationScope.

Есть еще один вариант синтакиса оператора c:set, когда нужно установить значение свойства некоторого java-bean внедренного на страницу.
  1. <c:set target="${helloMachine}" property="fio" value="Ленка Слонова" />
  2. <c:out value="${helloMachine.fio}" />
Для удаления переменных (на самом деле простого присвоения имя значения null) используется оператор remove, например, так:
  1. <c:set var="fio" scope="session" value="Vasyano Petrovno" />
  2.  fio1 = <c:out value="${fio}" />
  3.  <c:remove var="fio" />
  4.  fio2 = <c:out value="${fio}" />
Вывод переменных без предварительного их анализа, без условных операторов малополезен. Так что нам нужны операторы if и switch. Оператор if не слишком похож на своих больших братьев в java и других языках: нельзя задать ни ветку else, ни ветку elseif (elif), можно только проверить некоторое условие и если оно истинно, то сделать что-то:
  1. <c:if test="${param.age gt 12}">
  2.       Возраст более 12 лет
  3.   </c:if>
  4.  
  5.   <c:if test="${param.age lt 25}">
  6.       Возраст менее 25 лет
  7.   </c:if>
Обратите внимание на то, что я в условии использую не знаки “<” или “>”, а слова “gt” и “lt”, это необходимо т.к. в противном случае мы нарушим правила записи xml-документов. Еще есть такие слова-операторы:
 <strong>eq</strong> – проверка на равенство
 <strong>ne</strong> – проверка на неравенство
 <strong>lt</strong> – строго менее чем
 <strong>gt</strong> – строго более чем
 <strong>le</strong> – меньше либо равно чему-то
 <strong>ge</strong> – больше или равно чему-то
У тега if есть несколько необязательных атрибутов, которые возможно пригодятся вам, чтобы не записывать повторяющиеся выражения. Так если указан атрибут var, то в эту переменную будет записан результат вычисления условия (атрибута test). Куда именно будет положена эта переменная (в какой контекст) задается атрибутом scope.
  1. <c:if test="${param.age gt 12}" var="if_less_12">
  2.       Возраст более 12 лет
  3.   </c:if>
  4.  
  5.   <c:if test="${if_less_12}">
  6.       Еще раз повторяю: Возраст более 12 лет
  7.   </c:if>
В случае если нам нужно проверить несколько условий, то оператор if – не самое лучшее решение, здесь нужно использовать тег choose -> when -> otherwise:
  1. <c:choose>
  2.       <c:when test="${param.age lt 10}">
  3.           Возраст менее 10 лет
  4.       </c:when>
  5.       <c:when test="${param.age lt 20}">
  6.           Возраст в отрезке от 10 до 20 лет
  7.       </c:when>
  8.       <c:otherwise>
  9.           Срочно пройдите на процедуру усыпления
  10.       </c:otherwise>
  11.   </c:choose>
Теперь разберемся как работать в JSTL с циклами. Есть две разновидности циклов: для прохода по элементам некоторого списка (для прохода всех чисел в отрезке ОТ и ДО). И вторая разновидность служит для прохода по списку token-ов (частей, на которые была разбита строка на основании некоторого символа разделителя). Первый тег “c:ForEach” имеет целых 6 атрибутов управляющих его работой, но среди них нет ни одного обязательного. Все дело в том, что c:ForEach предназначен и для прохода по элементам некоторого списка, например, так:

Прежде всего, я ввел в состав описанного выше класса HelloMachineBean несколько новых методов (как-бы-настоящий свойств) возвращающих массив элементов и список элементов.
  1. public List< String> getFriendsAsList (){
  2.         return Arrays.asList(getFriendsAsArray ());
  3.     }
  4.  
  5.     public String[] getFriendsAsArray(){
  6.         return new String[]{"Васька", "Петька", "Ленка"};
  7.     }
Теперь пример использования этих данных внутри цикла:
  1. <c:set var="friends" value="${helloMachine.friendsAsArray}" />
  2.  <c:set var="friends2" value="${helloMachine.friendsAsList}" />
  3.  
  4.   <c:forEach items="${friends}" var="friend">
  5.       <h2>
  6.           <c:out value="${friend}"/>
  7.       </h2>
  8.   </c:forEach>
  9.  
  10.  <c:forEach items="${friends2}" var="friend">
  11.       <h3>
  12.           <c:out value="${friend}"/>
  13.       </h3>
  14.   </c:forEach>
Как видите, мне пришлось задействовать два атрибута тега ForEach – это items играющий роль источника данных и var – переменная, в которую будет последовательно помещаться элементы массива/списка.

Второй вариант цикла ForEach предназначен для прохода по целым числам в отрезке от X до Y, например, так:
  1. <c:forEach var="friend_i" begin="0" end="2">
  2.       <h5>
  3.           <c:out value="${friend_i}"/> =  <c:out value="${friends[friend_i]}"/>
  4.       </h5>
  5.      <h4>
  6.          <c:out value="${friend_i}"/> =  <c:out value="${friends2[friend_i]}"/>
  7.      </h4>
  8.   </c:forEach>
Обратите внимание на то, что я могу с помощью индекса (квадратных скобок) обращаться к элементами не только массива, но и списка.

Еще один атрибут для тега forEach – это step. Его назначение управлять величиной шага, с которым выполняется проход по элементам массива. Обратите внимание, что в следующем примере атрибут step умеет корректно работать не только, когда цикл перебирает цифры в отрезке от X до Y, но и когда перебирается содержимое некоторой коллекции элементов.
  1. <c:forEach items="${friends}" var="friend" step="2">
  2.       <h2>
  3.           <c:out value="${friend}"/>
  4.       </h2>
  5.   </c:forEach>
  6.  
  7.  <c:forEach var="friend_i" begin="0" end="2" step="2">
  8.       <h5>
  9.           <c:out value="${friend_i}"/> =  <c:out value="${friends[friend_i]}"/>
  10.       </h5>
  11.      <h4>
  12.          <c:out value="${friend_i}"/> =  <c:out value="${friends2[friend_i]}"/>
  13.      </h4>
  14.   </c:forEach>
И, наконец, последний атрибут для цикла For – varStatus. В эту переменную будет помещена информация о выполняемом цикле и текущем его шаге. Значение переменной разнится в зависимости от того, какая разновидность цикла нами используется, например, когда у нас обычный цикл от X до Y, то значением этой переменной будет текущий индекс:
  1. <c:forEach var="friend_i" begin="0" end="2" step="2" varStatus=”friendStatus”>
  2.       <h5>
  3.           friend_i = <c:out value="${friendStatus}"/>*
  4.           <c:out value="${friend_i}"/> =  <c:out value="${friends[friend_i]}"/>
  5.       </h5>
  6.   </c:forEach>
Если же цикл выполняется по элементам коллекции, то поведение varStatus меняется. Теперь это не число, а сложный объект с информацией о начальном и конечном шаге цикла, об том первый элемент коллекции перебирается или нет, например:
  1. <c:forEach items="${friends}" var="friend" step="1" varStatus="friendStatus">
  2.       Status:
  3.       index=<c:out value="${friendStatus.index}"/><br />
  4.       count=<c:out value="${friendStatus.count}"/><br />
  5.       first=<c:out value="${friendStatus.first}"/><br />
  6.       last=<c:out value="${friendStatus.last}"/><br />
  7.       step=<c:out value="${friendStatus.step}"/><br />
  8.       <h2>
  9.           <c:out value="${friend}"/>
  10.       </h2>
  11.   </c:forEach>
Разобравшись с одной разновидностью цикла (наиболее часто встречающейся и наиболее полезной), перейдем ко второй – forTokens. Этот цикл похож на ранее приведенный forEach: совпадают многие атрибуты тега. Но ключевое отличие в том, что цикл идет по списку лексем, на которые была разбита строка:
  1. <c:set var="str" value="Гравитон Фотон Бозон Мюон" />
  2.   <c:forTokens items="${str}" delims=" " var="token" begin="1" varStatus="tokenStatus" step="1">
  3.       index=<c:out value="${tokenStatus.index}"/><br />
  4.       count=<c:out value="${tokenStatus.count}"/><br />
  5.       first=<c:out value="${tokenStatus.first}"/><br />
  6.       last=<c:out value="${tokenStatus.last}"/><br />
  7.       step=<c:out value="${tokenStatus.step}"/><br />
  8.  
  9.       <h2>     <c:out value="${token}"/>      </h2>
  10.   </c:forTokens>
Итак: строка “Гравитон Фотон Бозон Мюон” была разбита на отдельные подстроки с помощью разделителя delims (пробела). Цикл был начат не с первого элемента “Гравитона”, а со второго “Фотона”. На каждой итерации цикла также выводились сведения о номере этой итерации, признаке первая ли это итерация, а может последняя. Неприятность только в том, что значение переменной count использовать нельзя, эта величина не равна “4”, как ожидалось, а растет по мере изменения цикла (похоже, цикл идет по мере разбора строки, а не после его завершения). Значением атрибута delims может быть не отдельный символ, а некоторое их количество, например, следующий пример идентичен ранее приведенному (значение delims теперь пробел, точка и запятая):
  1. <c:set var="str" value="Гравитон,Фотон.Бозон Мюон" />
  2.   <c:forTokens items="${str}" delims=" ,." var="token" begin="0" varStatus="tokenStatus" step="1">
  3.       <h2>
  4.        <c:out value="${token}"/>
  5.       </h2>
  6.   </c:forTokens>
Очевидно, что написать хороший код страницы в одном файле, без разбиения его на части тяжело. Поэтому в состав JSTL входит тег, позволяющий в один jsp-файл включить другой jsp-файл, и называется этот тег import. Тег этот очень гибкий, в самом простом случае в страницу можно включить статический кусочек текста в виде html/txt-блока:
  1. <c:import url="heading.html" />
Однако настоящей гибкости мы можем добиться лишь, когда включаемое содержимое является динамическим. Проверим, умеет ли import “включать” в jsp-страницу результат работы другой jsp-страницы:

Включаемая страница (footer.jsp):
  1. <%@ page import="java.util.Date" %>
  2. <%@ page contentType="text/html;charset=UTF-8" language="java"%>
  3. <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
  4.  
  5. <%
  6.     out.write("Сложные расчеты по внедрению информации внутрь ... " + new Date());
  7. %>
  8. <h1>
  9.     <c:out value="${externalVar}" />
  10. </h1>
Обратите внимание, что здесь я обращаюсь и хочу вывести на экран переменную externalVar. Откуда же она взялась?

А теперь пример кода главной страницы (обратите внимание на то, что я указал область действия переменной externalVar – значит, что она будет доступна всем страницам обслуживающим данный запрос):
  1. <c:set var="externalVar" value="Hello from Outer page"  scope="request"/>
  2.    <c:import url="footer.jsp" />
Аналогичным образом переменные можно передавать и из вложенной страницы во внешнюю (не забывайте про контекст переменных).

Естественно, что на включаемой странице будут доступны и те переменные, которые были переданы главному скрипту из html-формы.

Количество атрибутов управляющих поведением тега import гораздо больше, чем один адрес включаемого документа. Начнем с попытки включить как вложенную, страницу содержащую русские буквы. И конечно же мы увидели кракозюбры. Дело в том, что кодировка включаемого документа по-умолчанию рассматривается как ISO8859-1. Для того чтобы явно указать кодировку делайте так:
  1. <c:import url="heading.html" charEncoding="utf-8" />
Содержимое включаемой страницы можно даже не посылать сразу в браузер клиенту, а накопить в какой-то переменной, для последующей обработки, например, так:
  1. <c:import url="heading.html" charEncoding="utf-8" var="inner_c" scope="request" />
  2.     <c:out value="${requestScope.inner_c}" escapeXml="true" />
Последний атрибут для импорта – это varReader. В случае если мы укажем его, то результат вставки страницы не будет ни выведен на экран, ни помещен в строковую переменную var, а будет доступен только при чтении потока varReader.

Вторым наиболее часто используемым приемом сборки странички из кусочков, является перенаправление на другой адрес, для этого используем тег redirect, в качестве атрибута которого укажем url (есть еще атрибут var и scope, но глубокого смысла в их существовании я не нашел).
  1. <c:redirect url="heading.html">
  2.       <c:param name="fio" value="${helloMachine.fio}" />
  3.   </c:redirect>
Тег param можно использовать и при внедрении в страницу “подстраницы”:
  1. <c:import url="heading.jsp" charEncoding="utf-8">
  2.       <c:param name="fio" value="${helloMachine.fio}" />
  3.  </c:import>
Еще один малополезный тег = c:url, он служит для декорирования некоторого адреса, так если я запишу:
  1. <c:url value="/a3.jsp" />
То на страницу будет вставлен адрес включая контекст приложения (собственно говоря, идея контекстов специфична для java, а браузер не может отличить контекст от просто имени папки), например, так:
 /Имя-Моего-Контекста/a3.jsp

Categories: Java