Jakarta EE 编程/Jakarta Server Pages 语法
The JavaServer Pages 是一种技术,用于使用 Java servlet 容器将动态内容插入 HTML 或 XML 页面。换句话说,您可以发送 HTML 页面到 web 客户端,这些页面对于每个客户端来说都是不同的,并且每次他们收到它时都是不同的(例如使用数据库数据)。为了解释它,让我们在一个 web 服务器上有一个简单的 HTML 页面。数据流很简单。简而言之,客户端通过发送页面 URL 来请求一个 HTML 页面,服务器返回给定的 HTML 页面
客户端 | 服务器 | |||
URL | ||||
检索 该 文件 | ||||
以下是 HTML 文件和客户端上的显示
HTML 页面 | 显示 |
<html>
<body>
<span style="color: blue;">The current time</span>
</body>
</html>
|
当前时间 |
返回的页面始终相同。现在我们想要显示当前时间。为此,我们在 web 服务器上添加了一个 servlet 容器。HTML 页面将不再存在于服务器上。当页面被客户端请求时,一个 Java 类将生成 HTML 页面,并且 HTML 页面将被发送到客户端。这个 Java 类被称为一个 *servlet*
客户端 | Web 服务器 | Servlet 容器 | |||||
URL | |||||||
询问 到 生成 该 文件 |
|||||||
执行 该 Java 类 |
|||||||
以下是 Java 类、生成的 HTML 文件和客户端上的显示
Servlet | HTML 页面 | 显示 |
package jsp_servlet;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
class _myservlet implements javax.servlet.Servlet, javax.servlet.jsp.HttpJspPage {
public void _jspService(javax.servlet.http.HttpServletRequest request,
javax.servlet.http.HttpServletResponse response)
throws javax.servlet.ServletException,
java.io.IOException {
javax.servlet.ServletConfig config = …; // Get the servlet config
Object page = this;
PageContext pageContext = …; // Get the page context for this request
javax.servlet.jsp.JspWriter out = pageContext.getOut();
HttpSession session = request.getSession(true);
try {
out.print("<html>\r\n");
out.print("<body>\r\n");
out.print("<span style="color: blue;">");
DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
Calendar cal = Calendar.getInstance();
out.print(dateFormat.format(cal.getTime()));
out.print("</span>\r\n");
out.print( "</body>\r\n" );
out.print( "</html>\r\n" );
…
} catch (Exception _exception) {
// Clean up and redirect to error page in <%@ page errorPage="myerror.jsp" %>
}
}
}
|
<html>
|
2024/10/24 14:47:26 |
现在页面是动态的,但 servlet 很难编码和阅读。因此,我们将使用一个 *JavaServer Page* (JSP)。JSP 写起来像 HTML 页面一样。它具有用于 HTML 页面的文件扩展名 `。jsp` 和用于 XML 标记页面的文件扩展名 `。jspx`。除了 HTML 语法之外,它还嵌入了 *脚本片段* 和 *jsp 标签*。脚本片段是 Java 代码的一部分。servlet 容器从 JSP 生成一个 servlet,该 servlet 将用于生成 HTML 页面或 XML 内容。HTML 标记将保持原样。嵌入的脚本片段被插入到 servlet 的代码中。jsp 标签被转换为 Java 代码。因此,让我们使用 JSP
客户端 | Web 服务器 | Servlet 容器 | |||||
URL | |||||||
询问 到 生成 该 文件 |
|||||||
生成 该 servlet | |||||||
执行 该 Java 类 | |||||||
实际上,servlet 仅在第一次请求调用时由 JSP 生成。之后会重复使用相同的 servlet。当 JSP 发生变化时,它会被再次生成。现在我们有一个 JSP,它生成一个 servlet,它生成一个 HTML 页面,该页面将显示给客户端
JSP | Servlet | HTML 页面 | 显示 |
<%@ page errorPage="myerror.jsp" %>
<%@ page import="java.text.DateFormat" %>
<%@ page import="java.text.SimpleDateFormat" %>
<%@ page import="java.util.Calendar" %>
<html>
<body>
<% DateFormat dateFormat =
new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
Calendar cal = Calendar.getInstance();
%>
<span style="color: blue;">
<%= dateFormat.format(cal.getTime()) %>
</span>
</body>
</html>
|
package jsp_servlet;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
class _myservlet implements javax.servlet.Servlet,
javax.servlet.jsp.HttpJspPage {
public void _jspService(
javax.servlet.http.HttpServletRequest request,
javax.servlet.http.HttpServletResponse response)
throws javax.servlet.ServletException,
java.io.IOException {
javax.servlet.ServletConfig config = …;
Object page = this;
PageContext pageContext = …;
javax.servlet.jsp.JspWriter out =
pageContext.getOut();
HttpSession session = request.getSession(true);
try {
DateFormat dateFormat =
new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
Calendar cal = Calendar.getInstance();
out.print("<html>\r\n");
out.print("<body>\r\n");
out.print("<span style="color: blue;">\r\n");
out.print(dateFormat.format(cal.getTime()));
out.print("\r\n");
out.print("</span>\r\n");
out.print( "</body>\r\n" );
out.print( "</html>\r\n" );
…
} catch (Exception _exception) {
}
}
}
|
<html>
|
2024/10/24 14:47:26 |
有 *三种* 基本的脚本元素类型,允许将 java 代码直接插入到 JSP 中。
- 一个 *声明* 标签将变量定义放在 java servlet 类的主体内部。也可以定义静态数据成员。还可以在这里定义内部类。
<%! int serverInstanceVariable = 1; %>
声明标签还允许定义方法。
<%! /** * Converts the Object into a string or if * the Object is null, it returns the empty string. */ public String toStringOrBlank(Object obj) { if (obj != null) { return obj.toString(); } return ""; } %>
- 一个 *脚本片段* 标签将它包含的所有语句放在 java servlet 类中 `_jspService()` 方法的内部。
<% int localStackBasedVariable = 1; out.println(localStackBasedVariable); %>
- 一个 *表达式* 标签将要计算的表达式放在 java servlet 类的内部。表达式不应以分号结尾。
<%= "expanded inline data " + 1 %>
- 一个 *注释* 标签什么也不做。它被忽略了。它允许您对文件进行文档化。它与 HTML 注释不同,因为 HTML 注释 (
<!-- -->
) 将出现在生成的 HTML 文件中。
<%-- This is my first JSP. --%>
JSP 指令添加到 JSP 页面的顶部。这些指令控制 JSP 编译器如何生成 servlet。以下指令可用
- 包含
- include 指令通知 JSP 编译器将一个完整的文件包含到当前文件中。就像将包含文件的内容直接粘贴到原始文件中一样。此功能类似于 C 预处理器提供的功能。包含的文件通常具有扩展名“jspf”(代表 JSP **片段**)
<%@ include file="somefile.jspf" %>
- 页面
- page 指令具有几个属性
导入 |
导致在结果文件中插入一个 Java `import` 语句。 |
contentType |
指定生成的內容。如果未使用 HTML 或字符集不是默认字符集,则应使用它。 |
errorPage |
指示在处理 HTTP 请求时发生异常时应该显示的页面的地址。 |
isErrorPage |
如果设置为 true,则表示这是错误页面。默认值为 *false*。 |
isThreadSafe |
如果结果 servlet 必须是线程安全的,则为 true。 |
autoFlush |
自动刷新内容。值为 true(默认值)表示缓冲区已满时应刷新缓冲区。值为 false(很少使用)表示缓冲区溢出时应抛出异常。当也使用 buffer="none" 时,值为 false 是非法的。 |
会话 |
为了维护会话。值为 true(默认值)表示预定义变量 session(类型为 `HttpSession`)应绑定到现有会话(如果存在),否则应创建一个新会话并将其绑定到它。值为 false 表示不使用任何会话,尝试访问变量 session 将导致在 JSP 页面被翻译成 servlet 时出现错误。 |
缓冲区 |
设置缓冲区大小。默认值为 8k,建议您增加它。 |
isELIgnored |
定义当 JSP 被翻译时是否忽略 表达式语言 (EL) 表达式。 |
语言 |
定义脚本片段、表达式和声明中使用的脚本语言。目前,唯一可能的值是“java”。 |
扩展 |
定义此 JSP 将成为的类的超类。除非您 *确实* 知道自己在做什么,否则不要使用它 - 它会覆盖容器提供的类层次结构。 |
信息 |
定义一个字符串,该字符串被放入翻译后的页面中,以便您可以使用生成的 servlet 的继承的 `getServletInfo()` 方法获取它。 |
pageEncoding |
定义 JSP 的字符编码。默认值为“ISO-8859-1”(除非 contentType 属性已定义字符编码,或者页面使用 XML 文档语法)。 |
<%@ page import="java.util.*" %> <%-- example import --%> <%@ page contentType="text/html" %> <%-- example contentType --%> <%@ page isErrorPage="false" %> <%-- example for non error page --%> <%@ page isThreadSafe="true" %> <%-- example for a thread safe JSP --%> <%@ page session="true" %> <%-- example for using session binding --%> <%@ page autoFlush="true" %> <%-- example for setting autoFlush --%> <%@ page buffer="20kb" %> <%-- example for setting Buffer Size --%>
- **注意:** 只有“import”页面指令可以在同一个 JSP 中多次使用。
- taglib
- taglib 指令表示要使用 JSP 标签库。该指令需要一个前缀(类似于 C++ 中的命名空间)和标签库描述的 URI。
<%@ taglib prefix="myprefix" uri="taglib/mytag.tld" %>
无论 JSP 编译器是否为 servlet 生成 Java 源代码或直接发出字节码,了解 JSP 编译器如何将页面转换为 Java servlet 都是有帮助的。例如,考虑以下输入 JSP 及其生成的 Java Servlet。
输入 JSP | 生成的 servlet |
<%@ page errorPage="myerror.jsp" %>
<%@ page import="com.foo.bar" %>
<html>
<head>
<%! int serverInstanceVariable = 1;%>
<% int localStackBasedVariable = 1; %>
</head>
<body>
<table>
<%-- This will be ignored. --%>
<tr><td><%= toStringOrBlank( "expanded inline data " + 1 ) %></td></tr>
</table>
</body>
</html>
|
package jsp_servlet;
import java.util.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
import com.foo.bar; // Imported as a result of <%@ page import="com.foo.bar" %>
import …
class _myservlet implements javax.servlet.Servlet, javax.servlet.jsp.HttpJspPage {
// Inserted as a
// result of <%! int serverInstanceVariable = 1;%>
int serverInstanceVariable = 1;
…
public void _jspService(javax.servlet.http.HttpServletRequest request,
javax.servlet.http.HttpServletResponse response)
throws javax.servlet.ServletException,
java.io.IOException {
javax.servlet.ServletConfig config = …; // Get the servlet config
Object page = this;
PageContext pageContext = …; // Get the page context for this request
javax.servlet.jsp.JspWriter out = pageContext.getOut();
HttpSession session = request.getSession(true);
try {
out.print("<html>\r\n");
out.print("<head>\r\n");
…
// From <% int localStackBasedVariable = 1; %>
int localStackBasedVariable = 1;
…
out.print("</head>\r\n");
out.print("<body>\r\n");
out.print("<table>\r\n");
out.print(" <tr><td>");
// From <%= toStringOrBlank( "expanded inline data " + 1) %>
out.print(toStringOrBlank( "expanded inline data " + 1 ));
out.print(" </td></tr>\r\n");
out.print("</table>\r\n");
out.print("</body>\r\n");
out.print("</html>\r\n");
…
} catch (Exception _exception) {
// Clean up and redirect to error page in <%@ page errorPage="myerror.jsp" %>
}
}
}
|
我们已经看到,我们可以在脚本中声明对象,供以后使用。还有一些已经声明的对象可供程序员使用。它们被称为*隐式对象*。
out |
用于将数据写入响应流的JspWriter。 |
页面 |
servlet 本身。 |
pageContext |
包含与整个页面相关联的数据的PageContext 实例。给定的 HTML 页面可以在多个 JSP 之间传递。 |
request |
提供 HTTP 请求信息的HttpServletRequest 对象。 |
response |
可用于将数据发送回客户端的HttpServletResponse 对象。 |
会话 |
可用于跟踪从一个请求到另一个请求的用户信息的HttpSession 对象。 |
config |
提供 servlet 配置数据。 |
application |
应用程序中所有 JSP 和 servlet 共享的数据。 |
exception |
应用程序代码未捕获的异常。 |
JSP 操作是 XML 标记,用于调用内置的 Web 服务器功能。它们在运行时执行。有些是标准的,有些是自定义的(由 Java 开发人员开发)。以下是标准操作。
与 include 指令类似,此标记将指定的 jsp 包含到返回的 HTML 页面中,但其工作原理不同。Java servlet 会暂时将请求和响应传递给指定的 JavaServer 页面。一旦另一个 JSP 完成,控制权将返回到当前 JSP。使用此方法,JSP 代码将在多个其他 JSP 之间共享,而不是重复。
<html>
<head></head>
<body>
<jsp:include page="mycommon.jsp" >
<jsp:param name="extraparam" value="myvalue" />
</jsp:include>
name:<%=request.getParameter("extraparam")%>
</body>
</html>
可以在jsp:include
、jsp:forward
或jsp:params
块中使用。指定将添加到请求当前参数的参数。
用于将请求和响应传递给另一个 JSP 或 servlet。控制权永远不会返回到当前 JSP。
<jsp:forward page="subpage.jsp" >
<jsp:param name="forwardedFrom" value="this.jsp" />
</jsp:forward>
在此转发示例中,请求将转发到subpage.jsp
。
旧版本的 Netscape Navigator 和 Internet Explorer 使用不同的标记来嵌入小程序。此操作生成浏览器特定的标记,用于包含小程序。插件示例说明了在网页中嵌入小程序的 HTML 统一方式。在<OBJECT>
标记出现之前,没有通用的嵌入小程序的方式。
<jsp:plugin type=applet height="100%" width="100%"
archive="myjarfile.jar, myotherjar.jar"
codebase="/applets"
code="com.foo.MyApplet" >
<jsp:params>
<jsp:param name="enableDebug" value="true" />
</jsp:params>
<jsp:fallback>
Your browser does not support applets.
</jsp:fallback>
</jsp:plugin>
目前,jsp:plugin
标记不允许动态调用小程序。例如,jsp:params
不能与需要将数据点作为参数传递的图表小程序一起使用,除非数据点的数量是恒定的。例如,您不能遍历 ResultSet 来创建jsp:param
标记。每个jsp:param
标记都必须手动编码。
如果浏览器不支持小程序,则显示的内容。
从指定的 JavaBean 获取属性。
除了预定义的 JSP 操作之外,开发人员还可以使用 JSP 标记扩展 API 添加自己的自定义*操作*。开发人员编写一个 Java 类,该类实现其中一个 Tag 接口,并提供一个标记库 XML 描述文件,该文件指定标记和实现这些标记的 java 类。
考虑以下 JSP。
<%@ taglib uri="mytaglib.tld" prefix="myprefix" %> … <myprefix:myaction> <%-- The start tag --%> … </myprefix:myaction> <%-- The end tag --%> …
JSP 编译器将加载 mytaglib.tld XML 文件。
<taglib>
<tlib-version>1.0</tlib-version>
<jsp-version>2.0</jsp-version>
<short-name>My taglib</short-name>
<tag>
<name>myaction</name>
<tag-class>org.wikibooks.en.MyActionTag</tag-class>
<body-content>empty</body-content>
</tag>
</taglib>
JSP 编译器将看到标记myaction
由 java 类MyActionTag
实现。
public class MyActionTag extends TagSupport {
public MyActionTag() { … }
// Releases all instance variables.
public void release() { … }
// Called for the start tag
public int doStartTag() { … }
// Called at the end tag
public int doEndTag() { … }
}
标记在文件中首次使用时,它将创建一个MyActionTag
实例。然后(以及每次使用该标记时),当它遇到开始标记时,它将调用方法doStartTag()
。它查看开始标记的结果,并确定如何处理标记的主体。主体是开始标记和结束标记之间的文本。doStartTag()
方法可以返回以下值之一
SKIP_BODY |
不会处理标记之间的主体。 |
EVAL_BODY_INCLUDE |
评估标记的主体。 |
EVAL_BODY_TAG |
评估标记的主体并将结果推送到流中(存储在标记的主体内容属性中)。 |
注意:如果标记扩展了BodyTagSupport
类,则在调用doEndTag()
之前,将在主体处理完成后调用方法doAfterBody()
。此方法用于实现循环结构。
当它遇到结束标记时,它将调用doEndTag()
方法。该方法可以返回以下两个值之一
EVAL_PAGE |
这表示应处理 JSP 文件的其余部分。 |
SKIP_PAGE |
这表示不应进行任何进一步的处理。控制权离开 JSP 页面。这就是用于转发操作的方法。 |
如果要迭代主体几次,则 Java 类(标记处理程序)必须实现IterationTag
接口。它返回EVAL_BODY_AGAIN
- 表示再次调用主体。
JavaServer Pages 标准标记库 (JSTL) 是 Java EE Web 应用程序开发平台的一个组件。它通过添加 JSP 标记的标记库来扩展 JSP 规范,这些标记库用于常见任务,例如 XML 数据处理、条件执行、循环和国际化。
Struts 项目包含Struts 标记库,其中大部分在独立于 Struts 架构的情况下很有用。
JSP 中的国际化与普通 Java 应用程序中的国际化方式相同,即使用资源束。
表达式语言
[edit | edit source]表达式语言 (EL) 是一个更快/更简单的显示参数值的方式。它自 JSP 2.0 起可用。例如,它允许开发者创建 Velocity 风格的模板。
Hello, ${param.visitor}
与以下代码相同
Hello, <%=request.getParameter("visitor")%>
它还提供了一种更清晰的方式来遍历嵌套 bean。考虑一些 bean
class Person {
String name;
// Person nests an organization bean.
Organization organization;
public String getName() { return this.name; }
public Organization getOrganization() { return this.organization; }
}
class Organization {
String name;
public String getName() { return this.name; }
}
然后,如果一个 Person 实例要被放置到一个名为 "person" 的请求属性中,JSP 将有
Hello, ${person.name}, of company ${person.organization.name}
与以下代码相同。
Hello,
<% Person p = (Person) request.getAttribute("person");
if (p != null) {
out.print(p.getName());
}
%>, of company
<% if ((p != null) && (p.getOrganization() != null)) {
out.print(p.getOrganization().getName());
}
%>
Java EE 5 平台中的 JSP 技术
[edit | edit source]Java EE 5 的重点是通过使用 Java 语言注解来简化开发,这些注解是在 J2SE 5.0 中引入的。JSP 2.1 通过定义用于 JSP 标签处理程序和上下文监听器的依赖注入的注解来支持这个目标。
Java EE 5 规范的另一个关键关注点是使其 Web 层技术(即 JavaServer Pages (JSP)、JavaServer Faces (JSF) 和 JavaServer Pages Standard Tag Library (JSTL))保持一致。
这种对齐工作的成果是统一表达式语言 (EL),它集成了 JSP 2.0 和 JSF 1.1 定义的表达式语言。
统一 EL 中从对齐工作中产生的主要关键补充是:一个可插拔的 API,用于将变量引用解析为 Java 对象,以及用于解析应用于这些 Java 对象的属性;对延迟表达式的支持,这些表达式可以在需要时由标签处理程序进行评估,不同于它们的常规表达式对应物,这些表达式在页面执行和渲染时立即进行评估;对 lvalue 表达式的支持,这些表达式出现在赋值操作的左侧。当用作 lvalue 时,EL 表达式代表对数据结构(例如:JavaBeans 属性)的引用,该数据结构被分配了一些用户输入。统一 EL 在其自身的规范文档中定义,该文档与 JSP 2.1 规范一起提供。
由于统一 EL 的存在,JSTL 标签(例如 JSTL 迭代标签)可以以直观的方式与 JSF 组件一起使用。
JSP 2.1 利用 Servlet 2.5 规范来实现其 Web 语义。
模型-视图-控制器模式
[edit | edit source]模型-视图-控制器模式可以与 JSP 文件一起使用,以便将表示与请求处理和计算机数据存储分离。使用常规 servlet 或单独的 JSP 文件来处理请求。请求处理完成后,控制权将传递给仅用于创建输出的 JSP。有几个基于模型-视图-控制器模式的平台用于 Web 层(如 Barracuda、Apache Struts、Stripes 和 Spring MVC 框架)。
环境
[edit | edit source]Java 服务器 (J2EE 规范) 和页面脚本和/或添加的扩展定制编程都通过 (在被加载的程序的运行时上下文中使用) 一个称为虚拟机的预安装基础程序与主机操作系统集成,这种类型是 Java 虚拟机 (JVM)。
因为无论是编译器-JVM 集 (称为 SDK 或 JDK) 还是独立的 JVM (称为 JRE,Java 运行时环境) 都是为大多数计算机平台操作系统而制作的,并且为 JVM 编译的程序被编译成 JVM 的特殊 Java 字节码文件,字节码文件 (编译的 Java 程序 .class 文件) 可以有效地在平台之间传输,无需重新编译,除非版本兼容性或特殊情况。这些 J2EE servlet 或 J2EE JSP 程序的源代码几乎总是与 J2EE JSP 材料和 J2EE Web 应用程序一起提供,因为服务器在加载它们时必须调用编译器。这些小的扩展程序 (自定义标签、servlet、bean、页面脚本) 是可变的,并且很可能在运行时之前或间歇性地更新或更改,但特别是在发送 JSP 页面请求时,它需要 JSP 服务器访问 Java 编译器 (SDK 或 JDK) 和所需的源代码 (不仅仅是 JVM JRE 和字节码类文件) 才能成功利用服务方法。
JSP 语法有两种基本形式,脚本和标记,尽管从根本上说,页面是 HTML 或 XML 标记。脚本标记 (称为脚本元素) (分隔) 代码块 与标记不是有效的标记,并且允许任何与 java 服务器相关的 API (例如,运行的二进制文件本身或数据库连接 API 或 java 邮件 API) 或更专业的 JSP API 语言代码被嵌入到 HTML 或 XML 页面中,前提是 JSP 文件中使用了正确的声明,并且使用了页面的文件扩展名。脚本块不需要在块本身内完成,只需要块本身的最后一行在语法上正确地作为语句完成,它可以在后面的块中完成。这种分割的内联代码部分系统被称为跨越脚本,因为它可以通过跨越静态标记来进行包装。在运行时 (在客户端请求期间),代码被编译和评估,但代码的编译通常只发生在对文件代码进行更改时。