Уловки JSP, упрощающие создание шаблонов?

На работе мне было поручено превратить кучу HTML файлов в простой JSP проект. Это действительно все статично, нет серверной логики для программирования. Я должен упомянуть, что я совершенно новичок в Java. Файлы JSP, похоже, упрощают работу с общими включениями и переменными, как и PHP, но я хотел бы знать простой способ получить что-то вроде наследования шаблонов (стиль Django) или, по крайней мере, иметь возможность иметь base.jsp файл, содержащий верхний и нижний колонтитулы, поэтому я могу вставить содержимое позже.

Бен Лингс, кажется, дает некоторую надежду в своем ответе здесь: Наследование шаблонов JSP Может кто-нибудь объяснить, как этого добиться?

Учитывая, что у меня не так много времени, я думаю, что динамическая маршрутизация - это немного сложнее, поэтому я рад просто отображать URL-адреса непосредственно на файлы .jsp, но я открыт для предложений.

Спасибо.

edit: Я не хочу использовать какие-либо внешние библиотеки, потому что это увеличило бы кривую обучения для меня и других, кто работает над проектом, и компания, в которой я работаю, получила контракт на это.

Еще одно изменение. Я не уверен, что JSP tags будет полезно, потому что в моем содержании нет никаких переменных шаблона. Мне нужен способ сделать это:

base.html:

<html><body>
{ content.body }
</body></html>

somepage.html

<wrapper:base.html>
<h1>Welcome</h1>
</wrapper>

с выходом:

<html><body>
<h1>Welcome</h1>
</body></html>

Думаю, это дало бы мне достаточно гибкости, чтобы делать все, что мне нужно. Это может быть достигнуто с помощью includes, но тогда мне понадобятся верхняя и нижняя части для каждой оболочки, что немного беспорядочно.


person Scott    schedule 18.08.2009    source источник


Ответы (7)


Как предложил скаффман, файлы тегов JSP 2.0 - это колени пчелы.

Возьмем ваш простой пример.

Поместите следующее в WEB-INF/tags/wrapper.tag

<%@tag description="Simple Wrapper Tag" pageEncoding="UTF-8"%>
<html><body>
  <jsp:doBody/>
</body></html>

Теперь на вашей example.jsp странице:

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@taglib prefix="t" tagdir="/WEB-INF/tags" %>

<t:wrapper>
    <h1>Welcome</h1>
</t:wrapper>

Это именно то, что вы думаете.


Итак, давайте расширим это до чего-то более общего. WEB-INF/tags/genericpage.tag

<%@tag description="Overall Page template" pageEncoding="UTF-8"%>
<%@attribute name="header" fragment="true" %>
<%@attribute name="footer" fragment="true" %>
<html>
  <body>
    <div id="pageheader">
      <jsp:invoke fragment="header"/>
    </div>
    <div id="body">
      <jsp:doBody/>
    </div>
    <div id="pagefooter">
      <jsp:invoke fragment="footer"/>
    </div>
  </body>
</html>

Чтобы использовать это:

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@taglib prefix="t" tagdir="/WEB-INF/tags" %>

<t:genericpage>
    <jsp:attribute name="header">
      <h1>Welcome</h1>
    </jsp:attribute>
    <jsp:attribute name="footer">
      <p id="copyright">Copyright 1927, Future Bits When There Be Bits Inc.</p>
    </jsp:attribute>
    <jsp:body>
        <p>Hi I'm the heart of the message</p>
    </jsp:body>
</t:genericpage>

Что это вам дает? На самом деле много, но становится еще лучше ...


WEB-INF/tags/userpage.tag

<%@tag description="User Page template" pageEncoding="UTF-8"%>
<%@taglib prefix="t" tagdir="/WEB-INF/tags" %>
<%@attribute name="userName" required="true"%>

<t:genericpage>
    <jsp:attribute name="header">
      <h1>Welcome ${userName}</h1>
    </jsp:attribute>
    <jsp:attribute name="footer">
      <p id="copyright">Copyright 1927, Future Bits When There Be Bits Inc.</p>
    </jsp:attribute>
    <jsp:body>
        <jsp:doBody/>
    </jsp:body>
</t:genericpage>

Чтобы использовать это: (предположим, что в запросе есть пользовательская переменная)

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@taglib prefix="t" tagdir="/WEB-INF/tags" %>

<t:userpage userName="${user.fullName}">
  <p>
    First Name: ${user.firstName} <br/>
    Last Name: ${user.lastName} <br/>
    Phone: ${user.phone}<br/>
  </p>
</t:userpage>

Но оказывается, вам нравится использовать этот блок сведений о пользователе в других местах. Итак, мы проведем рефакторинг. WEB-INF/tags/userdetail.tag

<%@tag description="User Page template" pageEncoding="UTF-8"%>
<%@tag import="com.example.User" %>
<%@attribute name="user" required="true" type="com.example.User"%>

First Name: ${user.firstName} <br/>
Last Name: ${user.lastName} <br/>
Phone: ${user.phone}<br/>

Теперь предыдущий пример выглядит следующим образом:

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@taglib prefix="t" tagdir="/WEB-INF/tags" %>

<t:userpage userName="${user.fullName}">
  <p>
    <t:userdetail user="${user}"/>
  </p>
</t:userpage>

Прелесть файлов тегов JSP заключается в том, что они позволяют вам помечать общую разметку, а затем рефакторировать ее по своему усмотрению.

JSP Tag Files в значительной степени узурпировали такие вещи, как Tiles и т. Д., По крайней мере, для меня. Я считаю, что их намного проще использовать, поскольку единственная структура - это то, что вы ей даете, без каких-либо предвзятостей. Кроме того, вы можете использовать файлы тегов JSP для других целей (например, фрагмент сведений о пользователе выше).

Вот пример, похожий на DisplayTag, который я сделал, но все это делается с помощью файлов тегов (и платформы Stripes, это теги s: ..). Это приводит к таблице строк, чередующихся цветов, навигации по страницам и т. Д .:

<t:table items="${actionBean.customerList}" var="obj" css_class="display">
  <t:col css_class="checkboxcol">
    <s:checkbox name="customerIds" value="${obj.customerId}"
                onclick="handleCheckboxRangeSelection(this, event);"/>
  </t:col>
  <t:col name="customerId" title="ID"/>
  <t:col name="firstName" title="First Name"/>
  <t:col name="lastName" title="Last Name"/>
  <t:col>
    <s:link href="/Customer.action" event="preEdit">
      Edit
      <s:param name="customer.customerId" value="${obj.customerId}"/>
      <s:param name="page" value="${actionBean.page}"/>
    </s:link>
  </t:col>
</t:table>

Конечно, теги работают с JSTL tags (например, c:if и т. Д.). Единственное, что вы не можете сделать в теле тега файла тега, - это добавить код скриптлета Java, но это не такое большое ограничение, как вы могли подумать. Если мне нужны скриптлеты, я просто вставляю логику в тег и вставляю его. Легко.

Итак, файлы тегов могут быть практически такими, какими вы хотите их видеть. На самом базовом уровне это простой рефакторинг вырезания и вставки. Возьмите кусок макета, вырежьте его, выполните простую параметризацию и замените его вызовом тега.

На более высоком уровне вы можете делать сложные вещи, такие как этот тег таблицы, который у меня здесь.

person Will Hartung    schedule 15.07.2010
comment
Спасибо за это. Это лучший учебник, который я смог найти по файлам тегов JSP, которые мне очень понравились из JSF. Хотел бы я отдать больше одного голоса за. - person digitaljoel; 02.11.2010
comment
+ 40млн. Спасибо, что объяснили это в 50 000 раз лучше, чем любой дурацкий учебник, который я нашел. Я пришел из мира Rails и отсутствовал ERB, это именно то, что мне нужно. Вам следует вести блог. - person cbmeeks; 08.04.2011
comment
Действительно хороший учебник. Не могли бы вы поделиться с нами кодом для созданного вами тега таблицы? Я сам создал его некоторое время назад, но ваш подход лучше. - person Thiago Duarte; 28.04.2012
comment
Очень полезный учебник, большое спасибо! Небольшой вопрос: когда вы говорите «Если мне нужен скриптлет», я просто вставляю логику в тег и вставляю его. Вы на самом деле имеете в виду taglib, верно? Потому что в противном случае вы, кажется, говорите, что код скриптлета не может использоваться в теге, но вместо этого можно добавить его в другой тег! - person electrotype; 13.07.2012
comment
Если вы создаете тег файла тега, содержимое этого тега в файле JSP не может содержать код скриптлета: ‹t: mytag› здесь нет кода скриптлета ‹/ t: mytag›. Но внутри файла тега, реализующего сам тег, может быть любой код скриптлета, который вы хотите, как и любой JSP. - person Will Hartung; 13.07.2012
comment
@WillHartung сначала спасибо за отличный ответ, у меня есть один вопрос: как бороться с вложенным тегом в файлах тегов jsp? Например, в вашем теге t:table t:col должен быть в одном table, так как же получить родительский тег при реализации дочернего тега? - person donnior; 07.08.2012
comment
Я просто помещаю вещи в известные места в запросе и т. Д. Например, тег t:table может создать список в _tableColumns, чем заполняет тег t:col. Затем, когда t:table выполняет jsp:doBody, t:col добавляет элементы в этот список, а в конце, после jsp:doBody, тег t:table отображает элементы в списке, а затем удаляет их из запроса. - person Will Hartung; 07.08.2012
comment
@WillHartung спасибо, я понял ваши средства, это действительно просто. - person donnior; 08.08.2012
comment
Примечание. Похоже, порядок тегов важен; jsp: attribute должен стоять перед jsp: body, иначе вы получите сообщение об ошибке. Также мне пришлось установить соответствующий тег @attribute для соответствия jsp: invoke, чтобы избежать еще одной ошибки. Использование GlassFish 3.2.2 - person Ryan; 04.04.2013
comment
файлы тегов дают действительно низкую производительность и очень мало функциональности по сравнению с tile-3 (но они отлично подходят, если вам не нужна производительность или дополнительные функции, и вам ничего не мешает использовать оба - каждый в соответствии с их сильными сторонами ). - person mck; 28.07.2013
comment
@mck не могли бы вы уточнить свои требования к производительности? В настоящее время я оцениваю, стоит ли идти по пути Apache Tiles или файлам тегов JSP 2.x, и из этого ответа кажется, что файлы тегов значительно упрощают работу по сравнению с поддержкой файла (ов) tile.xml. - person Morten Jacobsen; 20.09.2013
comment
Итак, в ERB вы можете определить шаблон страницы для рендеринга по умолчанию, который использует ‹% = yield%›. Пытаюсь понять, как сделать что-то подобное в тегах JSP. - person Kevin Suttle; 18.02.2014
comment
Просто примечание: очевидно, вы должны поставить все jsp:attribute перед jsp:body. Если я сделаю наоборот, появится случайная ошибка, как описано в связанном вопросе. stackoverflow.com/questions/30219719/ - person Niklas Peter; 24.12.2015
comment
Можно ли нам добавить атрибуты? Например, я хочу добавить код javascript через код в нескольких местах и ​​отобразить его в конце базового макета. - person Glapa; 10.06.2017
comment
Я люблю вас, великий великий господин. - person Eduardo La Hoz Miranda; 04.05.2021
comment
Я прав, что мы не можем использовать директиву сценария ‹% в содержимом тела yield, как мы это делаем в blade с laravel? @KevinSuttle, мы говорим об одном и том же. Вы когда-нибудь находили способ сделать это? - person exSnake; 09.05.2021

Я сделал довольно простую библиотеку тегов наследования JSP-шаблонов в стиле Django. https://github.com/kwon37xi/jsp-template-inheritance

Я считаю, что управлять макетами проще простого.

пример кода:

base.jsp: макет

<%@page contentType="text/html; charset=UTF-8" %>
<%@ taglib uri="http://kwonnam.pe.kr/jsp/template-inheritance" prefix="layout"%>
<!DOCTYPE html>
<html lang="en">
    <head>
        <title>JSP Template Inheritance</title>
    </head>

<h1>Head</h1>
<div>
    <layout:block name="header">
        header
    </layout:block>
</div>

<h1>Contents</h1>
<div>
    <p>
    <layout:block name="contents">
        <h2>Contents will be placed under this h2</h2>
    </layout:block>
    </p>
</div>

<div class="footer">
    <hr />
    <a href="https://github.com/kwon37xi/jsp-template-inheritance">jsp template inheritance example</a>
</div>
</html>

view.jsp: содержимое

<%@page contentType="text/html; charset=UTF-8" %>
<%@ taglib uri="http://kwonnam.pe.kr/jsp/template-inheritance" prefix="layout"%>
<layout:extends name="base.jsp">
    <layout:put name="header" type="REPLACE">
        <h2>This is an example about layout management with JSP Template Inheritance</h2>
    </layout:put>
    <layout:put name="contents">
        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin porta,
        augue ut ornare sagittis, diam libero facilisis augue, quis accumsan enim velit a mauris.
    </layout:put>
</layout:extends>
person KwonNam    schedule 13.09.2013

Основываясь на той же базовой идее, что и в ответе @Will Hartung, вот мой волшебный механизм расширяемых шаблонов с одним тегом. Он даже включает документацию и пример :-)

WEB-INF / tags / block.tag:

<%--
    The block tag implements a basic but useful extensible template system.

    A base template consists of a block tag without a 'template' attribute.
    The template body is specified in a standard jsp:body tag, which can
    contain EL, JSTL tags, nested block tags and other custom tags, but
    cannot contain scriptlets (scriptlets are allowed in the template file,
    but only outside of the body and attribute tags). Templates can be
    full-page templates, or smaller blocks of markup included within a page.

    The template is customizable by referencing named attributes within
    the body (via EL). Attribute values can then be set either as attributes
    of the block tag element itself (convenient for short values), or by
    using nested jsp:attribute elements (better for entire blocks of markup).

    Rendering a template block or extending it in a child template is then
    just a matter of invoking the block tag with the 'template' attribute set
    to the desired template name, and overriding template-specific attributes
    as necessary to customize it.

    Attribute values set when rendering a tag override those set in the template
    definition, which override those set in its parent template definition, etc.
    The attributes that are set in the base template are thus effectively used
    as defaults. Attributes that are not set anywhere are treated as empty.

    Internally, attributes are passed from child to parent via request-scope
    attributes, which are removed when rendering is complete.

    Here's a contrived example:

    ====== WEB-INF/tags/block.tag (the template engine tag)

    <the file you're looking at right now>

    ====== WEB-INF/templates/base.jsp (base template)

    <%@ page trimDirectiveWhitespaces="true" %>
    <%@ taglib prefix="t" tagdir="/WEB-INF/tags" %>
    <t:block>
        <jsp:attribute name="title">Template Page</jsp:attribute>
        <jsp:attribute name="style">
            .footer { font-size: smaller; color: #aaa; }
            .content { margin: 2em; color: #009; }
            ${moreStyle}
        </jsp:attribute>
        <jsp:attribute name="footer">
            <div class="footer">
                Powered by the block tag
            </div>
        </jsp:attribute>
        <jsp:body>
            <html>
                <head>
                    <title>${title}</title>
                    <style>
                        ${style}
                    </style>
                </head>
                <body>
                    <h1>${title}</h1>
                    <div class="content">
                        ${content}
                    </div>
                    ${footer}
                </body>
            </html>
        </jsp:body>
    </t:block>

    ====== WEB-INF/templates/history.jsp (child template)

    <%@ page trimDirectiveWhitespaces="true" %>
    <%@ taglib prefix="t" tagdir="/WEB-INF/tags" %>
    <t:block template="base" title="History Lesson">
        <jsp:attribute name="content" trim="false">
            <p>${shooter} shot first!</p>
        </jsp:attribute>
    </t:block>

    ====== history-1977.jsp (a page using child template)

    <%@ page trimDirectiveWhitespaces="true" %>
    <%@ taglib prefix="t" tagdir="/WEB-INF/tags" %>
    <t:block template="history" shooter="Han" />

    ====== history-1997.jsp (a page using child template)

    <%@ page trimDirectiveWhitespaces="true" %>
    <%@ taglib prefix="t" tagdir="/WEB-INF/tags" %>
    <t:block template="history" title="Revised History Lesson">
        <jsp:attribute name="moreStyle">.revised { font-style: italic; }</jsp:attribute>
        <jsp:attribute name="shooter"><span class="revised">Greedo</span></jsp:attribute>
    </t:block>

--%>

<%@ tag trimDirectiveWhitespaces="true" %>
<%@ tag import="java.util.HashSet, java.util.Map, java.util.Map.Entry" %>
<%@ tag dynamic-attributes="dynattributes" %>
<%@ attribute name="template" %>
<%
    // get template name (adding default .jsp extension if it does not contain
    // any '.', and /WEB-INF/templates/ prefix if it does not start with a '/')
    String template = (String)jspContext.getAttribute("template");
    if (template != null) {
        if (!template.contains("."))
            template += ".jsp";
        if (!template.startsWith("/"))
            template = "/WEB-INF/templates/" + template;
    }
    // copy dynamic attributes into request scope so they can be accessed from included template page
    // (child is processed before parent template, so only set previously undefined attributes)
    Map<String, String> dynattributes = (Map<String, String>)jspContext.getAttribute("dynattributes");
    HashSet<String> addedAttributes = new HashSet<String>();
    for (Map.Entry<String, String> e : dynattributes.entrySet()) {
        if (jspContext.getAttribute(e.getKey(), PageContext.REQUEST_SCOPE) == null) {
            jspContext.setAttribute(e.getKey(), e.getValue(), PageContext.REQUEST_SCOPE);
            addedAttributes.add(e.getKey());
        }
    }
%>

<% if (template == null) { // this is the base template itself, so render it %>
    <jsp:doBody/>
<% } else { // this is a page using the template, so include the template instead %>
    <jsp:include page="<%= template %>" />
<% } %>

<%
    // clean up the added attributes to prevent side effect outside the current tag
    for (String key : addedAttributes) {
        jspContext.removeAttribute(key, PageContext.REQUEST_SCOPE);
    }
%>
person amichair    schedule 04.07.2014

Используйте плитки. Это спасло мне жизнь.

Но если вы не можете, есть тег include, делая его похожим на php.

Тег body может на самом деле не делать то, что вам нужно, если у вас нет сверхпростого содержимого. Тег body используется для определения тела указанного элемента. Взгляните на этот пример:

<jsp:element name="${content.headerName}"   
   xmlns:jsp="http://java.sun.com/JSP/Page">    
   <jsp:attribute name="lang">${content.lang}</jsp:attribute>   
   <jsp:body>${content.body}</jsp:body> 
</jsp:element>

Вы указываете имя элемента, любые атрибуты, которые элемент может иметь (в данном случае «lang»), а затем текст, который в нем идет - тело. Так что если

  • content.headerName = h1,
  • content.lang = fr и
  • content.body = Heading in French

Тогда вывод будет

<h1 lang="fr">Heading in French</h1>
person geowa4    schedule 18.08.2009

Этого также можно добиться с помощью jsp: include. Чад Дарби хорошо объясняет здесь, в этом видео https://www.youtube.com/watch?v=EWbYj0qoNHo

person Sandeep Amarnath    schedule 24.09.2019

Добавить зависимости для использования <%@tag description="User Page template" pageEncoding="UTF-8"%>

<dependencies>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.5</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.2</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>javax.servlet.jsp.jstl</groupId>
        <artifactId>javax.servlet.jsp.jstl-api</artifactId>
        <version>1.2.1</version>
    </dependency>
    <dependency>
        <groupId>taglibs</groupId>
        <artifactId>standard</artifactId>
        <version>1.1.2</version>
    </dependency>
</dependencies>
person Juan Silupú Maza    schedule 21.08.2019

Я знаю, что этот ответ придет спустя годы, и Уилла Хартунга уже есть отличный ответ JSP, но есть Facelets, они даже упоминаются в ответах на связанный вопрос в исходном вопросе.

Описание тега Facelets SO

Facelets - это основанная на XML технология просмотра для платформы JavaServer Faces. Facelets, разработанный специально для JSF, призван стать более простой и мощной альтернативой представлениям на основе JSP. Первоначально это был отдельный проект, технология была стандартизирована как часть JSF 2.0 и Java-EE 6, а JSP не рекомендуется. Почти все целевые библиотеки компонентов JSF 2.0 больше не поддерживают JSP, а только Facelets.

К сожалению, лучшее простое описание учебника, которое я нашел, было на Википедии, а не на учебном сайте. Фактически, раздел, описывающий шаблоны, даже соответствует тому, что задавался в исходном вопросе. для.

В связи с тем, что Java-EE 6 не поддерживает JSP, я бы порекомендовал использовать Facelets, несмотря на то, что похоже, что может потребоваться больше, чтобы получить незначительный выигрыш по сравнению с JSP.

person Fering    schedule 20.11.2018
comment
Java EE 6 не объявляет устаревшим JSP, просто не рекомендуется использовать JSP в качестве технологии просмотра для JSF. - person Ryan; 14.05.2019
comment
@Ryan Поскольку в данном случае оба говорили о технологии просмотра, что плохого в том, что она объявила о том, что она устарела? - person Fering; 14.05.2019
comment
Вопрос не имеет отношения к JSF. Речь идет о чистом JSP. Ваш ответ - использовать Facelets для JSF. - person Ryan; 14.05.2019