| « Системы управления версиями для программистов и не только. Часть 2 | Как в spring написать валидатор, использующий коды сообщений, и не забыть кого-то из них » |
Jsp теги, новые и улучшенные
Одной из важнейших и, к сожалению, плохо оцененных возможностей jsp являются пользовательские теги. В прошлой своей статье рассказывающей об jsp я упоминал о сложившейся на первых этапах развития jsp ситуации когда все-кому-не-лень создавали собственные теги, зачастую изобретая никому не нужные велосипеды в виде очередного тега IF, FOR и т.д. Таким образом, появление jstl стало важным этапом унификации и дало единую точку применения силы многих тысяч java-разработчиков. Тем не менее, необходимость создания собственных тегов никуда не пропала, а идеи создания сайтов-приложений из множества кусочков, где элементарная единица это не параграф, заголовок или тег span, а нечто более крупное и наделенное долей интеллекта (да я говорю про вычисления, возможность настроить поведение блока с помощью некоторых атрибутов тега). Так вот эта необходимость есть. Вот только создание тегов требует значительных усилий именно java-разработчиков, так что мы не можем делегировать часть ответственности верстальщикам. Ведь им нужны знания tag-библиотек, нужно знать, что и когда возвращать из методов, какие классы нужно наследовать. Кроме того, не решена проблема с переносом внутрь тега html-блоков кода и сложной процедурой тестирования тегов. Ведь для того чтобы перечитать заново определения тегов и обновленный java-код требуется перезапускать контекст веб-приложения и это медленно, дорого, да и просто раздражает. Естественным решением от sun было создать новые теги (новые правила их написания), улучшенные и не повторяющие ошибок предшественников. Фактически для создания таких тегов вам не нужно досконально знать java-tag-api, хватит знаний html, jstl и немного смекалки. Кроме того, процедура установки и тестирования таких тегов значительно облегчилась. Если вам интересно, то давайте перейдем ближе к делу.В папке WEB-INF мы создадим подпапку с именем tags (это имя зарезервировано и менять его не стоит). Именно в эту папку мы будем складывать файлы с описаниями тегов (я уже говорил, что писать код на java мы не будем?). Для лучшего упорядочения можно создать подкаталоги внутри tags соответствующие разным тегам и служащие их логической группировки. Теперь в папке tags создается новый файл с именем HelloTag.tag (расширение tag и это важно). Не знаю в чем вы пишите код, но я пользуюсь intellij idea и крайне доволен тем, что для tag-файлов работает подсветка и мастер подсказок. Теперь надо в этом файле написать директивы, подсказывающие tomcat-у о том как будет себя вести тег, какие у него будут атрибуты, обязательные они или нет. Начну я с самого простого – создам тег, который выводит на экран слово “Hello”. Итак, я создал файл HelloTag.tag и поместил его внутрь каталога /WEB-INF/tags. Содержимое файла очень простое (да, да ничего больше кроме этой строки):
Hello from TagТеперь я возвращаюсь назад в jsp-файл и подключаю в нем новый набор тегов (пока состоящий из одного экземпляра). Используется та же директива что и для подключения “старших братьев” (tag-ов написанных на java) – директива taglib, также указывается атрибут “prefix”, а вот вместо атрибута uri с идентификатором библиотеки, нужно использовать атрибут tagdir и значением которого будет путь к каталогу с тегами:
<%@ taglib tagdir="/WEB-INF/tags" prefix="t" %><%@ taglib tagdir="/WEB-INF/tags/food" prefix="food" %>
<t:HelloTag />
Ура, пример должен заработать.
Пользы от такого тега не много, ну разве что как очередной способ собрать страницу из множества статических кусочков (шапка, футер сайта …). Попробуем передать в тег данные для обработки. Эти данные бывают двух видов: тело тега и атрибуты. В любом случае мы должны будем в самом начале tag-файла указать специальные директивы (что мы принимаем и как этим будем пользоваться). Надо сказать, что это крайне удобно по сравнению со старыми java¬-тегами, когда приходилось поддерживать два раздельных набора файлов – код java и описание тегов виде tld-документа.
<%@ tag import="java.util.Date" %><%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %><%= new Date() %>Hello from Tag
<t:HelloTag message="Привет пользователь" />
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %><%@ attribute name="message" required="true" rtexprvalue="true" type="java.lang.String" %><%@ attribute name="count" required="false" rtexprvalue="true" type="java.lang.Integer" %><c:set var="count" value="${empty count?20:count}" /><h1>Messages</h1>
<c:forEach var="i" begin="1" end="${count}"><p><c:out value="${i}" /> <c:out value="${message}" />
</p></c:forEach>
public void foo (){
try {
((PageContext)getJspContext()).getOut().println (“bla-bla-bla”);
} catch (IOException e) {}
}
Атрибуты, получаемые моим тегом, бывают трех видов: обычные, динамические и вычисляемые пользователем. В первом случае я явно указываю имена этих атрибутов, затем при вызове тега веб-контейнер (tomcat) следит за тем, чтобы переменные были переданы тегу, а перед передачей выполняет вычисление значения атрибута, так что на вход тегу поступают уже готовые для обработки данные. Пример обычных атрибутов был показан выше. Теперь про динамические теги: это такие теги имена которых не фиксированы, фактически когда я вызываю тег то могу указать ему произвольный набор тегов, например:
<x:makeFoo foo=”apple” bar=”orange” tar=”grapes” />
<%@ tag dynamic-attributes="varContainer" %>
<%@ tag dynamic-attributes="varContainer" %><%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %><h1>dynamic variables</h1>
<c:forEach var="i" items="${varContainer}"><p>key = <c:out value="${i.key}" />;value = <c:out value="${i.value}" /></p></c:forEach>
Итак, пробуем, вот код тега. Обратите внимание на то что я в цикле проверяю значение переменной ${i} и если оно четное то делаю вызов (jsp:invoke) fragment-переменной с именем even_fragment, иначе вызываю вычисление атрибута odd_fragment.
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %><%@ attribute name="start" required="true" rtexprvalue="true" type="java.lang.Integer" %><%@ attribute name="end" required="true" rtexprvalue="true" type="java.lang.Integer" %><%@ attribute name="even_fragment" required="true" fragment="true" %><%@ attribute name="odd_fragment" required="true" fragment="true" %><h1>Variables with fragment-attributes</h1>
<c:forEach var="i" begin="${start}" end="${end}"><c:choose><c:when test="${ i mod 2 eq 0}"><jsp:invoke fragment="odd_fragment" /></c:when><c:otherwise><jsp:invoke fragment="even_fragment" /></c:otherwise></c:choose></c:forEach>
<t:HelloTag start="1" end="10"><jsp:attribute name="even_fragment"><span style="color: red;">${i}</span>
</jsp:attribute><jsp:attribute name="odd_fragment"><span style="color: green;">${i}</span>
</jsp:attribute></t:HelloTag>
<c:set value="20px" var="size" /><t:HelloTag start="1" end="10"><jsp:attribute name="even_fragment"><span style="color: red; font-size: ${size};">${i}</span>
</jsp:attribute><jsp:attribute name="odd_fragment"><span style="color: green; font-size: ${size};">${i}</span>
</jsp:attribute></t:HelloTag>
<c:forEach var="i" begin="${start}" end="${end}" ><c:set value="${i}" var="i" scope="request" /><c:choose><c:when test="${ i mod 2 eq 0}"><jsp:invoke fragment="odd_fragment" /></c:when><c:otherwise><jsp:invoke fragment="even_fragment" /></c:otherwise></c:choose></c:forEach>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %><%@ attribute name="start" required="true" rtexprvalue="true" type="java.lang.Integer" %><%@ attribute name="end" required="true" rtexprvalue="true" type="java.lang.Integer" %><%@ attribute name="even_fragment" required="true" fragment="true" %><%@ attribute name="odd_fragment" required="true" fragment="true" %><%@ variable name-given="i" %><h1>Variables with fragment-attributes</h1>
<c:forEach var="i" begin="${start}" end="${end}" ><c:choose><c:when test="${ i mod 2 eq 0}"><jsp:invoke fragment="odd_fragment" /></c:when><c:otherwise><jsp:invoke fragment="even_fragment" /></c:otherwise></c:choose></c:forEach>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %><%@ attribute name="start" required="true" rtexprvalue="true" type="java.lang.Integer" %><%@ attribute name="end" required="true" rtexprvalue="true" type="java.lang.Integer" %><%@ attribute name="even_fragment" required="true" fragment="true" %><%@ attribute name="odd_fragment" required="true" fragment="true" %><%@ attribute name="cycleVar" required="true" rtexprvalue="false" type="java.lang.String" %><%@ variable name-from-attribute="cycleVar" alias="cycleVarAlias" scope="NESTED" %><h1>Variables with fragment-attributes</h1>
<c:forEach var="cycleVarAlias" begin="${start}" end="${end}" ><c:choose><c:when test="${ cycleVarAlias mod 2 eq 0}"><jsp:invoke fragment="odd_fragment" /></c:when><c:otherwise><jsp:invoke fragment="even_fragment" /></c:otherwise></c:choose></c:forEach>
<t:HelloTag start="1" end="10" cycleVar="j"><jsp:attribute name="even_fragment"><span style="color: red; font-size: ${size};">${j}</span>
</jsp:attribute><jsp:attribute name="odd_fragment"><span style="color: green; font-size: ${size};">${j}</span>
</jsp:attribute></t:HelloTag>
<%@ tag body-content="empty" %>
<t:HelloTag start="1" end="10">Привет, это тело тега
</t:HelloTag>
<t:HelloTag start="1" end="10"><jsp:body>Привет, это тело тега
</jsp:body></t:HelloTag>
<%@ tag body-content="scriptless" %><%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %><%@ attribute name="start" required="true" rtexprvalue="true" type="java.lang.Integer" %><%@ attribute name="end" required="true" rtexprvalue="true" type="java.lang.Integer" %><h1>Tag with body</h1>
<c:forEach var="i" begin="${start}" end="${end}" ><h2>${i}</h2>
<c:set var="color" value="${i mod 2 eq 0?'red':'green'}" /><p style="border: 1px solid ${color};"><jsp:doBody /></p></c:forEach>
<t:HelloTag start="1" end="10"><jsp:body>Привет, это тело тега
</jsp:body></t:HelloTag>
<t:HelloTag start="1" end="10"><jsp:body>Привет, это тело тега
<c:if test="${1 eq 1}"><h3>1 eq 1</h3>
</c:if></jsp:body></t:HelloTag>
<t:HelloTag start="1" end="10"><jsp:body>Привет, это тело тега
<h3><%out.println(new Date());%></h3></jsp:body></t:HelloTag>
<%@ tag body-content="tagdependent" %>
В теле тега можно использовать переменные также как мы использовали переменные с fragment-атрибутами. Вот пример исходного кода тега:
<%@ tag body-content="scriptless" %><%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %><%@ attribute name="start" required="true" rtexprvalue="true" type="java.lang.Integer" %><%@ attribute name="end" required="true" rtexprvalue="true" type="java.lang.Integer" %><%@ attribute name="cycleVar" required="true" rtexprvalue="false" type="java.lang.String" %><%@ variable alias="cycleVarAlias" name-from-attribute="cycleVar" %><h1>Tag with body</h1>
<c:forEach var="cycleVarAlias" begin="${start}" end="${end}" ><h2>${cycleVarAlias}</h2>
<c:set var="color" value="${cycleVarAlias mod 2 eq 0?'red':'green'}" /><p style="border: 1px solid ${color};"><jsp:doBody /></p></c:forEach>
<t:HelloTag start="1" end="10" cycleVar="i"><jsp:body>It's Body. Variable "i" is
<c:choose><c:when test="${i mod 2 eq 0}">Even
</c:when><c:otherwise>Odd
</c:otherwise></c:choose></jsp:body></t:HelloTag>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<jsp:root xmlns:c="http://java.sun.com/jsp/jstl/core">... какие-то действия ...
</jsp:root>
<%@ taglib tagdir="/WEB-INF/tags" prefix="t" %>
<jsp:rootversion="2.0"xmlns:jsp="http://java.sun.com/JSP/Page"xmlns="http://www.w3.org/1999/xhtml"xmlns:t="urn:jsptagdir:/WEB-INF/tags"xmlns:c="http://java.sun.com/jsp/jstl/core"><jsp:directive.page contentType="text/html; charset=UTF-8" /><html><head><title>Simple jspx page</title></head>
<body><t:HelloTag start="1" end="10" cycleVar="i"><jsp:body>It's Body. Variable "i" is
<c:choose><c:when test="${i mod 2 eq 0}">Even
</c:when><c:otherwise>Odd
</c:otherwise></c:choose></jsp:body></t:HelloTag></body></html></jsp:root>
| « Системы управления версиями для программистов и не только. Часть 2 | Как в spring написать валидатор, использующий коды сообщений, и не забыть кого-то из них » |