« Тестируй веб-сайты вместе с Badboy. Часть 3 | Тестируй веб-сайты вместе с Jmeter » |
JSTL: Шаблоны для разработки веб-приложений в java. Часть 1
Эта статья является логическим развитием материалов посвященных средствам отображения информации (слой View в ставшей уже классической связке Model-View-Controller). Чтобы вы понимали место, которое занимают JSTL и Velocity нужно рассказать о Страшной Ошибке постигшей разработчиков jsp. Как вы наверняка слышали, много-много лет назад java-программисты хотевшие создать веб-приложение не имели в своем распоряжении ничего кроме сервлетов. Сервлет это был класс со специальным методом (doGet или doPost), который вызывался из браузера и должен был сгенерировать html-страницу. Очевидно, что сначала нужно было на основании пришедших от клиента данных (параметры ссылки или содержимое полей формы) выполнить расчет какой-то информации, подготовить набор переменных, массивов, списков и, второй шаг, каким-то образом надо это визуализировать, т.е. перемещать html-теги и те самые подготовленные данные. И было это ужасно:OutputStream out = response.getOutputStream();
out.println("<html>");
out.println("<body>");
out.println("<h1> Привет, " + vasyanoFIO + “</h1>”);
out.println("</body>");
out.println("</html>");
<%@ page contentType="text/html;charset=UTF-8" language="java"%>
<body>
<%
String vasuanoFIO = "Бла-бла-бла";
if ( 1 < 2)
vasuanoFIO = "Бум-бам-тарарам";
%>
<h1><%= vasuanoFIO %></h1>
</body>
public class HelloMachineBean {
public String fio;
public String age;
public String sex;
public String getFio() {
return fio;
}
public void setFio(String fio) {
this.fio = fio;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getResult (){
if ("male".equalsIgnoreCase(sex))
return "Это дядя " + fio + " его возраст "+age;
return "Это тетя "+fio+ " ее возраст "+age;
}
}
// создаем объект Логики, указываем его имя и java-класс
<jsp:useBean id="helloMachine" class="testi.HelloMachineBean" />
// теперь начинаем заполнять значениями поля этого класса-бина, синтаксис
// property=”*” означает, что все пришедшие параметры запроса должны быть помещены внутрь bean-а.
<jsp:setProperty name="helloMachine" property="*" />
// а вот еще вариант, когда указывается конкретное имя свойства, которое нужно заполнить информацией и имя поля из http-запроса
<jsp:setProperty name="helloMachine" property="fio" param="fio" />
// в конце-концов, можно присвоить атрибуту значение в виде константы
<jsp:setProperty name="helloMachine" property="sex" value="female" />
// а теперь использование, как будто бы в составе класса helloMachine есть свойство result.
// на самом деле для обращения к свойствам используются методы getter-ы,
// так что фактическое существование поля класса с именем result не существенно.
<h2>
Hello <jsp:getProperty name="helloMachine" property="result" />
</h2>
<%
If ( bla-bla)
…
Else
…
%>
<%
String vasuanoFIO = "Нет. Ты не сможешь победить силы Зла";
if ( 1 < 2)
vasuanoFIO = "Твоя борьба против каши в JSP обречена на провал, Ха-ха-ха";
%>
<showcalendar />
<mailsend to=vasyano@mail.ru />
<my:if test=”bla == bla”>
Bla-bla-bla
</my:if>
1. Загрузить с сайта архив с библиотекой jstl (я использую версию 1.2). 2. Добавить эту библиотеку в папку WEB-INF/lib вашего веб-приложения. 3. Подключить в начале jsp-файла с помощью специальных директив taglib те библиотеки тегов, которые хотите использовать:Основные теги позволяющие делать циклы, условия, выводить информацию на экран:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/xml" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/sql" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head><title>Simple jsp page</title></head>
<body>
<c:out value="hello, Vasuano" />
</body>
</html>
<c:out value="12+56*2" />
<c:out value="${12+56*2}" />
<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 |
header | В этом объекте хранится информация об http-заголовках которые были переданы от браузера клиента вашему веб-серверу. |
headerValues | Список значений http-заголовков. |
initParam | Конфигурационные параметры, указанные для вашей страницы, сервлета в файле web.xml |
cookie | Список переменных помещенных внутрь cookie. |
pageContext | Ссылка на объект pageContext (см. описание служебных объектов автоматически создаваемых внутри jsp-страницы). |
<c:out value="${param.fio}" />
<c:out value="${param['fio']}" />
<c:out value="${param.fio}" default="NO DATA" escapeXml="true" />
Научившись выводить переменные самое время задуматься над тем как эти переменные создавать. Помимо очевидных вариантов: из параметров запроса, из объекта java bean с логикой, из сессии есть еще способ самим внутри страницы положить какую-нибудь переменную внутрь одного и указанных выше контекстов. Для этого мы используем тег c:set. В качестве его атрибутов указывается название контекста, куда мы хотим положить переменную, имя переменной и значение:
<c:set var="vasyano" scope="session" value="Вася Тапкин" />
<c:set var="petyano" scope="page">
Петька Козлов на странице
</c:set>
<c:set var="petyano" scope="request">
Петька Козлов в запросе
</c:set>
<c:set var="petyano" scope="session">
Петька Козлов в сессии
</c:set>
<c:set var="petyano" scope="application">
Петька Козлов в приложении
</c:set>
vasyano: <c:out value="${sessionScope.vasyano}" />
<br />
petyano: <c:out value="${petyano}" />
Есть еще один вариант синтакиса оператора c:set, когда нужно установить значение свойства некоторого java-bean внедренного на страницу.
<c:set target="${helloMachine}" property="fio" value="Ленка Слонова" />
<c:out value="${helloMachine.fio}" />
<c:set var="fio" scope="session" value="Vasyano Petrovno" />
fio1 = <c:out value="${fio}" />
<c:remove var="fio" />
fio2 = <c:out value="${fio}" />
<c:if test="${param.age gt 12}">
Возраст более 12 лет
</c:if>
<c:if test="${param.age lt 25}">
Возраст менее 25 лет
</c:if>
<strong>eq</strong> – проверка на равенство <strong>ne</strong> – проверка на неравенство <strong>lt</strong> – строго менее чем <strong>gt</strong> – строго более чем <strong>le</strong> – меньше либо равно чему-то <strong>ge</strong> – больше или равно чему-тоУ тега if есть несколько необязательных атрибутов, которые возможно пригодятся вам, чтобы не записывать повторяющиеся выражения. Так если указан атрибут var, то в эту переменную будет записан результат вычисления условия (атрибута test). Куда именно будет положена эта переменная (в какой контекст) задается атрибутом scope.
<c:if test="${param.age gt 12}" var="if_less_12">
Возраст более 12 лет
</c:if>
<c:if test="${if_less_12}">
Еще раз повторяю: Возраст более 12 лет
</c:if>
<c:choose>
<c:when test="${param.age lt 10}">
Возраст менее 10 лет
</c:when>
<c:when test="${param.age lt 20}">
Возраст в отрезке от 10 до 20 лет
</c:when>
<c:otherwise>
Срочно пройдите на процедуру усыпления
</c:otherwise>
</c:choose>
Прежде всего, я ввел в состав описанного выше класса HelloMachineBean несколько новых методов (как-бы-настоящий свойств) возвращающих массив элементов и список элементов.
public List< String> getFriendsAsList (){
return Arrays.asList(getFriendsAsArray ());
}
public String[] getFriendsAsArray(){
return new String[]{"Васька", "Петька", "Ленка"};
}
<c:set var="friends" value="${helloMachine.friendsAsArray}" />
<c:set var="friends2" value="${helloMachine.friendsAsList}" />
<c:forEach items="${friends}" var="friend">
<h2>
<c:out value="${friend}"/>
</h2>
</c:forEach>
<c:forEach items="${friends2}" var="friend">
<h3>
<c:out value="${friend}"/>
</h3>
</c:forEach>
Второй вариант цикла ForEach предназначен для прохода по целым числам в отрезке от X до Y, например, так:
<c:forEach var="friend_i" begin="0" end="2">
<h5>
<c:out value="${friend_i}"/> = <c:out value="${friends[friend_i]}"/>
</h5>
<h4>
<c:out value="${friend_i}"/> = <c:out value="${friends2[friend_i]}"/>
</h4>
</c:forEach>
Еще один атрибут для тега forEach – это step. Его назначение управлять величиной шага, с которым выполняется проход по элементам массива. Обратите внимание, что в следующем примере атрибут step умеет корректно работать не только, когда цикл перебирает цифры в отрезке от X до Y, но и когда перебирается содержимое некоторой коллекции элементов.
<c:forEach items="${friends}" var="friend" step="2">
<h2>
<c:out value="${friend}"/>
</h2>
</c:forEach>
<c:forEach var="friend_i" begin="0" end="2" step="2">
<h5>
<c:out value="${friend_i}"/> = <c:out value="${friends[friend_i]}"/>
</h5>
<h4>
<c:out value="${friend_i}"/> = <c:out value="${friends2[friend_i]}"/>
</h4>
</c:forEach>
<c:forEach var="friend_i" begin="0" end="2" step="2" varStatus=”friendStatus”>
<h5>
friend_i = <c:out value="${friendStatus}"/>*
<c:out value="${friend_i}"/> = <c:out value="${friends[friend_i]}"/>
</h5>
</c:forEach>
<c:forEach items="${friends}" var="friend" step="1" varStatus="friendStatus">
Status:
index=<c:out value="${friendStatus.index}"/><br />
count=<c:out value="${friendStatus.count}"/><br />
first=<c:out value="${friendStatus.first}"/><br />
last=<c:out value="${friendStatus.last}"/><br />
step=<c:out value="${friendStatus.step}"/><br />
<h2>
<c:out value="${friend}"/>
</h2>
</c:forEach>
<c:set var="str" value="Гравитон Фотон Бозон Мюон" />
<c:forTokens items="${str}" delims=" " var="token" begin="1" varStatus="tokenStatus" step="1">
index=<c:out value="${tokenStatus.index}"/><br />
count=<c:out value="${tokenStatus.count}"/><br />
first=<c:out value="${tokenStatus.first}"/><br />
last=<c:out value="${tokenStatus.last}"/><br />
step=<c:out value="${tokenStatus.step}"/><br />
<h2> <c:out value="${token}"/> </h2>
</c:forTokens>
<c:set var="str" value="Гравитон,Фотон.Бозон Мюон" />
<c:forTokens items="${str}" delims=" ,." var="token" begin="0" varStatus="tokenStatus" step="1">
<h2>
<c:out value="${token}"/>
</h2>
</c:forTokens>
<c:import url="heading.html" />
Включаемая страница (footer.jsp):
<%@ page import="java.util.Date" %>
<%@ page contentType="text/html;charset=UTF-8" language="java"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%
out.write("Сложные расчеты по внедрению информации внутрь ... " + new Date());
%>
<h1>
<c:out value="${externalVar}" />
</h1>
А теперь пример кода главной страницы (обратите внимание на то, что я указал область действия переменной externalVar – значит, что она будет доступна всем страницам обслуживающим данный запрос):
<c:set var="externalVar" value="Hello from Outer page" scope="request"/>
<c:import url="footer.jsp" />
Естественно, что на включаемой странице будут доступны и те переменные, которые были переданы главному скрипту из html-формы.
Количество атрибутов управляющих поведением тега import гораздо больше, чем один адрес включаемого документа. Начнем с попытки включить как вложенную, страницу содержащую русские буквы. И конечно же мы увидели кракозюбры. Дело в том, что кодировка включаемого документа по-умолчанию рассматривается как ISO8859-1. Для того чтобы явно указать кодировку делайте так:
<c:import url="heading.html" charEncoding="utf-8" />
<c:import url="heading.html" charEncoding="utf-8" var="inner_c" scope="request" />
<c:out value="${requestScope.inner_c}" escapeXml="true" />
Вторым наиболее часто используемым приемом сборки странички из кусочков, является перенаправление на другой адрес, для этого используем тег redirect, в качестве атрибута которого укажем url (есть еще атрибут var и scope, но глубокого смысла в их существовании я не нашел).
<c:redirect url="heading.html">
<c:param name="fio" value="${helloMachine.fio}" />
</c:redirect>
<c:import url="heading.jsp" charEncoding="utf-8">
<c:param name="fio" value="${helloMachine.fio}" />
</c:import>
<c:url value="/a3.jsp" />
/Имя-Моего-Контекста/a3.jsp
« Тестируй веб-сайты вместе с Badboy. Часть 3 | Тестируй веб-сайты вместе с Jmeter » |