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

Chr*_*ris 33 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>
Run Code Online (Sandbox Code Playgroud)

谢谢大家

Bal*_*usC 42

我认为如果你亲眼看到它实际上可以在没有 scriptlet的情况下完全完成,那会更有帮助.

这里有一个1对1重写与中别人的帮助JSTL(刚落jstl-1.2.jar/WEB-INF/lib)核心功能的taglib:

<%@ 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>
Run Code Online (Sandbox Code Playgroud)

这是一个更优化的重写,请注意我曾经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>
Run Code Online (Sandbox Code Playgroud)

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

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

<jsp-config>
    <jsp-property-group>
        <url-pattern>*.jsp</url-pattern>
        <scripting-invalid>true</scripting-invalid>
    </jsp-property-group>
</jsp-config>
Run Code Online (Sandbox Code Playgroud)

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

  • 如果你比较"之前"和"之后"模板,你会发现它们几乎是一样的.我不知道后者如何自动比前者更好(除了看起来更清洁一点). (5认同)
  • @Ree - 老实说,我认为JSTL的最大优点是它不会让你自由地编写你的JSP中想到的任何代码.JSTL为您提供编写模板所需的大多数工具,而不是控制器,业务逻辑管理器或数据访问对象. (3认同)

Thi*_*ilo 9

<%= request.getContextPath() %>顺便说一句,对于那些不那么皱眉的scriptlet 是可以接受的吗?

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

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

但从根本上说(如"保持模板中的逻辑")我没有看到任何区别

<% if(request.getRequestURI().contains("/events/")) { %>
Run Code Online (Sandbox Code Playgroud)

${fn:contains(pageContext.request.requestURI, '/events/') 
Run Code Online (Sandbox Code Playgroud)

  • 太好了!我以为我是唯一有此意见的人! (3认同)

Jac*_*eow 6

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

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

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

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

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

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

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


Spi*_*ams 6

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

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