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, но тогда мне понадобится верхняя и нижняя части для каждой обертки, которая является довольно грязной.

Ответ 1

Как скаффман предложил, Файлы тегов 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: tags..). Это приводит к таблице строк, чередующихся цветов, навигации по страницам и т.д.:

<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, но это не так много ограничений, как вы думаете. Если мне нужен материал scriptlet, я просто помещаю логику в тег и отбрасываю тег. Просто.

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

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

Ответ 2

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

Я думаю, что упростить управление макетами без кривой обучения.

пример кода:

base.jsp: layout

<%@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: contents

<%@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>

Ответ 3

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

WEB-INF/теги/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 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);
    }
%>

Ответ 4

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

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

Тег тела может фактически не делать то, что вам нужно, если у вас нет супер простого контента. Тег 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>

Ответ 5

Вы должны знать, что использование JSP с большим количеством <%...% > в целом показало, что для создания приложений, которые трудно поддерживать (нетривиального размера), обычно показано.

Следовательно, вы уже должны подготовиться к тому, чтобы узнать дополнительный слой, как для этого, так и для следующего проекта. Лично я выбрал JSF для проекта, который позволил создавать XML-jsp-страницы, вызывающие taglib, которые были достаточно хороши, но имели крутую кривую обучения, поэтому я не буду рекомендовать ее, если вы не рассмотрели ее сначала:)

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

Ответ 6

@Уилл Хартунг, я создал класс UserName, чтобы все было понятно. Чтобы сделать его более полным, я поместил тип Integer в переменную phone.

Но тогда переменная не появилась в вашем примере из-за типа Integer. Как я могу сделать это показано на моей странице JSP?

Заранее спасибо!

Ответ 7

добавить зависимости для использования & lt;% @tag description = "Шаблон страницы пользователя" 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>

Ответ 8

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

Ответ 9

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

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

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

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

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