| « Системы управления версиями для программистов и не только. Часть 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:root
- version="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 написать валидатор, использующий коды сообщений, и не забыть кого-то из них » |