如何避免JSP文件中的Java代码?

我是Java EE的新手,我知道像下面三行

<%= x+1 %> <%= request.getParameter("name") %> <%! counter++; %> 

是一种古老的编码方式,在JSP版本2中存在一种避免JSP文件中的Java代码的方法。 有人可以告诉我的替代JSP 2线,这种技术被称为?

自从十多年前taglibs (如JSTL )和EL ( expression式语言 ,那些${}东西)诞生以来, JSP中使用scriptlet (那些<% %>东西)的确非常令人沮丧。

scriptlet的主要缺点是:

  1. 可重用性:您不能重用脚本。
  2. 可replace性:您不能使脚本抽象化。
  3. OO能力:你不能使用inheritance/组合。
  4. 可debugging性:如果scriptlet中途抛出一个exception,你只能得到一个空白页面。
  5. 可testing性: scriptlet不是单元可testing的。
  6. 可维护性:每个saldo需要更多的时间来维护混合/杂乱/重复的代码逻辑。

Sun Oracle本身也build议在JSP编码约定中避免使用scriptlet,只要(tag)类可以实现相同的function。 以下是几个相关的例子:

从JSP 1.2规范中,强烈build议在Web应用程序中使用JSP标准标记库(JSTL),以帮助减less对页面中JSP脚本的需求 。 一般来说,使用JSTL的页面更易于阅读和维护。

在可能的情况下,只要标记库提供等效的function,就避免使用JSP脚本 这使得页面更易于阅读和维护,有助于将业务逻辑从表示逻辑中分离出来,并使您的页面更容易演化为JSP 2.0风格的页面(JSP 2.0规范支持,但是强调scriptlet的使用)。

采用模型 – 视图 – 控制器(MVC)devise模式以减less表示层与业务逻辑之间的耦合, JSP脚本不应该用于编写业务逻辑。 相反,必要时使用JSP脚本将从处理客户端请求返回的数据(也称为“值对象”)转换为适当的客户端就绪格式。 即使如此,使用前端控制器servlet或自定义标签也可以做得更好。


如何完全取代scriptlet取决于代码/逻辑的唯一目的。 这个代码经常被放在一个完整的Java类中:

  • 如果您希望在每个请求上调用相同的 Java代码,则无论请求的页面如何,例如检查用户是否已login,然后在doFilter()方法中实施filter并相应地编写代码。 例如:

     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { if (((HttpServletRequest) request).getSession().getAttribute("user") == null) { ((HttpServletResponse) response).sendRedirect("login"); // Not logged in, redirect to login page. } else { chain.doFilter(request, response); // Logged in, just continue request. } } 

    当映射到覆盖感兴趣的JSP页面的适当的<url-pattern> ,则不需要在所有JSP页面上复制同一段代码。


  • 如果你想调用一些Java代码来预处理一个请求,比如从一个数据库中预加载一些列表到一些表中,如果有必要的话,根据一些查询参数,然后在doGet()方法中实现一个servlet并编写相应的代码。 例如:

     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { List<Product> products = productService.list(); // Obtain all products. request.setAttribute("products", products); // Store products in request scope. request.getRequestDispatcher("/WEB-INF/products.jsp").forward(request, response); // Forward to JSP page to display them in a HTML table. } catch (SQLException e) { throw new ServletException("Retrieving products failed!", e); } } 

    这样处理exception就容易了。 在JSP呈现过程中,数据库不会被访问,而是在JSP显示之前。 只要DB访问引发exception,您仍然可以更改响应。 在上面的例子中,将会显示默认的错误500页面,您可以通过web.xml<error-page>进行自定义。


  • 如果你想调用一些Java代码来后处理一个请求,例如处理一个表单提交,然后在doPost()方法中实现一个servlet并相应地编写代码。 例如:

     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username = request.getParameter("username"); String password = request.getParameter("password"); User user = userService.find(username, password); if (user != null) { request.getSession().setAttribute("user", user); // Login user. response.sendRedirect("home"); // Redirect to home page. } else { request.setAttribute("message", "Unknown username/password. Please retry."); // Store error message in request scope. request.getRequestDispatcher("/WEB-INF/login.jsp").forward(request, response); // Forward to JSP page to redisplay login form with error. } } 

    这种方式处理不同的结果页面的目的地更容易:在出现错误的情况下重新显示带有validation错误的表单(在这个特定的例子中,您可以使用EL中的 ${message}重新显示它),或者只是在所需的目标页面的成功。


  • 如果您想调用一些Java代码来控制执行计划和/或请求和响应的目的地,那么请根据MVC的Front Controller Pattern实现一个servlet 。 例如:

     protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { Action action = ActionFactory.getAction(request); String view = action.execute(request, response); if (view.equals(request.getPathInfo().substring(1)) { request.getRequestDispatcher("/WEB-INF/" + view + ".jsp").forward(request, response); } else { response.sendRedirect(view); } } catch (Exception e) { throw new ServletException("Executing action failed.", e); } } 

    或者只是采用像JSF , Spring MVC , Wicket等MVC框架,以便最终只需一个JSP / Facelets页面和一个Javabean类,而不需要定制的servlet。


  • 如果您想要调用一些Java代码来控制 JSP页面中的stream程 ,那么您需要获取(现有的)stream控制标签库,如JSTL内核 。 例如,在表格中显示List<Product>

     <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> ... <table> <c:forEach items="${products}" var="product"> <tr> <td>${product.name}</td> <td>${product.description}</td> <td>${product.price}</td> </tr> </c:forEach> </table> 

    使用适合所有HTML的XML风格的标签,代码比带有不同的开始和结束花括号( “这个结束花括号所在的位置?” )的一串scriptlet具有更好的可读性(因而更好的可维护性)。 一个简单的帮助是configuration您的Web应用程序抛出一个exception,无论何时通过添加下面的片段到web.xml仍然使用scriptlet

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

    在Facelets中 ,作为Java EE提供的MVC框架JSF的一部分的JSP的inheritance者,已经不可能使用scriptlet 。 这样你就自动被迫做“正确的方式”。


  • 如果你想调用一些Java代码来访问和显示 JSP页面中的“后端”数据,那么你需要使用EL(expression式语言)这些${}东西。 例如,重新显示提交的input值:

     <input type="text" name="foo" value="${param.foo}" /> 

    ${param.foo}显示request.getParameter("foo")


  • 如果你想直接在JSP页面调用一些实用 Java代码(通常是public static方法),那么你需要将它们定义为EL函数。 JSTL中有一个标准的函数taglib ,但是你也可以很容易地自己创build函数 。 下面是JSTL fn:escapeXml如何防止XSS 攻击的一个例子。

     <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> ... <input type="text" name="foo" value="${fn:escapeXml(param.foo)}" /> 

    请注意,XSS敏感性与Java / JSP / JSTL / EL无关,无论如何,在开发的每个 Web应用程序中需要考虑这个问题。 scriptlet的问题在于它不提供内build的预防方法,至less不使用标准的Java API。 JSP的继任者Facelets已经隐式的HTML转义,所以你不必担心Facelets中的XSS漏洞。

也可以看看:

  • JSP,Servlet和JSF有什么区别?
  • Servlet,ServletContext,HttpSession和HttpServletRequest / Response如何工作?
  • 具有JSP,Servlet和JDBC的基本MVC示例
  • Java Web应用程序中的devise模式
  • JSP / Servlet的隐藏function

作为保障:禁用好的Scriptlets

另一个问题是讨论,你可以并且总是应该禁用web.xml web应用程序描述符中的scriptlet。

我总是这样做,以防止任何开发人员添加脚本,特别是在大型公司,迟早你会失去概述。 web.xml设置如下所示:

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

JSTL为条件,循环,集合,获取等提供标签。例如:

 <c:if test="${someAttribute == 'something'}"> ... </c:if> 

JSTL使用请求属性 – 它们通常由Servlet设置在请求中,该Servlet 转发给JSP。

我不知道如果我得到这个正确的。

你应该读一些有关MVC的东西。 Spring MVC和Struts 2是两个最常见的解决scheme。

您可以将JSTL标记与ELexpression式一起使用,以避免混杂Java和HTML代码:

 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %> <html> <head> </head> <body> <c:out value="${x + 1}" /> <c:out value="${param.name}" /> // and so on </body> </html> 

还有一些基于组件的框架,例如Wicket ,可以为您生成大量的HTML。 以HTML结尾的标签是非常基本的,实际上没有任何混合的逻辑。结果几乎是空的,就像具有典型HTML元素的HTML页面一样。 缺点是Wicket API中有很多组件需要学习,而在这些限制之下有些事情可能很难实现。

在MVC架构模式中,JSP表示View层。 在JSP中embeddedJava代码被认为是不好的做法。 您可以使用JSTL , freeMarker ,以JSP作为“模板引擎”的速度 。 这些标签的数据提供者依赖于你正在处理的框架Struts 2webwork作为MVC模式的实现使用OGNL “非常有趣的技术将Bean属性公开到JSP”。

经验表明,JSP有一些缺点,其中之一是难以避免混合标记与实际代码。

如果可以的话,那就考虑使用专门的技术来做你需要做的事情。 在Java EE 6中,有JSF 2.0,它提供了很多很好的function,包括通过#{bean.method(argument)}方法将Java bean与JSF页面粘合在一起。

Wicket也是一个完全从HTML中分离出来的替代scheme,所以devise者和程序员可以一起工作,在不同的代码集之间互相理解。

看看Wicket。

如果您只是想避免在JSP中使用Java编码的缺点,那么即使使用脚本也可以这样做。 只要遵循一些规范,就可以在JSP中使用最less的Java,而在JSP页面中几乎不需要计算和逻辑。

 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%//instantiate a JSP controller MyController clr = new MyController(request, response); //process action if any clr.process(request); //process page forwaring if necessary //do all variable assignment here String showMe = clr.getShowMe();%> <html> <head> </head> <body> <form name="frm1"> <p><%= showMe %> <p><% for(String str : clr.listOfStrings()) { %> <p><%= str %><% } %> // and so on </form> </body> </html> 

学习使用JSTL自定义和编写自己的标签

注意EL是EviL (运行时exception,重构)
Wicket也可能是邪恶的(性能,小应用程序或简单的视图层困难)

来自java2s的例子

这必须添加到Web应用程序的web.xml中

 <taglib> <taglib-uri>/java2s</taglib-uri> <taglib-location>/WEB-INF/java2s.tld</taglib-location> </taglib> 

创build文件:java2s.tld在/ WEB-INF /

 <!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd"> <!-- a tab library descriptor --> <taglib xmlns="http://java.sun.com/JSP/TagLibraryDescriptor"> <tlib-version>1.0</tlib-version> <jsp-version>1.2</jsp-version> <short-name>Java2s Simple Tags</short-name> <!-- this tag manipulates its body content by converting it to upper case --> <tag> <name>bodyContentTag</name> <tag-class>com.java2s.BodyContentTag</tag-class> <body-content>JSP</body-content> <attribute> <name>howMany</name> </attribute> </tag> </taglib> 

将下面的代码编译成WEB-INF \ classes \ com \ java2s

 package com.java2s; import java.io.IOException; import javax.servlet.jsp.JspWriter; import javax.servlet.jsp.tagext.BodyContent; import javax.servlet.jsp.tagext.BodyTagSupport; public class BodyContentTag extends BodyTagSupport{ private int iterations, howMany; public void setHowMany(int i){ this.howMany = i; } public void setBodyContent(BodyContent bc){ super.setBodyContent(bc); System.out.println("BodyContent = '" + bc.getString() + "'"); } public int doAfterBody(){ try{ BodyContent bodyContent = super.getBodyContent(); String bodyString = bodyContent.getString(); JspWriter out = bodyContent.getEnclosingWriter(); if ( iterations % 2 == 0 ) out.print(bodyString.toLowerCase()); else out.print(bodyString.toUpperCase()); iterations++; bodyContent.clear(); // empty buffer for next evaluation } catch (IOException e) { System.out.println("Error in BodyContentTag.doAfterBody()" + e.getMessage()); e.printStackTrace(); } // end of catch int retValue = SKIP_BODY; if ( iterations < howMany ) retValue = EVAL_BODY_AGAIN; return retValue; } } 

启动服务器并在浏览器中加载bodyContent.jsp

 <%@ taglib uri="/java2s" prefix="java2s" %> <html> <head> <title>A custom tag: body content</title> </head> <body> This page uses a custom tag manipulates its body content.Here is its output: <ol> <java2s:bodyContentTag howMany="3"> <li>java2s.com</li> </java2s:bodyContentTag> </ol> </body> </html> 

你提出了一个很好的问题,虽然你有很好的答案,但我build议你摆脱JSP。 这是过时的技术,最终会死亡。 使用现代的方法,如模板引擎。 您将会有非常清晰的业务和表示层分离,当然模板中没有Java代码,所以您可以直接从Web演示编辑软件生成模板,大多数情况下都可以利用所见即所得。

当然,远离filter和前后处理,否则你可能会遇到支持/debugging的困难,因为你总是不知道variables的价值在哪里。

不pipe你尝试避免多less,当你和其他开发者一起工作时,他们中的一些人仍然会更喜欢scriptlet,然后把恶意代码插入到项目中。 因此,如果您真的想减lessscriptlet代码,那么在第一个符号中设置项目就非常重要。 有几种技术可以解决这个问题(包括其他提到的几个框架)。 但是,如果您更喜欢纯粹的JSP方式,那么使用JSTL标签文件。 关于这一点的好处是你也可以为你的项目设置母版页,所以其他页面可以inheritance母版页

在WEB-INF /标签下创build一个名为base.tag的母版页,内容如下

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

在这个主页上,我创build了一个名为“title”的片段,这样在子页面中,我可以在主页面的这个位置插入更多的代码。 此外,标签<jsp:doBody/>将被replace为子页面的内容

在WebContent文件夹中创build子页面(child.jsp):

 <%@ taglib prefix="t" tagdir="/WEB-INF/tags" %> <t:base> <jsp:attribute name="title"> <bean:message key="hello.world" /> </jsp:attribute> <jsp:body> [Put your content of the child here] </jsp:body> </t:base> 

<t:base>用于指定要使用的母版页(此时为base.tag)。 标签<jsp:body>所有内容将取代您的母版页上的<jsp:doBody/> 。 您的子页面也可以包含任何标签库,您可以像上面提到的那样正常使用它。 但是,如果在这里使用任何scriptlet代码( <%= request.getParameter("name") %> …)并尝试运行此页面,将会得到JasperException because Scripting elements ( &lt;%!, &lt;jsp:declaration, &lt;%=, &lt;jsp:expression, &lt;%, &lt;jsp:scriptlet ) are disallowed here 。 因此,其他人不可能把邪恶的代码包含到jsp文件中

从您的控制器调用此页面:

您可以轻松地从您的控制器调用child.jsp文件。 这也适用于struts框架

为了避免JSP文件中的java代码java现在提供了像JSTL这样的标签库java还提供了JSF,你可以在其中用标签的forms编写所有的编程结构

在JSP中使用JSTL Tag libraries ,这将是完美的。

只需使用JSTL标签和ELexpression式。

如果有人真的反对使用更多的语言编程 ,我build议GWT,理论上你可以避免所有的JS和HTML元素,因为Google Toolkit将所有的客户端和共享代码转换成JS,你不会有问题,所以你有一个没有任何其他语言编码的web服务。 即使你可以使用某些默认的CSS,如扩展名(smartGWT或Vaadin)。 你不需要学习几十个注释。

当然,如果你愿意的话,你可以深入到代码的深处,注入JS并丰富你的HTML页面,但是如果你愿意的话,你可以避免它,结果将会像在其他框架中写的一样好。 我认为值得一试,基本的GWT是有据可查的。

当然,许多程序员在此描述或推荐了其他几种解决scheme。 GWT适用于那些真正不想处理Web部分或将其最小化的人。

来自Python世界的简洁思想是模板属性语言 ; TAL是由Zope引入的(因此也称为“Zope页面模板”,ZPT),并且是一个标准,在PHP,XSLT和Java中也有实现(我已经使用了Python / Zope和PHP化身)。 在这类模板语言中,上面的例子可能是这样的:

 <table> <tr tal:repeat="product products"> <td tal:content="product/name">Example product</td> <td tal:content="product/description">A nice description</td> <td tal:content="product/price">1.23</td> </tr> </table> 

该代码看起来像普通的HTML(或XHTML)加上XML命名空间中的一些特殊属性; 它可以用浏览器查看,并安全地由devise师调整。 还有对macros和i18n的支持:

 <h1 i18n:translate="">Our special offers</h1> <table> <tr tal:repeat="product products"> <td tal:content="product/name" i18n:translate="">Example product</td> <td tal:content="product/description" i18n:translate="">A nice description</td> <td tal:content="product/price">1.23</td> </tr> </table> 

如果内容的翻译可用,则使用它们。

不过,我不太了解Java的实现 。

在JSP中使用scriptlet并不是一个好的做法。

相反,您可以使用:

  1. JSTL标签
  2. ELexpression式
  3. 自定义标签 – 您可以定义自己的标签来使用。

请参阅:

  1. http://docs.oracle.com/javaee/1.4/tutorial/doc/JSTL3.html
  2. EL

从技术上讲,JSP在运行时都转换为Servlet 。 JSP最初是为了解耦业务逻辑和devise逻辑而创build的,遵循MVC模式。 所以JSP在运行时在技术上都是Java代码。 但是为了回答这个问题,标签库通常用于将逻辑(删除Java代码)应用于JSP页面。

当然,更换<%! 计数器++; %>通过事件生产者 – 消费者体系结构(业务层被通知需要增加计数器),它作出相应的反应,并通知演示者,以便他们更新视图。 涉及到很多数据库交易,因为将来我们需要知道柜台的新旧价值,他们已经增加了这个价值,并且考虑到了什么目的。 显然,序列化涉及到,因为层是完全解耦的。 你将能够通过RMI,IIOP,SOAP增加你的计数器。 但是只有HTML是必需的,你不能实现,因为这是一个普通的情况。 您的新目标是在新的shiny的E7,64GB RAM服务器上每秒达到250个增量。

我有20多年的编程经验,大部分项目在六重奏之前都失败了:可重用性可replace性面向对象的可debugging性可testing性甚至还需要可维护性。 其他项目,由只关心function的人来运行,非常成功。 另外,在项目中实施得太早的僵硬的对象结构使得代码无法适应规范(也称为敏捷)的剧烈变化。

所以我认为拖延是在项目早期或者没有特别要求的时候定义“层”或冗余数据结构的活动。

如果我们在一个Java Web应用程序中使用下面的东西,那么Java代码可以从JSP的前台消除。

  1. 使用MVC体系结构进行Web应用程序

  2. 使用JSP标签

    一个。 标准标签

    湾 自定义标签

  3. expression语言

如何避免JSP文件中的Java代码?

除了expression式语言( EL )之外,您还可以使用选项卡库标记(如JSTL )。 但EL不能很好地处理JSP。 所以,完全放弃JSP并使用Facelets可能会更好。

Facelets是第一个为JSF(Java Server Faces)devise的非JSP页面声明语言,与JSP相比,它为JSF开发人员提供了一个更简单,更强大的编程模型。 它解决了用于Web应用程序开发的JSP中出现的不同问题。

在这里输入图像描述

JSP 2.0有一个名为“Tag Files”的function ,你可以在没有外部java代码和tld情况下编写标签。 您需要创build一个.tag文件并将其放入WEB-INF\tags您甚至可以创build目录结构来打包标签。

例如:

 /WEB-INF/tags/html/label.tag <%@tag description="Rensders a label with required css class" pageEncoding="UTF-8"%> <%@attribute name="name" required="true" description="The label"%> <label class="control-label control-default" id="${name}Label">${name}</label> 

像使用它

 <%@ taglib prefix="h" tagdir="/WEB-INF/tags/html"%> <h:label name="customer name" /> 

您也可以轻松阅读标签主体

 /WEB-INF/tags/html/bold.tag <%@tag description="Bold tag" pageEncoding="UTF-8"%> <b> <jsp:doBody/> </b> 

用它

 <%@ taglib prefix="h" tagdir="/WEB-INF/tags/bold"%> <h:bold>Make me bold</h:bold> 

样本非常简单,但您可以在这里执行许多复杂的任务。 请考虑你可以使用其他标签(例如:具有控制标签的JSTL ,如if/forEcah/chosen文本操作,如format/contains/uppercase ,甚至SQL标签select/update ),传递所有types参数,例如Hashmap ,访问sessionrequest ,…也在你的标签文件中。

标签文件非常容易开发,因为您不需要在更改服务器(如jsp文件)时重新启动服务器。 这使他们容易发展。

即使你使用像struts 2这样的框架,它有许多好的标签,你可能会发现拥有自己的标签可以减less你的代码。 你可以将你的标签参数传给struts,这样就可以定制你的框架标签。

您不仅可以使用标签来避免Java,而且可以最小化您的HTML代码。 我自己尝试检查HTML代码,并尽快build立标签,看到代码重复开始在我的网页。

(即使你最终在你的jsp代码中使用了java,我不希望你可以把这个代码封装在标签中)

尽可能多的回答说,使用JSTL或创build自己的自定义标签。 这里是关于创build自定义标签的好解释

By using JSTL tags together with EL expression you can avoid this. Put the following things in your jsp page:

 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>