javaserver_JavaServer页面简介

关于本教程

本教程是关于什么的?

本教程介绍了JavaServer Pages(JSP)技术2.0的基础,并提供了由Noel J. Bergman撰写的讨论JSP 1.2的原始教程的更新。 本教程将使您扎实地掌握JSP基础知识,并使您能够开始编写自己的JSP解决方案。 特别是,本教程将:

  • 讨论定义JSP的基本元素,例如概念,语法和语义。
  • 识别并举例说明每个元素。
  • 使用简短,具体的主题示例来说明每个元素,并清楚地阐明与该元素相关的重要问题。

我应该学习本教程吗?

这是一个入门教程,适用于对Java编程和HTML有扎实了解的新手或新手JSP程序员。 重点在于简单的解释,而不是详尽地涵盖每个选项。 如果您是JSP专家,那么本教程可能不适合您。 但是,它将为希望从较早版本过渡到JSP 2.0的博学的开发人员提供参考。

注意,本教程严格地是JSP语法和语义的概述。 它不解决编程风格。

Web容器要求

对于独立的容器,JSP 2.0规范要求使用J2SE 1.3或更高版本,对于作为J2EE 1.4环境的一部分的容器,则需要版本1.4。 要开始使用JSP 2.0,Web容器还必须支持Java Servlet 2.4规范。 所有JSP容器都必须能够在J2SE 1.4环境中运行。

同样,JSP 2.0规范将Servlet 2.4规范用于其Web语义。

JSP概述

背景

JSP是网站开发人员工具箱中最强大,易于使用的基础工具之一。 JSP将HTML和XML与Java TM servlet(服务器应用程序扩展)和JavaBeans技术结合在一起,从而创建了一个高效的环境,用于开发和部署可靠,交互式,高性能的独立于平台的网站。

JSP有助于在服务器上创建动态内容。 它是Java平台针对服务器端编程的集成解决方案的一部分,该解决方案为其他服务器端技术(例如CGI)提供了可移植的替代方案。 JSP集成了许多Java应用程序技术,例如Java servlet,JavaBeans,JDBC和Enterprise JavaBeans。 它还将信息表示与应用程序逻辑分开,并促进了程序的可重用组件模型。

一个常见的问题涉及确定是否使用JSP与其他Java服务器端技术。 与客户端不同,客户端具有争夺主导地位的众多技术(包括HTML和applet,基于Web对象和doclet等基于文档对象模型(DOM)的策略,等等),而服务器端在各种Java服务器之间的重叠相对较少,方面的技术和干净的交互模型。

谁使用JSP? Internet上是否存在JSP的实际,关键任务部署? 达美航空和CNN是依赖JSP的众多业务中的两个,主要的新站点经常出现。 在IBMWebSphere®产品线中,首选技术和编程模型使用JSP(尽管WebSphere支持其他服务器端编程)。

什么是JSP?

JSP到底是什么? 让我们从两个角度考虑该问题的答案:HTML设计人员和Java程序员。

如果您是HTML设计师,则可以将JSP视为HTML的扩展,它使您能够在HTML页面中无缝嵌入Java代码段。 Java代码的这些位生成动态内容,该内容嵌入在其他HTML / XML内容中。 更好的是,JSP提供了一种方法,程序员可以通过它创建新HTML / XML标记和JavaBeans组件,这些组件为HTML设计人员提供了新功能,而无需您学习如何编程。

注意:常见的误解是,嵌入在JSP中的Java代码与HTML一起传输并由用户代理(例如浏览器)执行。 不是这种情况。 JSP被转换为Java servlet,并在服务器上执行。 嵌入在JSP中的JSP语句成为从JSP生成的servlet的一部分。 生成的servlet在服务器上执行。 用户代理永远看不到它。

如果您是Java程序员,则可以将JSP视为编写Servlet的新的高级方法。 与其直接编写servlet类并从servlet发出HTML,不如编写带有Java代码HTML页面。 JSP环境获取您的页面并对其进行动态编译。 每当用户代理从Web服务器请求该页面时,都会执行从您的JSP代码生成的servlet,并将结果返回给用户。

一个简单的JSP

通过研究几个简单的JSP,让我们将讨论从抽象变为具体。 第一个示例是Hello World的JSP版本:

HelloWorld.jsp

<HTML>
<HEAD><TITLE>Hello World JSP Example</TITLE></HEAD>
<BODY>
Hello World.
</BODY>
</HTML>

这只是普通HTML。 JSP语法扩展了HTML; 它不会替代它。 服务器将页面上的所有静态内容(不变)传递给客户端。 在这种情况下,不存在动态内容,因此静态内容将原样传递给浏览器。 将其视为JSP与将其视为普通HTML页面之间的区别在于,普通HTML页面只是从Web服务器传输到客户端,而JSP被转换为servlet,并且当该servlet执行时, Servlet的响应包含HTML。 用户看到相同的内容; 只有服务器上使用的机制不同。

动态的JSP

接下来,让我们向此简单示例添加一些动态内容。 除了显示“ Hello World”之外,示例页面还显示了当前时间。 修改后的JSP如下所示,突出显示了新添加的内容:

HelloWorld2.jsp

<HTML>
<HEAD><TITLE>Hello World JSP Example w/Current Time</TITLE></HEAD>
<BODY>
Hello World. The local server time is
   <%= new java.util.Date() %>.

</BODY>
</HTML>

文本的奇数位<%= new java.util.Date() %>是JSP表达式。 稍后我们将解释表达式。 现在,只需了解为客户机准备输出时,便会获取服务器的当前时间并将其自动转换为String对象,并将其嵌入到位。

注意:显示的是服务器的时间,而不是客户端的时间。 与在客户端计算机上执行JavaScript元素不同,JSP代码在服务器上执行,存在于服务器的上下文中,并且对客户端完全透明。

另一种JSP语法

使用XML标签而不是<%标签为JSP元素定义了另一种语法。 使用XML标签编写的同一示例如下所示:

HelloWorld3.jsp

<HTML>
<HEAD><TITLE>Hello World JSP Example w/ Current Time</TITLE></HEAD>
<BODY>
Hello World. The local server time is
 <jsp:expression> new java.util.Date() </jsp:expression>.

</BODY>
</HTML>

XML语法不太紧凑,但是这次很明显,即使您不知道特定的JSP表达式是什么,您也正在寻找一个。 这两种表达方式是同义的。 含义和生成的servlet代码相同。

JSP是servlet

从技术上讲,JSP编译成一个Java servlet(请参阅相关主题的Java servlet为更多)。 尽管JSP实现有可能直接从JSP源代码生成字节码文件,但是通常,每个JSP首先会转换为Servlet类的Java源代码,然后编译该Servlet。 调用生成的servlet来处理该页面的所有请求。 页面转换步骤通常在第一次请求给定的JSP时执行,并且仅在此后页面的源代码更改时执行。 否则,将仅执行生成的servlet,从而将内容快速交付给用户。

JSP规范定义了JSP语言和JSP运行时环境,但未定义转换环境。 换句话说,它定义了JSP源文件的外观和运行时环境,包括生成的servlet的类和接口,但是它没有定义如何将JSP源转换为servlet,也没有强制执行该操作。必须部署servlet。

页面翻译

页面翻译的内在细节在很大程度上取决于JSP的任何特定实现的作者。 通常,这是有限的后果。 JSP的所有实现都必须遵循相同的标准,以便您的页面可移植。 但是,有时可能会在规范之外存在操作上的差异。

GNUJSP是JSP的开源实现,它采用了一种相当简单的方法。 当您请求JSP时,GNUJSP会从Web服务器接收请求。 GNUJSP在文件系统中查找以找到JSP。 如果找到该页面,它将检查是否需要重新编译。 如果页面自上次编译以来已被更改,或者在GNUJSP维护的高速缓存中未找到已编译的servlet,则GNUJSP会将JSP源代码转换为servlet类的Java源代码,并将生成的源代码编译为二进制servlet。 然后,GNUJSP执行生成的servlet。 这意味着您可以快速发展您的JSP。 GNUJSP会在必要时(仅在必要时)自动重新编译它们。

Apache Jakarta项目包含Java Servlet和JSP技术的参考实现。 Jakarta采用与GNUJSP不同的方法,鼓励以一种称为Web App Repository(WAR)文件的JAR文件类型部署Web应用程序,该文件是2.2版及更高版本Java Servlet规范的一部分。

WAR文件包含构成Web应用程序的所有资源。 您可以在WAR文件中使用部署描述符将统一资源标识符(URI)映射到资源。 这是一个配置和维护更复杂的环境,但是对于大规模的商业部署它具有许多好处,因为您可以将完全配置的,预编译的(无源)Web应用程序部署为单个二进制存档。

JspPage界面

您可能会发现知道每个生成的页面都是支持JspPage接口的servlet类很有用(从技术上讲,该类支持协议相关的后代,例如HttpJspPage )。 JspPage扩展了Servlet并且是JSP和JSP容器之间的基本协定。

这里不需要对JspPage接口进行完整的讨论。 基本信息是,处理请求的主要方法是_jspService()_jspService()方法是在页面翻译期间生成的。 您在JSP中编写的任何表达式,脚本和操作都会影响页面_jspService()方法的生成实现。 任何声明都会影响所生成的Servlet类的定义。

下一步是什么?

既然您已经了解了编写JSP与编写标准HTML的不同之处,那么让我们更加仔细地研究一下JSP的语法。 本教程的其余部分简要介绍了所有JSP语法和语义。

注意:从技术上讲,JSP规范允许您使用Java以外的其他语言作为JSP的脚本语言。 目前,脚本语言必须是Java。 但是,请了解,关于将Java代码嵌入JSP中,将来您可能会使用其他语言。

JSP语法

JSP语法元素

下表显示了JSP规范定义的四大类核心语法元素。

元素类型 元素含量
模板内容 JSP源文件中不是JSP元素的所有内容; 包括所有静态内容
指令 您在JSP中放置的说明,以告诉JSP实现如何构建页面,例如是否包括另一个文件
脚本元素 用于将Java代码嵌入到JSP中的声明表达式Scriptlet
动作 动作以自定义XML样式标记的形式向JSP提供了高级功能,而没有公开脚本语言。 标准动作包括在JSP中创建,修改和以其他方式使用JavaBean的动作。

JSP元素语法

每个JSP元素都以不同的方式编写。 下表大致指示了每种JSP元素的语法。 这只是一个概述,因此您可以全面了解JSP语法。 稍后,我们将详细介绍JSP语法的正式规范。 请记住,您可以使用<%语法和XML语法来编写大多数元素。

元件 <%语法 XML语法
输出评论 <!- 可见评论 -> <!- 可见评论 ->
隐藏的评论 <%- 隐藏的注释 -%> <%- 隐藏的注释 -%>
声明书 <%! Java声明 %> <jsp:声明>
Java语言声明
</ jsp:declaration>
表达方式 <%= Java表达式 %> <jsp:表达式>
Java表达式
</ jsp:expression>
脚本 <% Java语句 %> <jsp:scriptlet>
Java语句
</ jsp:scriptlet>
指令 <%@ ...%> <jsp:指令。 输入 ... />
动作 <jsp: 动作 ... />
要么
<jsp: 操作 > ... </ jsp: 操作 >

模板内容

什么是模板内容?

严格来说,模板内容不是特定元素。 而是,模板内容是JSP中的所有内容,而不是实际的JSP元素。 回顾我们的第一个HelloWorld.jsp示例( 一个简单的JSP ); 该页面上的所有内容都是模板内容。 JSP规范指出,不是实际JSP元素的所有内容都是模板内容,应无更改地传递到输出流中。 例如,考虑我们的Hello World示例的以下变体:

HelloWorld4.jsp

<jsp:directive.page import="java.util.Date"/>
<HTML>
<HEAD><TITLE>Hello World JSP Example w/Current Time</TITLE></HEAD>
<BODY>
Hello World. The local server time is <%= new Date() %>.
</BODY>
</HTML>

这与前面的示例相同,除了我们包括一个导入JSP指令以导入java.util.Date类,因此在创建Date对象时不必完全限定包名称。 因此,您可能会认为,由于我们没有更改模板内容,仅指定了import语句,因此该JSP的输出与我们先前收到的相同。 但是,该假设是不正确的。

事实是我们确实更改了模板内容: page指令后的回车符是新的,并成为模板内容的一部分。 您可以通过查看实时演示HTML源来确认这一点。

切记 :不属于JSP元素的所有内容都是模板内容。

注释

您可以通过以下三种方式在JSP中放置注释:

  • 写一个HTML注释
  • 写一个JSP注释
  • 在脚本元素中嵌入评论

输出评论

使用第一种方法,您可以按照以下格式编写标准HTML注释:

<!- I am an HTML comment -->

此注释是模板内容的一部分。 这里没什么好看的; 这只是普通的旧HTML。 有时将其称为可见注释,因为JSP容器将其像对待其他模板内容一样对待,并将其原样传递给输出流。 对于用户代理(例如,您的浏览器)来说,这实际上是一个注释是一个问题。 实际上,许多HTML注释根本不是真正的注释。 例如,HTML标准建议您将JavaScript语句嵌入注释中,以保持与JavaScript以前的浏览器的兼容性。 支持JavaScript的浏览器将这些注释作为普通语句处理,而不支持JavaScript的浏览器将忽略这些注释。

当然,由于HTML注释只是JSP容器的另一部分模板内容,因此您可以在其中嵌入JSP表达式。 例如,您可以编写以下内容:

HelloWorld5.jsp

<!-- The time is <%= new java.util.Date() %> -->
<HTML>
<HEAD><TITLE>Hello World JSP Example</TITLE></HEAD>
<BODY>
Hello World.
</BODY>
</HTML>

这导致日期被嵌入HTML注释中。 在浏览器视图中看不到任何东西,但是您可以通过查看实时演示HTML源代码来查看注释。

隐藏的评论

您可以使用JSP表示法以第二种方式编写注释:

<%-- I am a comment --%>

该注释被称为隐藏注释,因为它是实际的JSP注释,不会出现在生成的servlet中。 用户永远不会看到评论,也无法知道它的存在。 重复Hello World示例,但是这次带有JSP注释,结果如下:

HelloWorld6.jsp

<%-- This is our oft-repeated Hello World example: --%>
 <HTML>
 <HEAD><TITLE>Hello World JSP Example</TITLE></HEAD>
 <BODY>
 Hello World.
 </BODY>
 </HTML>

同样,请记住,不是JSP元素的所有内容都是模板内容。 注释后的回车符不是JSP注释元素的一部分; 它是模板内容的一部分,因此在浏览器中可见。

脚本语言注释

编写注释的第三种方法是将特定于语言的注释嵌入脚本元素中。 例如,您可以编写以下内容:

HelloWorld7.jsp

<jsp:directive.page import="java.util.*"/>
<jsp:declaration>
/* date is a new member variable, initialized when we are instantiated.
It can be used however we want, such as:
    out.println("Instantiated at " + date); // display our "birth" date
*/ Date date = new Date();
</jsp:declaration>
<HTML>
<HEAD><TITLE>Hello World JSP Example</TITLE></HEAD>
<BODY>
Hello World.<BR> 
This page was loaded into memory at <%= date %>.<BR>
The current time is <%= new Date() %>
</BODY> 
</HTML>

此版本在脚本元素(声明)中嵌入了注释。 实际上,还有一个嵌套的注释: /* ... */块注释中的//注释。 JSP规范允许的唯一类型的嵌套注释是脚本语言注释,即使如此,仅在脚本语言(例如Java)允许嵌套注释时才是如此。

引用和转义规则

<%是JSP容器理解为JSP元素开始的魔术令牌。 您如何将<%放入输出流? 例如,如果要使用JSP编写有关JSP主题的教程,如何编写页面,使其输出包含JSP的源代码? 那些JSP元素也不会被处理吗?

解决此问题的方法称为转义,并且需要以修改的形式对内容进行编码。 下表说明了作为JSP规范一部分的转义规则集。 它表示当您希望直接将<%传递而不是对其进行处理时,可以在模板内容中将它写为<\%

在此元素类型内... 如果你想写... 您将文本转义为...
模板内容 <% <\%
脚本元素 %> %\>
元素属性 ' \'
元素属性 \“
元素属性 \ \\
元素属性 <% <\%
元素属性 %> %\>

使用转义规则

下面的示例说明了这些转义序列的某些用法,并说明了有关HTML的要点:

HelloWorld8.jsp

<HTML> 
<HEAD><TITLE>Hello World JSP Escape Sequences</TITLE></HEAD> 
<!-- 
This page was run at <%= new java.util.Date() %>, according 
to <\%=new Date() %\> 
-->
<BODY> 
<P>Hello World.<BR> 
The local server time is <%= new java.util.Date() %>.<BR> 
The time is computed using the JSP expression 
<I>&lt;%=new Date()%&gt;.</I></P>
<P>The escaping sequence in the comment uses the JSP defined 
escape sequence, but that won't work within the normal HTML 
content. Why not? Because HTML also has escaping rules.</P>
<P>The most important HTML escaping rules are that '&lt;' is
encoded as <I>"&amp;lt;"</I>, '&gt;' is encoded as
<I>"&amp;gt;"</I>, and '&amp;' is encoded as <I>"&amp;amp;"</I>.
</P>
</BODY>
</HTML>

HTML实体

如上例所示,您不仅必须了解JSP的规则,而且还必须了解模板内容的规则。 在这种情况下,您将编写HTML内容,并且HTML也具有转义规则。 这些规则表明,除了在注释中,您不能在HTML中编写<\% 。 为什么不? HTML将<视为特殊字符; 它标记了HTML标签的开始。 因此,HTML要求您对某些特殊字符进行编码,例如下表中列出的那些字符。

所需字符 HTML转义序列
< &lt;
> &gt;
&amp;

存在许多其他有用HTML转义序列。 HTML V4.01规范的一部分包括正式HTML转义序列的完整列表,在HTML标准中称为字符实体引用 。

指令

JSP指令

指令向JSP容器提供其他信息,并描述页面的属性。 我们在下面描述JSP中通常使用的三个指令。 除了这些,JSP 2.0还引入了三个。 这些指令仅可用于标记文件,因此在标记文件中引入。

指示 目的
控制JSP的属性
包括 在翻译时将文件内容包含到JSP中
标签库 使自定义标签库在包含页面中可用

通常,您使用上述指令导入Java软件包以在页面中使用,包含文件或访问自定义标签的库。

请注意,伪指令不会对当前输出流产生任何输出。

指令的一般语法为:

<%@  directive [attr="value"]%>

要么

<jsp:directive.  directive [attr="value"] />

page指令

page指令定义了各种与页面相关的属性,并将这些属性传递给JSP容器。 page指令通过如下设置页面属性来实现此目的:

<%@ page [attribute="value"*] %>

要么

<jsp:directive.page [attribute="value"*] />

如前面的示例所示,通常,您将使用import属性:

<jsp:directive.page import="java.util.Date"/>
 <HTML>
 <HEAD><TITLE>Hello World JSP Example w/Current Time</TITLE></HEAD>
 <BODY>
 Hello World. 
 The local server time is <%= new Date() %>. 
 </BODY>
 </HTML>

该指令导致将java.util.Date导入(如Java语句import java.util.Date )到您的JSP中。

page指令属性

其他页面属性控制页面缓冲,异常处理,脚本语言等。

属性
language 此属性定义翻译单元主体(JSP以及使用稍后描述的include指令包括的任何文件)内的scriptlet,表达式和声明中使用的脚本语言。 JSP规范2.0支持的唯一语言是Java,因此这是此page属性的默认值。
extends 指定此JSP转换为的类的超类。 应该将其定义为完全合格的类名称。 它依赖于实现,因此应谨慎使用。 每个实现都提供自己的默认类。 覆盖默认值可能会影响实现质量。
import 描述可用作导入的所有Java类型。 它遵循与标准Java语言import语句相同的规则。 默认情况下,将导入java.lang.*javax.servlet.http.*javax.servlet.*javax.servlet.jsp.* 。 您可以添加需要为页面导入的任何其他包或类型。
session 指示页面是否需要参与HttpSession。 默认情况下,该值为true。 您可以选择将值指定为false(在这种情况下,将无法解析页面中对HttpSession的任何引用)。
buffer 该页面属性指示是否将缓冲从页面输出的内容。 默认情况下,页面输出以不小于8 KB的实现缓冲区大小进行缓冲。
autoFlush 指定在填充缓冲区时是否应自动刷新缓冲的输出(真值),还是应该引发异常(假值)以指示缓冲区溢出。 默认值是true。
isThreadSafe 指示生成的servlet是否是线程安全的。 JSP 2.0规范建议不要使用isThreadSafe因为生成的Servlet可能实现SingleThreadModel接口,而该接口已在Servlet 2.4规范中弃用。
info 定义一个可以合并到翻译页面中的string
isErrorPage 指示当前的JSP是否是另一个JSP的errorPage的预期目标。 默认值为false。
errorPage 定义资源的URL,该资源将捕获当前页面的异常。
contentType 定义MIME类型和JSP响应的字符编码。 值的形式为“ TYPE”或“ TYPE; charset = CHARSET”。
pageEncoding 定义资源的URL,该资源将捕获当前页面的异常。
isELIgnored 定义是否忽略或评估此页面和翻译单元的表达式语言(EL)表达式。 默认值取决于web.xml版本。 有关详细信息,请参见有关JSP配置的部分。

page指令是您可能需要向JSP容器描述的有关JSP的所有信息的全部内容。 好消息是,所有page指令属性的默认值都非常合理,您几乎不需要更改它们。

include指令

include指令在编译时将命名文件的内容直接包含在JSP的源代码中。 编写JSP的两种方法include伪指令:

<%@ include file="filename" %>

要么

<jsp:directive.include file="filename" />

include指令与jsp:include操作不同。 该指令包括在翻译时文件的内容,类似于C / C ++ #include ; 而动作包括在请求时间将页面输出到输出流中。

include指令对于将可重用的内容(例如常见的页脚和标头元素)包含到JSP中非常有用。 例如,JSP上的公共页脚元素是最后更改的指示器,因此人们知道页面的最新更新时间。 您可以在包含的文件中实现该功能。 通常包含的另一个页面会强制用户在访问页面内容之前登录。

include指令示例

让我们考虑一个include指令的简单演示。 到目前为止,我们的示例均未包含​​版权声明。 假设/copyright.html包含一个常见的版权声明,可包含在我们的示例页面中。 我们可以将/copyright.html作为标头包含在我们的JSP中。 如下所示:

HelloWorld9.jsp

<jsp:directive.include file="/copyright.html" /> 
<HTML>
<HEAD><TITLE>Hello World JSP Example</TITLE></HEAD>
<BODY>
Hello World.
</BODY>
</HTML>

翻译页面时,包含的文件仅成为JSP的一部分。

taglib指令

创建和使用定制标记库的能力是JSP提供的最强大和有用的功能之一。 此功能使开发人员可以定义新的XML样式的标签,以供页面设计者使用。 JSP规范将这些新标记称为新动作。 这些自定义标签允许非程序员使用全新的编程功能,而无需此类页面设计者理解Java编程语言或任何其他脚本语言。 所有详细信息都可以封装在自定义标签中。 此外,使用自定义标签的JSP与具有大量嵌入式Java代码(脚本或表达式)的JSP相比,界面设计和业务逻辑实现之间的分隔要清晰得多。

例如,到目前为止,我们经常使用Java Date类来显示当前时间。 相反,我们可以定义一个自定义的DATE标签,该标签可能如下所示:

<jsp:directive.taglib uri="jdg.tld" prefix="jdg" />
<HTML>
<HEAD><TITLE>Hello World JSP Example w/Current Time</TITLE></HEAD>
<BODY>
Hello World.
The local server time is <jdg:DATE />.
</BODY>
</HTML>

第一行是taglib指令。 uri属性告诉JSP容器在哪里可以找到taglib定义。 prefix属性告诉JSP容器,您将使用jdg:作为标记的前缀。 此前缀是必需的,可以防止名称空间冲突。 jsp前缀由Sun保留。

标记<jdg:DATE />导致使用自定义标记处理程序类。 在此处理程序类中,将当前日期和时间放入输出流。 页面设计人员不必了解Java代码。 相反,设计人员仅使用我们已记录的自定义DATE标签。

创建自定义标签库的过程超出了本教程的范围。

声明书

JSP声明

声明声明了新的数据和函数成员,供在JSP中使用。 这些声明成为页面转换期间生成的最终servlet类的一部分。 您可以通过两种方式编写JSP声明:

<%!  java declarations %>

要么

<jsp:declaration>  java declarations </jsp:declaration>

为了清楚起见,切勿使用<%语法进行声明。 XML语法是自描述性的,对读者而言,比记住哪个JSP元素使用感叹号要清晰得多。

JSP声明示例

这是Hello World的版本,该版本使用JSP声明来声明类变量和某些类函数。 它们被声明为静态的,因为它们与JSP的任何特定实例都不相关:

HelloWorld10.jsp

<jsp:directive.page import="java.util.Date"/> 
 <jsp:declaration>
private static String loadTime = new Date().toString();
private static String getLoadTime() { return loadTime; } 
private static String getCurrentTime() { return new Date().toString(); }
</jsp:declaration>
<HTML>
<HEAD><TITLE>Hello World JSP Example</TITLE></HEAD>
<BODY>
Hello World.<BR>
This page was loaded into memory at <%=  getLoadTime() %>.<BR> 
The current time is <%=  getCurrentTime() %>
</BODY>
</HTML>

第一个突出显示的部分是JSP声明。 回想一下,每个JSP都编译为Java servlet类。 JSP声明允许您声明该类的新成员。 在示例中,我们声明一个新的类变量loadTime和两个类函数getLoadTime()getCurrentTime() 。 然后,在我们的JSP主体中,我们引用我们的新功能。

JSP初始化和终止

JSP容器为页面提供了一种初始化其状态的方法。 页面可能会声明可选的初始化和清除例程。 在要求页面处理任何请求之前,将调用jspInit()方法。 如果JSP容器需要页面来释放资源,则它调用jspDestroy()方法。 以下是这些方法的签名:

public void jspInit(); 
public void jspDestroy();

您可以将这些可选方法放在JSP声明中,就像放置任何其他方法一样。

jspInit()jspDestroy()示例

此示例与JSP声明示例相同,不同之处在于,我们不是使用本地服务器时间显示时间,而是使用SimpleDateFormat对象将时间转换为GMT。 因为没有必要构建该对象不止一次,你在初始化jspInit()和去参考它在jspDestroy()

HelloWorld11.jsp

<jsp:directive.page import="java.util.*, java.text.*" />

<jsp:declaration> private static DateFormat formatter; 
private static String loadTime; 
private static String getLoadTime() { return loadTime; } 
private static String getCurrentTime()
  { return toGMTString(new Date()); } 

private static String toGMTString(Date date) 
{ 
    return formatter.format(date);
} 

 public void jspInit() 
{
    formatter = new SimpleDateFormat
      ("d MMM yyyy HH:mm:ss 'GMT'", Locale.US);
    formatter.setTimeZone(TimeZone.getTimeZone("GMT"));
    loadTime = toGMTString(new Date()); 
} 

public void jspDestroy() 
{ 
    formatter = null; 
}

</jsp:declaration>
<HTML>
<HEAD><TITLE>Hello World JSP Example</TITLE></HEAD>
<BODY>
Hello World.<BR>
This page was initialized at <%= getLoadTime() %>.<BR>
The current time is <%= getCurrentTime() %>
</BODY>
</HTML>

表达方式

JSP表达式

到目前为止,您应该对JSP表达式已经不陌生。 自编写第二个JSP示例以来,我们一直在使用表达式:

The local server time is <%= new java.util.Date() %>

文本<%= new java.util.Date() %>是JSP表达式。

有效的JSP表达式

Java代码中的表达式是产生值的语句。 Java语言规范的第15章详细讨论了表达式(请参阅参考资料 )。 JSP表达式是在请求时求值的Java表达式,将其转换为String并写入输出流。 JSP表达式编写为:

<%= a-java-expression %>

要么

<jsp:expression> a-java-expression </jsp:expression>

这是JSP语法的一方面,您可能会使用<%语法而不是XML语法。

重要提示:你不终止与分号的表达。

在执行JSP期间,将适当地发出JSP表达式的结果,这意味着JSP expression语句的文本将替换为其在页面上相同位置的值。

脚本

JSP脚本

到目前为止,我们已经讨论了如何声明新的数据和函数成员,以及如何使用Java表达式为页面创建动态内容。 但是,如何为页面添加逻辑流(如循环和分支)? 除了简单地评估表达式之外,您还有更多工作要做吗? 那就是脚本开始起作用的时候。

小脚本的名称含义是:小脚本(或多或少)是用脚本语言编写的少量语句集,除了前面提到的WebSphere以外,这意味着用Java代码编写。

小脚本在请求处理时执行。 它们是否在输出流中产生任何输出取决于scriptlet中的代码。

小脚本写为:

<% java-statements %>

要么

<jsp:scriptlet>
   java-statements 
</jsp:scriptlet>

再一次,最好在编写脚本时使用XML语法。

脚本示例

以下JSP使用scriptlet根据您使用的浏览器执行不同的操作:

Scriptlet.jsp

<jsp:scriptlet>
String userAgent = (String) request.getHeader("user-agent"); 
</jsp:scriptlet>
<HTML>
<HEAD><TITLE>JSP Scriptlet Example</TITLE></HEAD>
<BODY>
<jsp:scriptlet>
if (userAgent.indexOf("MSIE") != -1) 
{ 
</jsp:scriptlet> 
<p>You are using Internet Microsoft Explorer.</p>
<jsp:scriptlet>
} 
else 
{
</jsp:scriptlet>
<p>You are not using Internet Microsoft Explorer.
  You are using <%= userAgent %></p>
<jsp:scriptlet>
} 
</jsp:scriptlet> 
</BODY>
</HTML>

局部变量

Scriptlet.jsp示例中有几个scriptlet。 第一个小脚本显示允许出现在函数体内的任何Java语句,包括变量声明:

<jsp:scriptlet>
 String userAgent = (String) request.getHeader("user-agent");
 </jsp:scriptlet>

程序逻辑

Scriptlet.jsp示例中的其余scriptlet实现了一些简单的条件逻辑。 为了简洁起见,此JSP中重新格式化的关键序列是:

<jsp:scriptlet> if (  condition ) { </jsp:scriptlet> 
   JSP statements and template content 
<jsp:scriptlet> } else { </jsp:scriptlet> 
   JSP statements and template content 
<jsp:scriptlet> } </jsp:scriptlet>

如果条件为true,则JSP语句和模板内容的第一块有效,如果条件为false,则第二块有效。

这是一种常见的构造,提供了JSP内容的条件执行。 需要注意的重要一点是,脚本不必一定是完整的语句。 它们可以是片段,只要这些片段在上下文中缝合在一起时便形成完整的语句。 在此序列中,第一个脚本包含条件测试,并使复合语句处于打开状态。 因此,后面的所有JSP语句都包含在compound语句中,直到第二个scriptlet关闭该块并打开else块为止。 最后,第三个脚本关闭了else子句的复合语句。

您可以类似的方式在scriptlet中部署循环结构。

脚本警告

使用小脚本进行适度的锻炼。 编写scriptlet很容易被带走。 小脚本允许您在JSP中编写几乎任意的Java程序。 通常,这与良好的JSP设计不符。 另外,随着对JSP定制标记的使用变得越来越成熟,JSP作者应该期望看到许多脚本集用法已被定制标记取代。

使用自定义标签的原因之一是要使编程逻辑与表示形式分开。 同样,许多网页设计者不是Java程序员,因此嵌入在网页中的scriptlet可能会造成混淆。

错误处理

翻译时处理错误

通过JSP容器将JSP源转换为相应的servlet类时,可能会遇到错误。 这种翻译可能会发生:

  • 在将JSP部署到JSP容器之后以及客户端请求JSP之前。
  • 客户端请求JSP时。

在第一种情况下,错误处理与实现有关,并且JSP规范没有涵盖这一点。 However, in both cases, if translation errors are encountered, the JSP container should return the appropriate error code for the client requests.

For HTTP protocols, the error status code 500 is returned.

Request-time processing errors

Request-time processing errors occur during the request processing by a JSP. These errors can occur in the body of the generated servlet or in any other code called from the body of the JSP implementation class.

These exceptions might be caught and handled (as appropriate) in the body of the JSP implementation class.

Any uncaught exceptions thrown in the body of the JSP implementation class result in the forwarding of the client request and uncaught exception to the errorPage URL specified by the JSP. The next section discusses how you can use JSPs as error pages.

Using JSPs as error pages

A JSP is considered an error page if it sets the page directive's isErrorPage attribute to true. Such JSP URLs are generally defined as errorPage URLs in other JSPs. If an uncaught exception occurs in any of the JSPs defining errorPage JSPs, the control is transferred to these error pages.

Here is an example of an error page:

MyErrorPage.jsp

<%@ page isErrorPage="true" contentType="text/html" %> 
A fatal error has occurred. Please call the help desk.

The following JSP, MyErrorThrower.jsp, throws the error captured by MyErrorPage.jsp:

MyErrorThrower.jsp

<%@ page errorPage="MyErrorPage.jsp" contentType="text/html" %>
<% int totalCost = 200; int numberOfVehicles =0;
  int costOfVehicle = totalCost/numberOfVehciles ; %>

An ArithmeticException in the above code causes an unhandled exception to be thrown. This results in a request being forwarded to MyErrorPage.jsp.

Error-page element

You can also define error pages for JSPs in the web.xml file. The web.xml snippet below shows how to do this:

<error-page>
<exception-code>500</exception-code>
<location>/MyErrorPage.jsp</location>
</error-page>

JSP configuration

Changes in web.xml

JSP configuration information is specified in the deployment descriptor (WEB-INF/web.xml). As of the Java Servlet 2.4 and JSP 2.0 specifications, the web.xml is defined using XML schema instead of DTD. The obvious benefit of this is that now the order of the elements in web.xml does not matter.

Also, the deployment descriptor includes many new configuration options. We describe these configuration options below.

jsp-config element

You use the jsp-config element to provide the global configuration information for the JSP files in a Web application. Schema defines two sub-elements of this element:

  • taglib -- Defines the taglib mapping
  • jsp-property-group -- Defines the groups of JSP files

taglib element

The taglib element provides a mapping between the taglib URIs and TLD resource paths. The taglib element has two sub-elements:

  • taglib-uri -- This element describes a URI identifying a tag library used in the Web application. It can be an absolute URI or a relative URI. Note that same taglib-uri value cannot be repeated in a web.xml.
  • taglib-location -- This sub-element defines the actual TLD resource path for the taglib-uri .

As an example, consider that a JSP uses a TLD resource defined as test-taglib.tld . Now, this JSP can declare the taglib URI as:

<%@ taglib prefix="test" uri="http://ibm.com/test-example-taglib"%>

In this case, the entry in web.xml should look like this:

<taglib>
<taglib-uri>
http://ibm.com/test-example-taglib
</taglib-uri>
<taglib-location>
/WEB-INF/jsp/test-taglib.tld
</taglib-location>
</taglib>

jsp-property-group element

A jsp-property-group element represents a collection of properties that apply to a set of files that represent JSPs. These properties are defined in one or more jsp-property-group elements in the web.xml.

The properties that can be described in a jsp-property-group include:

  • el-ignored: Controls disabling of EL evaluation
  • scripting-invalid: Controls disabling of scripting elements
  • page-encoding: Indicates page-encoding information
  • include-prelude: Prelude automatic includes
  • include-coda: Coda automatic includes
  • is-xml: Indicates that a resource is a JSP document

Let's examine each of these properties.

el-ignored

Specifications prior to JSP 2.0 did not identify the EL pattern ${expr} , so in JSPs written before JSP 2.0, there might be situations where the intention is not to activate EL expression evaluation but to pass such patterns verbatim.

For Web applications delivered using a web.xml that uses the Servlet 2.4 format, the default mode evaluates EL expressions. The default mode for JSPs in a Web application delivered using a web.xml based on Servlet 2.3 or earlier formats ignores EL expressions.

The default mode can be explicitly changed by setting the value of el-ignored to true or false. The snippet shown below deactivates EL evaluation for the configtest.JSP inside folder jsp2:

<jsp-property-group>
<url-pattern>jsp2/configtest.jsp</url-pattern>
<el-ignored>true</el-ignored>
</jsp-property-group>

scripting-invalid

With the introduction of EL in JSP specification 2.0, JSP authors might want to disallow scripting elements within JSPs. By default, scripting is enabled. You can disable it by setting the value of the scripting-invalid property to false.

For example, the snippet shown below deactivates scripting for the configtest.JSP inside folder jsp2:

<jsp-property-group>
<url-pattern>jsp2/configtest.jsp</url-pattern>
<scripting-invalid>true</scripting-invalid>
</jsp-property-group>

Page encoding

You can use page encoding to set the pageEncoding property of a group of JSPs defined using the jsp-property-group element. If different values are given for the pageEncoding attribute of the JSP directive and in a JSP configuration element matching the page, a translation time error will occur.

For example, the snippet shown below sets the page encoding for a JSP group inside the jsp2 folder:

<jsp-property-group>
<url-pattern>jsp2/*.jsp</url-pattern>
<page-encoding>ISO-8859-1</page-encoding>
</jsp-property-group>

include-prelude

The value of include-prelude is a context-relative path that must correspond to an element in the Web application. When the element is present, the given path is automatically included (as in an include directive) at the beginning of the JSP in the jsp-property-group.

For example, the JSP fragment topFragment.jspf is automatically included at the beginning of all JSPs inside the jsp2 folder:

<jsp-property-group>
<url-pattern>jsp2/*.jsp</url-pattern>
<include-prelude>/jsp2/topFragment.jspf</include-prelude>
</jsp-property-group>

include-coda

The value of include-coda is a context-relative path that must correspond to an element in the Web application. When the element is present, the given path is automatically included (as in an include directive) at the end of the JSP in the jsp-property-group .

For example, the JSP fragment endFragment.jspf is automatically included at the end of all JSPs inside the jsp2 folder:

<jsp-property-group>
<url-pattern>jsp2/*.jsp</url-pattern>
<include-prelude>/jsp2/endFragment.jspf</include-prelude>
</jsp-property-group>

is-xml

This JSP configuration element denotes that a group of files are JSP documents and must, therefore, be interpreted as XML documents.

For example, the web.xml snippet shown below indicates that files with extension .jspx are XML documents:

<jsp-property-group>
<url-pattern>*.jspx</url-pattern>
<is-xml>true</is-xml>
</jsp-property-group>

Implicit objects

Implicit objects overview

Many objects are predefined by JSP architecture. They provide access to the run-time environment. The implicit objects are local to the generated _jspService() method (see The JspPage interface ). Scriptlets and expressions, which affect the _jspService() method, have access to the implicit objects, but declarations, which affect the generated class, do not.

You can write numerous JSPs and never have a need to refer to the implicit objects directly. Most of the implicit objects are standard Servlet API components. This next section lists and briefly discusses each, so you have an overview of the standard objects available to expressions and scriptlets.

Implicit objects descriptions

目的 目的
out javax.servlet.jsp.JspWriter The output stream
request javax.servlet.ServletRequest Provides access to details regarding the request and requester
response javax.servlet.ServletResponse Provides access to the servlet output stream and other response data
session javax.servlet.http.HttpSession Supports the illusion of a client session within the HTTP protocol
pageContext javax.servlet.jsp.PageContext Used extensively by the generated JSP code to access the JSP environment and user beans
config javax.servlet.ServletConfig Servlet configuration information
page java.lang.Object The page itself
application javax.servlet.ServletContext Represents the Web application; provides access to logging methods
exception java.lang.Throwable For error pages only ; the exception that caused the page to be invoked

The out object

The out object is an instance of javax.servlet.jsp.JspWriter . The JspWriter emulates java.io.PrintWriter , but supports buffering like java.io.BufferedWriter . The usual java.io.PrintWriter() methods are available with the modification that the JspWriter() methods throw java.io.IOException .

A JSP author infrequently references the out object directly. The most common use is within a scriptlet or as a parameter passed to another method.

For example, the code below uses out object to print "Example of out implicit object" on the JSP:

<% out.println("Example of out implicit object"); %>

The request object

The request object is a standard servlet object that is a protocol-dependent subclass of javax.servlet.ServletRequest . In other words, for the HTTP protocol, the request object is an instance of javax.servlet.http.HttpServletRequest .

The request object provides access to details regarding the request and requester. For example, the code below fetches the value of the request parameter named username:

<% String userName = request.getParameter("username"); %>

The response object

The response object is a standard servlet object that is a protocol-dependent subclass of javax.servlet.ServletResponse . For the HTTP protocol, the response object is an instance of javax.servlet.http.HttpServletResponse .

The response object provides access to the servlet output stream. It also allows you to set response headers, including cookies, content type, cache control, refresh, and redirection, and it supports URL encoding as an aid to session tracking when cookies aren't available.

For example, the code below sets the content type for a response:

<% response.setContentType("text/html"); %>

The session object

The session object is a standard servlet object that is an instance of the javax.servlet.http.HttpSession class. This object helps support the illusion of a client session within the HTTP protocol.

JSP authors essentially get sessions for free. Simply use session scope when using jsp:useBean to work with any session-specific beans.

The code below shows how to use session object to fetch session attribute username:

<% String userName = (String)session.getAttribute("username"); %>

The pageContext object

The pageContext object is an instance of javax.servlet.jsp.PageContext . The generated JSP code uses it extensively to access the JSP environment and user beans.

The pageContext object provides a uniform access method to the various JSP objects and beans, regardless of scope. It also provides the means through which the out object is acquired, so an implementation can supply a custom JspWriter , and provides the JSP interface to include and forward functionality.

As shown below, you can also use pageContext to set attributes or to fetch exceptions and process as required:

<% java.lang.Exception e = pageContext.getException()

 if (e.getMessage().equals("testException") { %>

The config object

The config object is a standard servlet object that is an instance of the javax.servlet.ServletConfig class. It provides access to the ServletContext and to any servlet initialization parameters. You'll rarely use this object.

The page object

The page object is the JSP object executing the request.

As discussed in The JspPage interface , the page object is a javax.servlet.jsp.JspPage interface descendent. javax.servlet.jsp.HttpJspPage is used for the HTTP protocol.

The methods of the JspPage interface:

void jspInit();// allows user action when initialized
void jspDestroy();// allows user action when destroyed public 
void _jspService(HttpServletRequest request, HttpServletResponse response)
  throws ServletException, IOException;

As you saw in the jspInit() and jspDestroy() example , you might declare the jspInit() and jspDestroy() methods. _jspService() is generated for you.

The application object

The application object is a standard servlet object that is an instance of the javax.servlet.ServletContext class. You also use ServletContext to get the RequestDispatcher .

The resource methods:

URL getResource(String path); InputStream getResourceAsStream(String path);

are especially useful because they isolate you from details of Web storage (for example, file system, JAR, or WAR file).

You'll most likely use this object to gain access to logging:

application.log(String); application.log(String, Throwable);

The exception object

The exception object is an instance of java.lang.Throwable . The exception object is present only in an error page, defined as such by the page directive.

This object contains the exception that causes the request to forward to the error page.

Objects and scopes

Application-specific objects

It is recommended that application behavior be encapsulated in objects whenever possible. This helps page designers focus on the presentation issues. Apart from the predefined implicit objects, a JSP can access, create, and modify server-side objects. You can create and use objects within a JSP in many ways:

  • Instance and class variables of the JSP's servlet class are created in declarations and accessed in scriptlets and expressions.
  • Local variables of the JSP's servlet class are created and used in scriptlets and expressions.
  • Attributes of scope objects (see the discussion below on objects and scopes) are created and used in scriptlets and expressions.
  • You can also create a JavaBeans component in a declaration or scriptlet and invoke the methods of a JavaBeans component in a scriptlet or expression.

Object scopes

Scope describes what entities can access the object. Objects can be defined in any of the following scopes:

page
Objects with page scope are accessible only within the page where they are created. All references to such an object are released after the JSP sends the response back to the client or the request is forwarded somewhere else. References to objects with page scope are stored in the pageContext object.

request
Objects with request scope are accessible from pages processing the same request where they were created. References to the object are released after the request processes. In particular, if the request is forwarded to a resource in the same runtime, the object is still reachable. References to objects with request scope are stored in the request object.

session
Objects with session scope are accessible from pages processing requests in the same session as the one in which they were created. It is illegal to define an object with session scope from within a page that is not session-aware. All references to the object are released after the associated session ends.

application
Objects with application scope are accessible from pages processing requests in the same application as the one in which they were created. The application object is the servlet context obtained from the servlet configuration object.

Expression language

Expression language

Expression language is used for run-time assignment of values to action element attributes. It was first introduced as part of Java Standard Tag Library (JSTL) 1.0 specification, but is now part of the JSP 2.0 specification.

As part of JSTL, you could only use expression language with JSTL actions. Now, as an integral part of the JSP 2.0 specification, you can use EL with template text, as well as with standard and custom actions.

Expression language is inspired by both ECMAScript and XPath expression languages, and uses the features of both languages, as well as introduces some new ones. For example, expression language performs data-type conversions automatically.

The main advantage of using EL in JSPs is to enforce writing scriptless JSPs. You can do this through the configuration element scripting-invalid . As discussed in JSP configuration , making the value of this element true allows the use of expression language, but prohibits the user from using Java scriptlets, Java expressions, or Java declaration elements within JSPs.

Let's cover some EL basics.

Implicit objects, EL basics

EL expressions support several implicit objects. The following table describes these objects.

Implicit object 描述
pageScope A map of all page-scoped variables and their values
requestScope A map of all request-scoped variables and their values
sessionScope A map of all session-scoped variables and their values
applicationScope A map of all application-scoped variables and their values
pageContext An object of the pageContext class
param A map of all request parameter values wherein each parameter is mapped to a single String value
paramValues A map of all request parameter values wherein each parameter is mapped to a String array
header A map of all request header values wherein each header is mapped to a single String value
headerValues A map of all request header values wherein each header is mapped to a String array
cookie A map of all request cookie values wherein each cookie is mapped to a single javax.servlet.http.Cookie value
initParam A map of all application initialization parameter values wherein each parameter is mapped to a single String value

Examples of implicit objects

Let's consider an example detailing the use of implicit objects. The table below describes how you can access and resolve information like request attributes, session attributes, and request parameters (first column of the table) using EL (second column of the table). The third column displays the result of the resolution.

Source JSP parameter/attribute Target JSP expression 结果
request.setAttribute ("reqAttr", "iAmReqAttr"); ${requestScope["reqAttr"]} iAmReqAttr
session.setAttribute ("sessAttr", "iAmSessAttr"); ${sessionScope["sessAttr"]} iAmSessAttr
request parameter "customerName" having value "Martin" ${param.customerName} 马丁
Header browser information ${header.user-agent} Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322)
Header host information ${header["host"]} 本地主机:8888

Syntax of expression language

The syntax of expression language is quite simple:

  • You can use a [] operator to access properties of JavaBeans objects, lists, or arrays of objects.
  • You can also use a . operator to access the properties of a JavaBean object.
  • You can use arithmetic operators for computations.
  • You can use standard Java relational operators for relational comparisons.
  • Logical operators are also available for your use.
  • Literals of boolean, integer, floating point, string, and null are available.
  • You can also use conditional operators for conditional processing.

Let's discuss the above syntactic rules in detail.

Arithmetic operators

Expression language can use the following five arithmetic operators to act on integer and floating point values:

  • + operator for addition
  • - operator for subtraction
  • * operator for multiplication
  • / operator for division
  • % operator for remainder

The table below describes the arithmetic operators through some examples.

Expression using arithmetic operator Result of the expression
${2.7 + 5.6} 8.3
${-2 - 7} -9
${9/2} 4.5
${10%4} 2

Relational operators

Relational operators operate on two operands and always return a boolean result. You can use the following relational operators with EL:

  • == operator for evaluating "equal condition"
  • ! = operator for evaluating "are not equal condition"
  • < operator for evaluating "less than" condition
  • > operator for evaluating "greater than" condition
  • <= operator for evaluating "less than equal to" condition
  • >= operator for evaluating "greater than equal to" condition

The table below describes the relational operators through some examples.

Expression using relational operator Result of the expression
${10 == 2*5} 真正
${10 > 4} 真正
${10 < 10/2}
${10 <= 20/2} 真正
${10 != 2*5}

Logical operators

Logical operators operate on two expressions and always return a boolean value. Expression language can use the following logical operators:

  • && operator returns true if both expressions evaluate to true.
  • || operator returns true if one of the expressions evaluate to true.
  • ! operator returns the inverse of the evaluation result.

The table below describes the logical operators through some examples.

Expression using logical operator Result of the expression
${10 > 4 && 4 < 16} 真正
${10 > 4 && 16 < 5}
${10 > 4 || 16 < 5} 真正
${!(10 > 4)}

Conditional operator

This operator is used for conditional processing. Depending on the boolean result of the condition, one of the two possible results is returned.

The following examples show conditional operator usage.

Expression using conditional operator Result of the expression
${(5<6) ? 5 : 6} 5
${(5>6) ? 5 : 6} 6

[] and . 经营者

As discussed above, you use [] and . operators to look for a named property in a bean or in a collection. Let's consider an example where customer is the name of a bean with property SSN .

To access the property SSN , you can use the following expression:

${customer['SSN']}

The value within the brackets must be a string literal for the property's name or a variable that holds the property's name.

You can even use a complete EL expression that resolves to a property.

Here are a few general rules that exist while evaluating expr-a[expr-b] :

  • Evaluate expr-a into value-a .
  • If value-a is null , return null .
  • Evaluate expr-b into value-b .
  • If value-b is null , return null .
  • If value-a is a map, list, or array, then evaluate whether value-b resolves to a property for it.

Using the . operator, the alternative syntax could be:

${customer.SSN}

动作

动作

Actions provide a higher level of functionality than the declarations, expressions, and scriptlets you've seen thus far. Unlike the scripting elements, actions are independent of any scripting language. In many respects, JSP actions are like built-in custom tags. In fact, only XML syntax is defined for actions; there is no equivalent <% syntax.

There are three categories of standard actions:

  • Those that use JavaBeans components
  • Those that control run-time forwarding/including
  • Those that prepare HTML for the Java plug-in

Also, JSP 2.0 introduced two standard actions you can use only in tag files. These actions are <jsp:invoke> and <jsp:doBody>. For a complete discussion of tag files, refer to Tag files .

JSPs and JavaBeans

JSP provides a remarkably well-designed integration between JavaBeans and HTML forms. The jsp:useBean , jsp:setProperty , and jsp:getProperty actions work together to achieve this integration.

行动 目的
jsp:useBean Prepare a bean for use within the JSP
jsp:setProperty Set one or more bean properties on a bean
jsp:getProperty Output the value of the bean property as a String

What is a bean?

Although a full treatment of the JavaBeans component architecture is outside this tutorial's scope, it is easy to explain the essential convention required to interact with jsp:useBean , jsp:setProperty , and jsp:getProperty .

For any given value (called a property ) of type T named N you want to make settable, the class must have a method whose signature is:

public void set  N ( T );

For any given property of type T named N you want to make gettable, the class must have a method whose signature is:

public  T get  N ();

jsp:useBean

jsp:useBean tells the JSP that you want a bean of a given name (which might be a request-time expression) and scope. You also provide creation information. The JSP checks to see if a bean of that name and scope already exists. If not, the bean is instantiated and registered.

The jsp:useBean tag is written as:

<jsp:useBean id=" name " scope="Bean-Scope" Bean-Specification/>

要么

<jsp:useBean id="  name " scope="Bean-Scope" Bean-Specification> 
   creation-body 
</jsp:useBean>

If the bean needs to be created and you use the second form of jsp:useBean , the statements that make up the creation-body are also executed.

The object made available by useBean is also known as a scripting variable and is available to other scripting elements within the JSP invoking jsp:useBean .

Bean scope

The jsp:useBean action makes the bean available as a scripting variable available within the page, but what is the overall lifespan of the bean? Is it recreated each time? Is there a unique copy of the bean for each session?

That is the purpose of the scope attribute. The bean remains available throughout the lifetime of the specified scope, which must be one of the following:

范围 持续时间
page The bean will be good only within the defining JSP and will be recreated for each new request.
request The bean will be good throughout that request and is available to included or forwarded pages.
session The bean will be associated with the particular session responsible for its creation and is good for the lifetime of the session.
application The bean is common to all sessions and is good until the Web application terminates.

Bean specification

The bean-specification attributes are extremely flexible and cover a wide range of options, as illustrated by the following table.

Specification 含义
class="className" class is the implementation class for the object.
type="typename" class="className" type is the type to be used for the bean within the page and must be compatible with the class. class is the implementation class for the object.
type="typeName" beanName="beanName" type is the type to be used for the bean within the page. beanName is the name of an existing bean and will be passed to java.beans.Beans.instantiate() . The beanName might be a JSP expression, whose value is computed at request time. Such an expression must use the <%-syntax.
type="typeName" type is the type to be used for the bean within the page.

jsp:setProperty

The jsp:setProperty action is a high-level, scripting-language-independent method for setting the values of a scripting variable.

The syntax for the jsp:setProperty action is:

<jsp:setProperty name=" beanName "  propertyExpression />

The value used for beanName is the name that was used for the id attribute in the jsp:useBean action, or a name similarly assigned by a custom tag. So, following a jsp:useBean statement like:

<jsp:useBean id = "myName" ... />

A subsequent jsp:setProperty (or jsp:getProperty ) action would use:

<jsp:setProperty name = "myName" ... />

The propertyExpression attribute

The propertyExpression for jsp:setProperty can take one of several forms, as the following table shows.

Property expression 含义
property="*" All bean properties for which there is an HTTP request parameter with the same name is automatically set to the value of the request parameter. This is probably the single most frequently used form of jsp:setProperty , typically used in conjunction with an HTTP form.
property="propertyName" Sets just that property to the corresponding request parameter.
property="propertyName" param="parameterName" Sets the specified property to the specified request parameter.
property="propertyName" value="propertyValue" Sets the specified property to the specified string value, which will be coerced to the property's type. The value might be a JSP expression, whose value is computed at request time. Such an expression must use the <%-syntax.

jsp:getProperty

The jsp:getProperty action is the counterpart to the jsp:setProperty action. Just as you use jsp:setProperty to set values into a scripting variable, you use jsp:getProperty to get the values from a scripting variable.

The syntax for the jsp:getProperty action is:

<jsp:getProperty name="name" property="propertyName" />

The result of the jsp:getProperty action is that the value of the specified bean property converts to a String , which is then written into the out object. This is essentially the same as a JSP expression of:

<%= beanName.getProperty() %>

Using the bean-related actions

Here is a simple self-contained example that uses the bean-related actions:

HelloWorld13.jsp

<HTML>
<jsp:declaration>
// this is a local "helper" bean for processing the HTML form
static public class localBean 
{
  private String value;
  public String getValue()            { return value;}
  public void setValue(String s)      { value = s; } 
}
</jsp:declaration>

<jsp:useBean id="localBean" scope="page" class="localBean" >
<%-- Every time we create the bean, initialize the string --%>
<jsp:setProperty name="localBean" property="value" value="World" />
</jsp:useBean>

<%-- Whatever HTTP parameters we have,
     try to set an analogous bean property --%>
<jsp:setProperty name="localBean" property="*" />

<HEAD><TITLE>HelloWorld w/ JavaBean</TITLE></HEAD>
<BODY>
<CENTER>
<P><H1>Hello
<jsp:getProperty name='localBean' property='value'/></H1></P>
<FORM method=post>
Enter a name to be greeted:
<INPUT TYPE="text" SIZE="32" NAME="value"
VALUE="<jsp:getProperty name='localBean' property='value'/>">
<BR>
<INPUT TYPE="submit" VALUE="Submit">
</FORM>
</CENTER>
</BODY>
</HTML>

Explanation: The local bean

This is our Hello World example, enhanced so that instead of greeting the world, we can tell it whom to greet.

The first change is that we declared a JavaBean within a declaration:

static public class localBean
{
    private String value;
    public String getValue()       { return value; }
    public void setValue(String s) { value = s; }
}

Yes, you can do that, and it is convenient for creating helper beans. There are drawbacks to declaring beans within a JSP, but locally declared beans can also be quite convenient under specific circumstances. Our bean has a single String property named value .

Important: You must always use page scope with any locally declared beans.

Explanation: Using the jsp:useBean tag

Next, you use a jsp:useBean action, so you can use a bean within your JSP:

<jsp:useBean id="localBean" scope="page" class="localBean" >
<%-- Every time we create the bean, initialize the string --%> 
  <jsp:setProperty name="localBean" property="value" value="World" /> 
</jsp:useBean>

The action tells the JSP container that you want to use a bean named localBean, that the bean will be used only within this page, and that the class of bean is localBean. If the bean does not already exist, it is created for you. Notice the lack of a closing / at the end of the jsp:useBean tag. Instead, there is a </jsp:useBean> tag later on. Everything that appears between the opening and closing tags is considered the action body. The body is executed if -- and only if -- the bean is instantiated by the jsp:useBean tag. In this case, because the bean exists only for the lifetime of the page, each request creates it anew, and, therefore, you will always execute the body.

Explanation: Using the jsp:setProperty tag

The body of our action consists of a single jsp:setProperty tag:

<jsp:setProperty name="localBean" property="value" value="World" />

The jsp:setProperty tag in the body names your bean and indicates that it wants to set the property named value to World. This means that the default is to greet the world. But how do you greet someone else? That is when the HTML form and the other jsp:setProperty tag enter the scenario:

<%-- Whatever HTTP parameters you have, 
    try to set an analogous bean property --%>
<jsp:setProperty name="localBean" property="*" />

As the comment implies, the second jsp:setProperty tag uses a bit of JSP magic on your behalf. It takes whatever fields you've named in an HTML form and, if your bean has a property of the same name, its value is set to the value submitted via the form. Right now, you only have one field, but when you have complex forms, you will really appreciate the simplicity of this single tag.

Explanation: Using the jsp:getProperty tag

Next, your JSP displays a greeting, but with a twist. Instead of simply saying "Hello World," the page asks the bean whom it should greet and displays that name:

Hello <jsp:getProperty name='localBean' property='value'/>

The jsp:getProperty tag is similar to the jsp:setProperty tag; it takes the name of the bean and the name of the property. The result of the jsp:getProperty tag is similar to that of a JSP expression: The value of the property is converted to a string and written into the output stream. So, whatever value is placed into your bean is used as the name to be greeted.

Explanation: the HTML form

That brings us to the final part of our example, the HTML form itself:

<FORM method=post>
Enter a name to be greeted:
<INPUT TYPE="text" SIZE="32" NAME="value"
VALUE="<jsp:getProperty name='localBean' property='value'/>">
<BR>
<INPUT TYPE="submit" VALUE="Submit">
</FORM>

There is nothing particularly special going on here, now that you understand how these tags work. The form simply defines a single input field. Whatever value you enter into that field is put into your bean by the jsp:setProperty tag. The form also uses a jsp:getProperty tag to initialize the field, as a convenience.

JSPs can be smart forms

Did you notice that no form action is associated with the sample form?

The form submits back to your JSP, which handles the form directly. JSPs do not force you to submit a form's content to a CGI script, or other third party, for processing. Often, a FORM tag has an action parameter, which tells the form to which URL the form data should be submitted, encoded as GET parameters or POST data. However, when you leave off the action parameter, the FORM defaults to submitting it back to the current URL. This is why the form embedded in your JSP is posted back to your JSP for processing.

The jsp:include , jsp:forward , and jsp:param actions

The jsp:include and jsp:forward actions allow you to use the output from other pages within (or instead of, respectively) a JSP's content.

行动 目的
jsp:include Include the referenced resource's content within the including page's content
jsp:forward Substitute the referenced resource's content for the forwarding page's content
jsp:param Pass a parameter to the resource referenced by the enclosing jsp:include or jsp:forward

jsp:include

The page parameter tells the JSP container to include another resource's content into the stream. The resource is specified by a relative URL and can be dynamic (for example, a JSP, servlet, or CGI script) or static (for example, an HTML page).

The difference between the jsp:include action and the include directive is that the action dynamically inserts the content of the specified resource at request time, whereas the directive physically includes the content of the specified file into the translation unit at translation time.

The jsp:include action is written as:

<jsp:include page=" relativeURL " flush="true" />

The flush parameter tells the JSP that whatever output you've written into the stream so far should be committed. The reason for this is that the output could be buffered, and you need to flush your buffer before you let someone else write to the output stream.

Prior to JSP 1.2, flush is required, and the mandatory value is true . For JSP version 1.2 and later, the default flush is false , so flush is optional.

jsp:forward

The jsp:forward action tells the JSP container that you want to forward the request to another resource whose content will substitute for your content. The resource is specified by a relative URL and can be dynamic or static.

The jsp:forward action is written as:

<jsp:forward page=" relativeURL " />

The page parameter specifies to which resource the request should be (re)directed. The resource is specified by a relative URL and can be dynamic or static.

The jsp:forward action is only permitted when you have not yet committed any content to the output stream.

jsp:param

The jsp:param action provides parameters that can be passed to the target page of an include or forward action, and is written as:

<jsp:param name=" name " value=" value " />

The parameters are [name, value] pairs that are passed through the request object to the receiving resource.

jsp:include example

The following example illustrates the jsp:include action:

UseHeader.jsp

<HTML>
<jsp:include page="head.jsp" flush="true">
<jsp:param name="html-title" value="JSP Include Action" />
</jsp:include>
<BODY>
</BODY>
</HTML>

The included page, head.jsp, is as follows:

head.jsp

<HEAD>
<TITLE>
<%=(request.getParameter("html-title") != null) ? 
    request.getParameter("html-title") : "UNTITLED"%>
</TITLE>

<META HTTP-EQUIV="Copyright" NAME="copyright" CONTENT="Copyright
  (C) 2001 Noel J. Bergman/DEVTECH All Rights Reserved." >
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
</HEAD>

The example consists of two separately compiled JSPs: UseHeader.jsp and head.jsp. Since the JSP generates no body content, the only thing you'll notice when you look at the browser view is the title. The source view shows the HEAD section.

When you request UseHeader.jsp, that JSP in turn invokes head.jsp to generate the HEAD section of the resulting HTML output. The generated HEAD section includes only a few tags in this example, but you could add as many as you want.

You use the jsp:param action to pass your desired title to head.jsp, where you use the request object to get it. If the parameter is not provided, the page still generates a default TITLE tag for you. You can see how you could provide support for keywords, descriptions, and so forth with appropriate default values for your Web sites.

The jsp:plugin action

Political and legal battles within the software industry have resulted in a situation where the JVM built into a browser is often outdated. The solution to the problem of out-of-date or missing browser support for Java is the ability to plug newer JVM versions into the browser. That is what the Java Plug-in provides.

The jsp:plugin action is a specialized tag, whose sole purpose is to generate the appropriate <OBJECT> or <EMBED> tag to load the Java Plug-in software as necessary, then load a specified applet or JavaBean.

The jsp:plugin action is written as:

<jsp:plugin type="bean|applet" code=" objectCode " codebase=" URL " [align=" alignment "]
[archive=" archiveList "] [height=" height "] [hspace=" hspace "]
[jreversion=" JRE-version "] [name=" componentName "] [vspace=" vspace "] 
[width=" width "] [nspluginurl=" URL "] [iepluginurl=" URL "]>

[<jsp:params> <jsp:param name=" name " value=" value " />* </jsp:params>]

[<jsp:fallback> arbitrary content </jsp:fallback> ]

</jsp:plugin>

All attributes except for type , jreversion , nspluginurl , and iepluginurl are defined by the HTML specification for the <OBJECT> tag.

属性 含义
type The type of component (an applet or a JavaBean)
jreversion The JRE version the component requires
nspluginurl A URL from which to download a Navigator-specific plug-in
iepluginurl A URL from which to download an IE-specific plug-in

jsp:plugin example

The following jsp:plugin and <APPLET> tags are equivalent:

jsp:plugin:

<jsp:plugin type="applet"
code="com.devtech.applet.PhotoViewer.class"
codebase="/applets"
archive="gallets.jar"
WIDTH="266" HEIGHT="392" ALIGN="TOP" >
<jsp:params>
<jsp:param name="image" value="1" />
<jsp:param name="borderWidth" value="10" /> 
<jsp:param name="borderColor" value="#999999" /> 
<jsp:param name="bgColor" value="#000000" /> 
</jsp:params>
<jsp:fallback>
<P>Unable to start Java plugin</P>
</jsp:fallback>
</jsp:plugin>

<APPLET> tag:

<APPLET WIDTH="266" HEIGHT="392"
BORDER="0" ALIGN="TOP" 
CODE="com.devtech.applet.PhotoViewer.class" 
CODEBASE="/applets/" ARCHIVE="gallets.jar"> 
    <PARAM NAME="ARCHIVE" VALUE="gallets.jar"> 
    <PARAM NAME="image" VALUE="1">
    <PARAM NAME="borderWidth" VALUE="10">
    <PARAM NAME="borderColor" VALUE="#999999">
    <PARAM NAME="bgColor" VALUE="#000000">
</APPLET>

As seen above, jsp:action is similar to the <APPLET> tag. It's slightly more verbose, but permits the server to generate the appropriate tags based on the requesting browser.

Tag files

Overview of tag files

JSP 2.0 introduced tag files. These are essentially tag extensions you can invoke via your JSPs.

Unlike with JSP 1.x, where page authors had to write tag handlers to generate tag extensions, with JSP 2.0, page authors do not need Java knowledge to write tag extensions. Now with tag files, you can write tag extensions using only JSP syntax. It echoes the purist belief that page authors should concentrate on presentation and not on server-side languages to write Web pages.

Let's discuss tag file basics.

Basics of tag files

The required file extension for tag files is .tag. Similar to how JSPs can contain JSP fragments, tag files can also contain tag fragments. The extension for such tag fragments is .tagx.

For a JSP container to recognize these tag files, you must place them in one of the two possible locations:

  • If your JSP files and tag files are part of a JAR file, you can place tag files in the /META-INF/tags/ directory (or a subdirectory of /META-INF/tags/) in a JAR file installed in the /WEB-INF/lib/ directory of the Web application.
  • If your JSP files and tag files are not part of a JAR file, you can place your tag files in the /WEB-INF/tags/ directory (or a subdirectory of /WEB-INF/tags/) of the Web application.

Tag files placed in locations other than the ones above are not considered tag extensions, and, according to JSP 2.0 specification, the JSP container will ignore them.

Tag tiles bundled in a JAR require a tag library descriptor (TLD). Per the JSP 2.0 specification, tag files not described in TLD but packaged in the JAR must be ignored by the JSP container.

To describe tags within a tag library, JSP 2.0 introduced a new TLD element: <tag-file> . This element requires <name> and <path> sub-elements that define the tag name and the full path of the tag file from the root of the JAR.

Directives applicable to tag files

JSP 2.0 introduced three new directives: the <tag> directive, the <attribute> directive, and the <variable> directive. These directives are available only to tag files. Let's examine these directives in detail.

指示 用法
tag The tag directive is similar to the page directive, but applies to tag files instead of JSPs. Apart from attributes like language, import, and pageEncoding that are also present in page directive, the tag directive has some attributes applicable to tag files only. These are the important ones:
  • display-name : It is a short name intended to be displayed by tools. Defaults to the name of the tag file without the .tag extension.
  • body-content : Provides information on the content of the body of this tag. Defaults to scriptless.
  • dynamic-attributes : The presence of this attribute indicates this tag supports additional attributes with dynamic names. A discussion on dynamic attributes is outside this tutorial's scope.
attribute The attribute directive allows the declaration of custom action attributes. Usage of the attribute directive is explained in more detail in the next section. You can use the following attributes with the attribute directive:
  • name : This is the unique name of the attribute being declared. This is a required attribute.
  • required : Whether this attribute is required (true) or optional (false). 默认值为false。
  • fragment : Whether this attribute is a fragment that should be evaluated by the tag handler (true) or a normal attribute that should be evaluated by the container prior to being passed to the tag handler.
  • rtexprvalue : Whether the attribute's value might be dynamically calculated at run time by a scriptlet expression.
  • type : The run-time type of the attribute's value. Defaults to java.lang.String if not specified.
  • description : Description of the attribute.
variable The variable directive defines the details of a variable exposed by the tag handler to the calling page.

Tag file implementation

Let's discuss a simple example that displays the total premium for an automobile insurance policy. First assume that this automobile policy can have liability coverage or thirdparty coverage, and the total premium is the sum of these coverages.

So, you have a JSP that is the parent page and a tag file that takes individual premiums for liability coverage and thirdparty coverage and displays the total premium:

automobile-policy.jsp

<%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %>
<p>
Automobile-Policy Page
</p>
<tags:coverages liability="2000" thirdparty="4500" >
<p>
The total premium will be: 
</p>
</tags:coverages>

The above JSP defines tags as the prefix for the tag files contained inside the /WEB-INF/tags folder. tags:coverages invokes the tag file coverages.tag. Notice that tags:coverages passes the values of liability premium and thirdparty premium as attributes. The tag file coverages.tag calculates the total premium value:

coverages.tag

<%@ tag body-content="scriptless" %>
<%@ attribute name="liability" required="false" %>
<%@ attribute name="thirdparty" required="false" %>
<jsp:doBody/>
<p> ${liability + thirdparty} </p>

The code above first defines the tag directive that provides information that the tag's content is scriptless. It does not contain any scripting elements. The lines below use the attribute directive and define the two attributes liability and thirdparty . required has a value of false that suggests that these attributes are not mandatory.

The <jsp:doBody/> is a new standard action introduced in JSP 2.0. This action suggests that the JSP contains content between <tags:coverages> and </tags:coverages> . If the JSP does not contain any content, you must define the value of the body-content attribute as empty instead of scriptless.

The last line in the tag file uses JSP 2.0 expression language to calculate the total premium.

To run this code, place coverages.tag inside the /WEB-INF/tags/ folder of the Web application.

Alternative technologies

Competing technologies

For many years, JSP has enjoyed a near total monopoly as the preferred technology for generating client-side presentation logic. However, many other technologies are now competing for a foothold in this arena. Some of these are:

  • WebMacro -- A 100-percent Java open source template language
  • Tapestry -- A powerful, open source, all-Java language framework for creating leading-edge Web applications in the Java language
  • FreeMarker -- A template engine written purely in the Java language
  • Apache Cocoon -- A Web development framework built around the concepts of separation of concerns and component-based Web development
  • Velocity -- An open source templating solution

速度

Of the above technologies, Velocity is the one creating ripples in Java/open source circles. So, let's look at Velocity more closely.

Velocity is an open source templating solution you can use in place of JSP for creating dynamic Web pages. Working with Velocity involves a template that contains the static portion of the output and placeholders for the dynamic content.

For example, a Velocity template might contain lines like these:

<html>
<head>
</head>
<body> 
#set( $this = "Velocity") This page is generated using $this. 
</body> 
</html>

As you can see, the template is a complete HTML file. The last line above contains a placeholder $this . This placeholder is resolved at run time by a templating engine, and the output is something like this:

This page is generated using Velocity.

This seems easy and straightforward. 是的。

Velocity has several advantages over JSP:

  • Velocity's biggest advantage is that it is interpreted at run time. This means you can read templates from anywhere, even from a database, and the changes are instantly reflected.
  • Velocity helps enforce a clean separation between the view layer and the model/control layers.
  • Velocity, which caches templates for speed, reportedly has performance comparable or better than JSP.
  • Velocity templates are not limited to HTML, and you can use it to produce any type of text output, including XML, SQL, ASCII, and PostScript.

But JSP scores over Velocity in other areas:

  • JSP 2.0 supports JSTL. So, JSP has the advantage of existing taglibs that make usage easy.
  • In JSP 2.0, expression language makes coding JSPs simple.
  • In JSP 2.0, tag files make writing custom tags easy.
  • JSP inherently works with server-side code.
  • JSP has long been a standard with a large following of experienced programmers.

Ultimately, the decision to use Velocity or JSP depends on the programming model you're developing. In fact, new technologies drive the industry.

Wrapping up

摘要

We have completed an overview of the JSP specification, including updates in JSP 2.0. It is important to understand that this introduction presented what you can do with JSP. This is not necessarily the same as what you should do. The former deals with syntax and grammar; the latter deals with philosophy and methodology.

For example, one of the benefits of using JSP is separating presentation from business logic. To that end, many JSP practitioners maintain that it's bad to put any Java code into a JSP, on the grounds that it's not maintainable by nonprogramming Web designers. In the extreme, the only JSP features they permit are the use of the action tags and custom tag libraries (and the attendant directives). Some permit "simple" expressions; 别人没有。

This introductory material follows the approach that one does not teach a subject by avoiding its features in an effort to advance a particular methodology. Ignoring debated aspects of a technology places the reader at a disadvantage. Furthermore, this tutorial's target audience is not the nonprogramming Web designer; it is the Java programmer who wants to employ JSP in content delivery solutions. It is just as likely that a reader might be involved in developing, say, new WYSIWYG editor tools that generate JSPs that use JSP constructs internally while at the same time hiding them from the user. And it is certainly the case that rapid prototyping might employ different tools from those used in production.

We abide by the philosophy that it is better to teach all of the options, illustrating why and when certain approaches are more or less desirable than others in practice. As this is only an introductory tutorial, we are not able to delve into such topics as programming models you can use to implement JSP solutions or how you can use JSP to manage the look and feel associated with your content. We recommend that you review other documents and more advanced tutorials that offer such information.


翻译自: https://www.ibm.com/developerworks/java/tutorials/j-introjsp/j-introjsp.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值