我是Java EE的新手,我知道类似以下三行内容
<%= x+1 %>
<%= request.getParameter("name") %>
<%! counter++; %>
这是一种古老的编码方式,在JSP版本2中,存在一种避免在JSP文件中使用Java代码的方法。有人可以告诉我替代的JSP 2行吗,这种技术叫什么?
我是Java EE的新手,我知道类似以下三行内容
<%= x+1 %>
<%= request.getParameter("name") %>
<%! counter++; %>
这是一种古老的编码方式,在JSP版本2中,存在一种避免在JSP文件中使用Java代码的方法。有人可以告诉我替代的JSP 2行吗,这种技术叫什么?
Answers:
自从标签库(例如JSTL)和EL(表达语言,那些东西)的诞生可追溯到2001年以来,在JSP中确实不鼓励使用scriptlet(那些<% %>
东西)。${}
scriptlet的主要缺点是:
Sun Oracle本身还建议在JSP编码约定中避免使用(标记)类可能具有相同功能的scriptlet。以下是一些相关的引用:
从JSP 1.2规范开始,强烈建议在您的Web应用程序中使用JSP标准标记库(JSTL),以帮助减少页面中对JSP scriptlet的需求。通常,使用JSTL的页面更易于阅读和维护。
...
只要有可能,只要标签库提供等效功能,就避免使用JSP scriptlet。这使页面更易于阅读和维护,有助于将业务逻辑与表示逻辑分离,并使您的页面更易于演变为JSP 2.0样式的页面(JSP 2.0规范支持但不强调脚本的使用)。
...
本着采用模型视图控制器(MVC)设计模式来减少表示层与业务逻辑之间的耦合的精神,不应将JSP脚本集用于编写业务逻辑。而是,如果有必要,可以使用JSP scriptlet将处理客户机的请求返回的数据(也称为“值对象”)转换为适当的客户机就绪格式。即使这样,最好还是使用前端控制器servlet或自定义标签来完成。
如何完全替换scriptlet取决于代码/逻辑的唯一目的。这段代码比以往更多地被放置在完全值得的Java类中:
如果要在每个请求上调用相同的 Java代码,则无论请求页面是多少,都应或多或少地调用它,例如,检查用户是否登录,然后实现过滤器,并在方法中相应地编写代码。例如:doFilter()
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.
}
}
当映射到适当的<url-pattern>
覆盖感兴趣的JSP页面时,则无需在整个JSP页面上复制粘贴同一段代码。
如果要调用某些Java代码来预处理请求,例如,如果需要,则基于某些查询参数,从数据库中预加载一些列表以显示在某个表中,则可以实现servlet,并在doGet()
方法中相应地编写代码。例如:
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);
}
}
这种处理异常的方法比较容易。在JSP渲染过程中,不能访问DB,但是要在显示JSP之前就可以访问DB。每当数据库访问引发异常时,您仍然可以更改响应。在上面的示例中,将显示默认错误500页面,您可以通过<error-page>
in对其进行自定义web.xml
。
如果要调用一些Java代码来对请求进行后处理(例如,处理表单提交),请实现Servlet并相应地在doPost()
方法中编写代码。例如:
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.
}
}
这种处理不同结果页面目标的方法更加容易:在出现错误的情况下重新显示带有验证错误的表单(在此特定示例中,您可以${message}
在EL中使用它重新显示它),或者在成功的情况下仅进入所需的目标页面。
如果要调用一些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);
}
}
或者只是采用MVC框架(如JSF,Spring MVC,Wicket等等),这样就只需要JSP / Facelets页面和JavaBean类即可,而无需自定义servlet。
如果要调用一些Java代码来控制 JSP页面内的流,则需要获取一个(现有的)流控制标记库,例如JSTL core。例如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>
使用XML样式的标签非常适合所有HTML,与一堆带有各种开合和结束大括号的脚本(更好的可读性,因此可维护性)相比,代码更易读(“此结束大括号到底在哪里?”)。一种简单的帮助是将Web应用程序配置为在仍然使用scriptlet的情况下抛出异常,方法是将以下内容添加到web.xml
:
<jsp-config>
<jsp-property-group>
<url-pattern>*.jsp</url-pattern>
<scripting-invalid>true</scripting-invalid>
</jsp-property-group>
</jsp-config>
在Facelets的,JSP的继任者,它是Java EE的一部分提供的MVC框架JSF,它已经不能够使用小脚本。这样,您将自动被迫以“正确的方式”做事。
如果要调用某些Java代码来访问和显示 JSP页面内的“后端”数据,则需要使用EL(表达语言)之类的${}
东西。例如,重新显示提交的输入值:
<input type="text" name="foo" value="${param.foo}" />
该${param.foo}
显示器的结果request.getParameter("foo")
。
如果要直接在JSP页面中调用一些实用 Java代码(通常是public static
方法),则需要将它们定义为EL函数。JSTL中有一个标准函数taglib,但是您也可以轻松地自己创建函数。这是一个示例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的问题在于,它无法提供内置的预防措施,至少没有使用标准的Java API。JSP的继任者Facelets已经具有隐式HTML转义,因此您不必担心Facelets中的XSS漏洞。
<% response.getWriter().flush(); %>
在</head>
和之间<body>
改善脚本浏览器中的网页解析性能。但是,当服务器端的输出缓冲区大小较小(1〜2KB)时,此使用可以完全忽略。另请参阅本文。
保障措施:永久禁用脚本
在讨论另一个问题时,您可以并且始终应该在web.xml
Web应用程序描述符中禁用scriptlet 。
我将始终这样做,以防止任何开发人员添加scriptlet,尤其是在大型公司中,您迟早会失去概述。该web.xml
设置是这样的:
<jsp-config>
<jsp-property-group>
<url-pattern>*.jsp</url-pattern>
<scripting-invalid>true</scripting-invalid>
</jsp-property-group>
</jsp-config>
<%-- comment that i don't want in the final HTML --%>
。我发现使用这些注释而不是HTML注释很有用。
JSTL为条件,循环,集合,获取等提供标签。例如:
<c:if test="${someAttribute == 'something'}">
...
</c:if>
JSTL与请求属性一起使用-它们通常由Servlet在请求中设置,然后将其转发到JSP。
我不确定我是否正确。
您应该阅读有关MVC的内容。Spring MVC和Struts 2是两个最常见的解决方案。
可以将JSTL标记与EL表达式一起使用,以避免将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>
在MVC体系结构模式中,JSP代表视图层。在JSP中嵌入Java代码被认为是不好的做法。您可以使用JSTL,FreeMarker的,速度与JSP作为“模板引擎”。这些标签的数据提供者取决于您正在处理的框架。Struts 2
和webwork
作为MVC模式的实现使用OGNL “非常有趣的技术暴露bean的属性给JSP”。
经验表明,JSP有一些缺点,其中之一就是很难避免将标记与实际代码混合在一起。
如果可以,请考虑使用专业技术来完成您的工作。在Java EE 6中提供了JSF 2.0,它提供了许多不错的功能,包括通过该#{bean.method(argument)}
方法将Java Bean与JSF页面粘合在一起。
如果您只是想避免JSP中Java编码的弊端,即使使用scriplet,也可以这样做。只需遵循一些规则即可在JSP中使用最少的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(运行时异常,重构)
Wicket也可能是邪恶的(性能,对于小型应用程序或简单的视图层来说很麻烦)java2s中的
示例,
这必须添加到Web应用程序的web.xml中
<taglib>
<taglib-uri>/java2s</taglib-uri>
<taglib-location>/WEB-INF/java2s.tld</taglib-location>
</taglib>
在/ WEB-INF /中创建File:java2s.tld
<!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>
您提出了一个很好的问题,尽管您得到了很好的答案,但我建议您摆脱JSP。过时的技术最终将消失。使用模板引擎等现代方法。您将业务层和表示层非常清楚地分开,并且模板中肯定没有Java代码,因此在大多数情况下,利用所见即所得,您可以直接从Web表示编辑软件生成模板。
并且一定要远离过滤器以及预处理和后期处理,否则您可能会遇到支持/调试难题,因为您始终不知道变量从何处获取值。
无论您试图避免多少,当您与其他开发人员一起工作时,其中一些仍然会喜欢scriptlet,然后将恶意代码插入到项目中。因此,如果您真的想减少scriptlet代码,那么在第一个标志处设置项目就非常重要。有几种技术可以克服这一问题(包括其他提到的几个框架)。但是,如果您喜欢纯JSP方式,请使用JSTL标记文件。这样做的好处是您还可以为项目设置母版页,以便其他页面可以继承母版页
在您的WEB-INF / tags下创建一个名为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>
在此母版页上,我创建了一个名为“标题”的片段,以便在子页上可以将更多代码插入母版页的此位置。同样,标签<jsp:doBody/>
将被子页面的内容替换
在您的WebContent文件夹中创建子页面(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/>
您的母版页上的。您的子页面也可以包含任何标签库,您可以像上面提到的其他标签库一样正常使用它。但是,如果您在此处(<%= request.getParameter("name") %>
...)使用任何scriptlet代码并尝试运行此页面,则将获得一个JasperException because Scripting elements ( <%!, <jsp:declaration, <%=, <jsp:expression, <%, <jsp:scriptlet ) are disallowed here
。因此,其他任何人都无法将邪恶代码包含在jsp文件中
从您的控制器调用此页面:
您可以从控制器轻松调用child.jsp文件。这在struts框架中也很好用
JSTL Tag libraries
在JSP中使用,将可以完美地工作。
如果有人真的反对使用一种以上的语言进行编程,我建议您使用GWT,从理论上讲,您可以避免使用所有JS和HTML元素,因为Google工具包将所有客户端代码和共享代码转换为JS,因此您不会遇到任何问题,因此您有一个没有使用任何其他语言编码的Web服务。甚至您也可以从扩展名(smartGWT或Vaadin)指定的位置使用某些默认CSS。您无需学习许多注释。
当然,如果愿意,您可以深入代码的深处,注入JS并丰富HTML页面,但实际上,如果愿意,可以避免使用它,其结果将是在任何其他框架中编写的。我说值得一试,基本的GWT有据可查。
当然,许多其他程序员在此描述或推荐了其他几种解决方案。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名称空间中的一些特殊属性。可以使用浏览器进行查看,并且可以由设计师安全地进行调整。同时支持宏和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实现并不了解很多。
tal:replace="structure (expression)"
属性一样。
在JSP中使用scriptlet并不是一个好习惯。
相反,您可以使用:
请参阅:
确定,更换 <%! counter++; %>
事件生产者-消费者体系结构,该体系结构将通知业务层需要增加计数器的信息,并相应地做出反应,并通知展示者,以便他们更新视图。涉及许多数据库事务,因为将来我们将需要知道计数器的新值和旧值,谁增加了计数器以及其目的是什么。显然,涉及序列化,因为这些层是完全解耦的。您将可以通过RMI,IIOP,SOAP来增加计数器。但是只有HTML是必需的,您不必实现,因为这是一种平凡的情况。您的新目标是在新的闪亮E7 64GB RAM服务器上达到每秒250个增量。
我从事编程已有20多年的经验,大多数项目在六重奏之前就失败了:可重用性可替换性OO能力可调试性可测试性甚至需要可维护性。由仅关心功能性的人员运行的其他项目非常成功。同样,在项目中实施得太早的硬对象结构使代码无法适应规范的急剧变化(又称敏捷)。
因此,我将在项目初期或在没有特别要求时定义“层”或冗余数据结构的活动视为拖延。
如果我们在Java Web应用程序中使用以下内容,则可以从JSP的前台中删除Java代码。
将MVC架构用于Web应用程序
使用JSP标签
一个。标准标签
b。自定义标签
表达语言
JSP 2.0具有称为“标记文件”的功能,您可以编写标记而无需外部java
代码和tld
。您需要创建一个.tag
文件并将其放入,WEB-INF\tags
甚至可以创建目录结构来打包标签。
例如:
/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
),传递所有种类的参数,例如Hashmap
,access session
,request
...在你的标签文件了。
标签文件 非常容易开发,因为更改它们时无需像JSP文件那样重新启动服务器。这使它们易于开发。
即使您使用诸如Struts 2这样的框架,该框架具有许多好的标签,您可能会发现拥有自己的标签可以大大减少代码。您可以将标签参数传递给struts,这样可以自定义框架标签。
您不仅可以使用标记来避免使用Java,还可以最小化HTML代码。我自己会尝试查看HTML代码并在看到页面中重复的代码后立即构建标签很多。
(即使您最终在JSP代码中使用Java,但我希望不要在Java代码中使用Java,您也可以将该代码封装在标签中)