如何避免在JSP页面中使用scriptlet?

时间:2010-02-02 23:59:44

标签: java jsp sitemesh scriptlet

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

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

这个JSP实际上是我的sitemesh主装饰页面。基本上我的网页设计有一个标签条和一个子菜单,我希望通过查看当前的请求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>

全部谢谢

7 个答案:

答案 0 :(得分:42)

我认为如果你亲眼看到它实际上可以完全没有 scriptlet,那么它会有所帮助。

这是一对一的重写,在其他JSTL的帮助下(只需在/WEB-INF/libjstl-1.2.jarcore taglib中删除functions

<%@ 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>标记来避免在每个链接中放置上下文路径(只需网页中的相对网址相对于它 - 没有前导斜杠!):

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

如果您收集所有“硬编码”值(如eventspeople)并在应用程序范围内的Map中链接文本并在每个JSTL下使用,则实际上可以进行更多优化<c:forEach>显示标签。

关于实际问题,您可以通过在webapp的web.xml中添加以下条目来禁用 scriptlet(并获得有关使用它的运行时错误)。它可能有助于发现监督小脚本。

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

要了解有关EL的更多信息,请查看Java EE tutorial part II chapter 5${pageContext}描述了隐式EL对象,例如{{1}}。要了解有关JSTL的更多信息,请查看here。请注意,JSTL和EL是两个独立的东西。 JSTL是标准taglib ,EL只允许以编程方式访问后端数据。虽然它通常用在像JSTL这样的taglib中,但它也可以在模板文本中单独使用。

答案 1 :(得分:9)

  

顺便说一下,<%= request.getContextPath() %>是否可以接受使用那些不那么皱眉的小脚本?

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

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

但从根本上说(如“保持模板中的逻辑”)我看不出

之间的任何区别
<% if(request.getRequestURI().contains("/events/")) { %>

${fn:contains(pageContext.request.requestURI, '/events/') 

答案 2 :(得分:6)

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

  

有人可以使用更多java / jsp   经验请给我一些   关于如何更改此代码的指针   所以它更“最佳实践”,无论如何   那可能是

在我看来,关于JSP的最佳实践是它应该严格地用作模板引擎,而不是更多(即没有业务逻辑)。正如许多人所指出的那样,使用JSTL肯定会帮助你实现目标,但即使使用JSTL,在JSP中也很容易做到。

我个人喜欢在JSP开发时遵循Terence Parr在Enforcing Strict Model-View Separation in Templating Engines中规定的规则。本文提到了模板引擎的目的(分离模型和视图),以及良好的模板引擎的特性。它需要仔细研究JSP,并指出它不是一个好的模板引擎。毫不奇怪,JSP基本上太强大,并且允许开发人员做太多。我强烈建议您阅读本文,它将帮助您将自己局限于JSP的“好”部分。

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

  
      
  1. 视图无法通过直接更改模型来修改模型   数据对象或通过调用方法   引起副作用的模型。   也就是说,模板可以访问数据   从模型和调用方法,但   这些参考必须是副作用   自由。这个规则部分产生   因为数据引用必须是   为了不敏感。见7.1节。
  2.   
  3. 视图无法对依赖数据执行计算   值因为计算可能   改变未来,他们应该   整齐地封装在模型中   任何情况。例如,视图不能   计算书籍销售价格为   “$价格* 0.90”。独立于   模型,视图无法制作   关于数据含义的假设。
  4.   
  5. 视图无法比较相关数据值,但可以测试   数据的属性,如   是否存在或长度   多值数据值。测试就像   $ bloodPressure&lt; 120必须转移到   医生喜欢保留的模型   降低最大收缩压   在我们身上视图中的表达式必须是   替换为a的存在测试   模拟布尔值的值   $ bloodPressureOk!= null模板输出   可以以模型数据和条件为条件   计算,有条件的   必须在模型中计算。甚至   做出负值的简单测试   红色应该在模型中计算;   正确的抽象级别是usu-   某种更高层次的盟友   “部门x正在赔钱。”
  6.   
  7. 视图无法进行数据类型假设。某些类型的假设是   当视图假设数据时显而易见   例如,value是一个日期,但更多   细微类型的假设:如果a   模板假设$ userID是   整数,程序员不能   将此值更改为非数字值   在模型中没有打破   模板。此规则禁止数组   索引,如colorCode [$ topic]和   $ name [$ ID]视图进一步不能   调用带有参数的方法   (静态或动态)有   假定的论证类型,除非一个   可以保证模型方法   仅将它们视为对象。   除了图形设计师不是   程序员;期待他们调用   方法,知道要传递的是什么   不现实的。
  8.   
  9. 模型中的数据不得包含显示或布局信息。   模型无法通过任何显示   伪装成视图的信息   数据值。这包括不通过   要应用的模板的名称   其他数据值。
  10.   

顺便提一下,Terence已经创建了自己的模板引擎String Template,据称它可以很好地执行这些规则。我没有个人经验,但我很乐意在下一个项目中查看。

答案 3 :(得分:6)

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

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

答案 4 :(得分:3)

您可能希望从使用标记库开始。您可以使用标准标记库JSTL来完成您需要编写的大多数常见内容。还有许多其他更丰富的标记库,如struts2框架或apache中使用的那样。

e.g。

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

会替换你的if语句。

答案 5 :(得分:3)

scriptlet的首选替代方法是JSTL表达式语言; here是一个很好的概述。你需要像这样添加taglib:

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

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

因此,您可以将<%request.getRequestURI%>替换为${pageContext.request.requestURI}

您可以使用<c:if>标记进行条件化。

答案 6 :(得分:2)

您需要使用一些Web框架。或者至少是一些方便的taglib。或者像 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