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