35

有人告诉我,在我的 JSP 页面中使用 scriptlet (<%= ... %>) 并不是一个好主意。

有更多java / jsp经验的人可以给我一些关于如何更改此代码的指示,以便它更“最佳实践”,不管是什么?

这个 JSP 实际上是我的站点网格主装饰器页面。基本上我的网页设计有一个标签条和一个子菜单,我希望通过查看当前请求 URI 以某种方式突出显示当前标签并显示正确的子菜单。

<%@ taglib uri="http://www.opensymphony.com/sitemesh/decorator" prefix="decorator" %>

<html>
<head>
  <title>My Events - <decorator:title /></title>
  <link href="<%= request.getContextPath() %>/assets/styles.css" rel="stylesheet" type="text/css" />
</head>
<body>

<div class="tabs">
  <a 
    <%= request.getRequestURI().contains("/events/") ? "class='selected'" : "" %>
    href='<%= request.getContextPath() %>/events/Listing.action'>Events</a>
  <a 
    <%= request.getRequestURI().contains("/people/") ? "class='selected'" : "" %>
    href='<%= request.getContextPath() %>/people/Listing.action'>People</a>
</div>

<div class="submenu">
  <% if(request.getRequestURI().contains("/events/")) { %>
    <a href="Listing.action">List of Events</a>
    |<a href="New.action">New Event</a>
  <% } %>
  <% if(request.getRequestURI().contains("/people/")) { %>
    <a href="Listing.action">List of People</a>
    |<a href="New.action">New Person</a>
  <% } %>  
  &nbsp;
</div>

<div class="body">
  <decorator:body />
</div>

</body>
</html>

谢谢大家

4

7 回答 7

42

我认为,如果您亲眼看到它实际上可以完全在没有脚本的情况下完成,它会更有帮助。

这是在其他JSTL(只需jstl-1.2.jar放入/WEB-INF/lib核心函数taglib 的帮助下进行的 1 对 1 重写:

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>

<html>
<head>
  <title>My Events - <decorator:title /></title>
  <link href="${pageContext.request.contextPath}/assets/styles.css" rel="stylesheet" type="text/css" />
</head>
<body>

<div class="tabs">
  <a 
    ${fn:contains(pageContext.request.requestURI, '/events/') ? 'class="selected"' : ''}
    href="${pageContext.request.contextPath}/events/Listing.action">Events</a>
  <a 
    ${fn:contains(pageContext.request.requestURI, '/people/') ? 'class="selected"' : ''}
    href="${pageContext.request.contextPath}/people/Listing.action">People</a>
</div>

<div class="submenu">
  <c:if test="${fn:contains(pageContext.request.requestURI, '/events/')}">
    <a href="Listing.action">List of Events</a>
    |<a href="New.action">New Event</a>
  </c:if>
  <c:if test="${fn:contains(pageContext.request.requestURI, '/people/')}">
    <a href="Listing.action">List of People</a>
    |<a href="New.action">New Person</a>
  </c:if>
  &nbsp;
</div>

这是一个更优化的重写,请注意,我曾经c:set“缓存”表达式结果以供重用,并且我使用 HTML<base>标记来避免将上下文路径放在每个链接中(只需使网页中的所有相对 URL 都相对于它 - 没有前导削减!):

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>

<c:set var="isEvents" value="${fn:contains(pageContext.request.requestURI, '/events/')}" />
<c:set var="isPeople" value="${fn:contains(pageContext.request.requestURI, '/people/')}" />

<html>
<head>
  <title>My Events - <decorator:title /></title>
  <base href="${pageContext.request.contextPath}">
  <link href="assets/styles.css" rel="stylesheet" type="text/css" />
</head>
<body>

<div class="tabs">
  <a ${isEvents ? 'class="selected"' : ''} href="events/Listing.action">Events</a>
  <a ${isPeople ? 'class="selected"' : ''} href="people/Listing.action">People</a>
</div>

<div class="submenu">
  <c:if test="${isEvents}">
    <a href="Listing.action">List of Events</a>|<a href="New.action">New Event</a>
  </c:if>
  <c:if test="${isPeople}">
    <a href="Listing.action">List of People</a>|<a href="New.action">New Person</a>
  </c:if>
  &nbsp;
</div>

如果您在应用程序范围内收集所有这些“硬编码”值,例如和链接文本,并在每个 JSTL 下使用以显示选项卡,它实际上可以events得到people更多Map优化<c:forEach>

至于您的实际问题,您可以通过在webappweb.xml. 它可能有助于发现受监督的小脚本。

<jsp-config>
    <jsp-property-group>
        <url-pattern>*.jsp</url-pattern>
        <scripting-invalid>true</scripting-invalid>
    </jsp-property-group>
</jsp-config>

要了解有关 EL 的更多信息,请查看Java EE 教程第二部分第 5 章。隐式 EL 对象,如此${pageContext}所述。要了解有关 JSTL 的更多信息,请查看Java EE 教程第二部分第 7 章。请注意,JSTL 和 EL 是两个不同的东西。JSTL 是一个标准的标记库,EL 只允许以编程方式访问后端数据。虽然它通常用于像 JSTL 这样的标记库中,但它也可以在模板文本中独立使用。

于 2010-02-03T01:48:01.010 回答
10

<%= request.getContextPath() %>顺便说一句,对于不那么不赞成的 scriptlet 的使用是否可以接受?

这可能是一个不受欢迎的观点,但如果你所做的只是简单的条件和文本插入,我在使用 scriptlet 时找不到太多错误。(注意if

我可能会使用 JSTL 和表达式语言,但主要是因为它可以减少输入,并且 IDE 支持可能会更好(但是一个好的 JSP IDE 也可以找到缺少的右括号之类的东西)。

但从根本上说(如“将逻辑排除在模板之外”)我看不出两者之间有任何区别

<% if(request.getRequestURI().contains("/events/")) { %>

${fn:contains(pageContext.request.requestURI, '/events/') 
于 2010-02-03T01:29:24.567 回答
7

Scriptlet 并不是世界上最糟糕的事情。一个重要的考虑因素是考虑谁将维护代码。如果它的网页设计师没有太多的 Java 经验,那么你最好选择标签库。但是,如果 Java 开发人员进行维护,他们可能更容易使用 scriptlet。

如果您最终使用标签库和 JSTL,您希望维护者也学习标签库并了解 JSTL。一些开发人员会对此感到满意,因为这是他们想要或已经拥有的技能,但对于一些只需要每隔几个月左右处理一次 JSP 的开发人员来说,使用用 nice 编写的清晰编写的 scriptlet 会不会那么痛苦,熟悉的Java。

于 2010-02-03T03:05:12.957 回答
6

这不是您问题的直接答案(并且已经有几个很好的答案,所以我不会尝试添加),但是您确实提到了:

有更多java / jsp经验的人可以给我一些关于如何更改此代码的指示,以便它更“最佳实践”,不管是什么?

在我看来,关于 JSP 的最佳实践是,它应该严格用作模板引擎,仅此而已(即,其中没有业务逻辑)。正如许多人指出的那样,使用 JSTL 绝对可以帮助您实现目标,但即使使用 JSTL,在 JSP 中也很容易做很多事情。

在 JSP 中开发时,我个人喜欢遵循 ​​Terence Parr在模板引擎中强制执行严格的模型视图分离中规定的规则。论文提到了模板引擎的目的(分离模型和视图),以及一个好的模板引擎的特点。它对 JSP 进行了很好的了解,并指出了它不是一个好的模板引擎的方式。毫不奇怪,JSP 基本上太强大了,允许开发人员做太多事情。我强烈推荐阅读这篇论文,它将帮助您将自己限制在 JSP 的“好”部分。

如果您只阅读该论文中的一个部分,请阅读第 7 章,其中包括以下规则:

  1. 视图不能通过直接更改模型数据对象或通过调用模型上导致副作用的方法来修改模型。 也就是说,模板可以访问模型中的数据并调用方法,但这样的引用必须是无副作用的。出现此规则的部分原因是数据引用必须是顺序不敏感的。请参阅第 7.1 节。
  2. 视图不能对相关数据值执行计算,因为计算可能会在未来发生变化,并且无论如何都应该将它们整齐地封装在模型中。例如,视图无法将图书销售价格计算为“$price*.90”。为了独立于模型,视图不能对数据的含义做出假设。
  3. 该视图不能比较相关数据值,但可以测试数据的属性,例如存在/不存在或多值数据值的长度。像 $bloodPressure<120 这样的测试必须转移到模型中,因为医生喜欢不断降低我们的最大收缩压。视图中的表达式必须替换为是否存在模拟布尔值的测试,例如 $bloodPressureOk!=null 模板输出可以以模型数据和计算为条件,只需在模型中计算条件. 即使是使负值变为红色的简单测试也应该在模型中进行计算;正确的抽象层次通常是更高层次的东西,例如“部门 x 亏钱”。</li>
  4. 视图不能做出数据类型假设。例如,当视图假设数据值是日期时,某些类型假设是显而易见的,但会出现更微妙的类型假设:如果模板假设 $userID 是整数,则程序员不能将此值更改为非- 模型中的数字而不破坏模板。此规则禁止数组索引,例如 colorCode[$topic] 和 $name[$ID] 视图进一步不能调用带参数的方法,因为(静态或动态)存在假定的参数类型,除非可以保证模型方法只是将它们视为对象。此外,图形设计师不是程序员;期望他们调用方法并知道要传递什么是不现实的。
  5. 来自模型的数据不得包含显示或布局信息。 模型不能将任何显示信息伪装成数据值传递给视图。这包括不传递模板名称以应用于其他数据值。

顺便说一句,Terence 创建了自己的模板引擎,称为String Template,据说它在执行这些规则方面做得非常好。我没有个人经验,但很想在我的下一个项目中检查一下。

于 2010-02-03T02:30:02.410 回答
3

您可能希望从使用标签库开始。您可以使用标准标记库JSTL来完成大多数需要 scriplets 的常见事情。还有许多其他更丰富的标签库,例如在 struts2 框架或 apache 中使用。

例如

  <c:if test="${your condition}">
       Your Content
  </c:if>

将替换您的 if 语句。

于 2010-02-03T00:10:38.160 回答
3

scriptlet 的首选替代方案是 JSTL 表达式语言。是一个很好的概述。您需要像这样添加标签库:

<%@ taglib uri='http://java.sun.com/jsp/jstl/core' prefix='c' %>

举个例子,JSTL 提供了一堆隐含的对象,可以为你提供你需要的东西;你想要的是pageContext.request

所以你可以<%request.getRequestURI%>${pageContext.request.requestURI}.

您可以使用<c:if>标签执行条件。

于 2010-02-03T00:11:47.567 回答
2

您需要使用一些网络框架。或者至少是一些方便的标签库。或者像FreeMarker这样的模板引擎。

广告框架:

如果您喜欢 JSP 编码方式,那么我建议您使用Struts 2

<s:if test="%{false}">
    <div>Will Not Be Executed</div>
</s:if>
<s:elseif test="%{true}">
    <div>Will Be Executed</div>
</s:elseif>
<s:else>
    <div>Will Not Be Executed</div>
</s:else>

然后是面向组件的JSF

如果您喜欢 OOP 并使用 Java 编写所有代码,请尝试Apache Wicket(我最喜欢的)或Google Web Toolkit

于 2010-02-03T00:04:06.477 回答