标签前面的三个空格在Servlet的out.write()语句中也是原样输出的。小贴士要查阅这个JSP 文件对应的 Servlet 代码,请查找下面的这个文件: %Catalina_HOME%\work\Catalina\localhost\jspBook\org\apache\jsp\ch06\sample_jsp.java。
JSP 元素(Elements)是必须经由 JSP 容器处理的部分,主要包括如下四种:
l指令元素:用来从整体上控制 Servlet 的结构;
l脚本元素: 用来嵌入Java 代码, 它们将成为JSP 页面转换得到的 Servlet 的一部分;
l动作元素:用来引入现有的组件或者控制JSP 引擎的行为;
l EL(Expression Language)元素:为简化开发中对于对象的引用和方便存取数据而 自定义的语言,是JSP 2.0 正式纳入的标准规范之一。
下面将用三节的篇幅分别详细介绍前三种类型的 JSP 元素;对于表达式语言(EL)和 标准标签库(JSTL),将在第 12 章《应用JSTL和 EL简化 JSP 开发》中将做较为详细的介 绍。
6.1.4 注释
JSP 页面中的注释可以分成三种:HTML/XML注释、JSP 专用注释和 Scriptlet 注释。
1. HTML/XML注释
这类注释原样发送到客户端,虽然用户浏览时看不到,但是可以通过“查看源文件”看 到这些代码。其语法格式为:
例如清单 6.1第 11 行所示:
在客户端的HTML源文件中可以看到完全一模一样的内容。
另外,这类注释中还可以嵌入一些动态的表达式,例如:
在客户端的HTML源文件中显示为:
·139·
2. JSP 专用注释
这类注释是JSP 程序员专用的注释,它写在JSP 程序中,但不发送给客户端,因而通常 称为隐藏注释。其语法格式为:
<%--comment--%>
或者:
<% /**comment**/ %>
例如清单 6.1第 12、33 行所示:
<%-- @author Wuerlang, 2008-6-26 10:18.--%>
<%
/**下面是使用表达式的例子**/ %>
第 33 行实质上使用的是Java 形式的一种首尾封闭的注释形式。
小贴士
用隐藏注释标记的字符会在JSP 编译时被忽略。 JSP 容器不会对它们中间
的语句进行编译,当然,也谈不上执行,更不会显示在客户端浏览器中。
注意在<%--和--%>之间,可以任意书写注释语句,但注释不能包含“--%>” 。
3. Scriptlet 注释
由于 Scriptlet 包含的是 Java 代码,所以 Java 中的注释规则在 Scriptlet 中同样适用。在 JSP 中,常用的Java 注释语句有表示单行注释的“//”和表示多行注释的“/* */” 。
例如清单 6.1第 21、
23 行所示:
/*通过循环输出 10 行“Hello World!” ,且奇偶行的背景颜色不同*/
//声明两个整型变量
小贴士使用隐藏注释和 Scriptlet 注释是 JSP 程序实用的方法,也是 Java 程序员 通常使用的方法。
6.1.5 Case:转义字符——Quoting和 Escape 规则
Quoting 主要是为了避免与语法产生混淆所使用的转换功能。为了能深刻理解这一点, 先来看例程Quoting-1.jsp及其执行结果。例程 Quoting-1.jsp的程序代码如清单 6.2所示。
清单6.2 转义字符示例Quoting-1.jsp
6:
Quoting例程-110:
Quoting例程-1
11: <%
12: out.println("在HTML标记中,对于属性的值可以使用定界符双引号""、单引号 '',或者干脆都不用。")?
13: out.println("
JSP以<%作为起始标记,以%>作为结束标记。")?
14: %>
例程 Quoting-1.jsp的执行结果如图 6.6 所示。
·140·
·141
·
图6.6 例程Quoting-1.jsp 的执行结果
为什么会出现这样的结果呢?我们在 Word 、记事本中一直是直接输入双引号""、单引 号''的呀!
这是因为双引号""、单引号''以及<%、%>、\等符号是 JSP 语法规定使用的字符或字符 串,当程序执行到""时,JSP 容器就直接提示程序有错误,如图 6.3 所示。通常,为了避免 产生这样的结果,当需要在浏览器中显示这些符号时,需要做如下适当修改:
l 起始标签(<%)改为:<?%
l 结束标签(%>)改为:%\> 或 %>?,推荐使用%>?
l 双引号(")改为:\"
l 单引号(')改为:\'
l 反斜杠(\)改为:\\ l
尖括号(< 和 >)改为:<?和>?
练一练 请读者将 Quoting-1.jsp 另存为 Quoting-2.jsp ,并独立完成修改工作。
6.1.6 简单认识 JSP 中的 XML
XML 是 eXtensible Markup Language 的缩写,意为可扩展的标记语言。第1 章 1.4节简 要介绍了XML 数据文档的基本结构,并给出了省份城市和高校的 XML 数据片段,1.5 节应 用 JavaScript 操纵这些 XML 数据构造了一个级联菜单。这些都是 XML 在 HTML 中的简单 应用。此外,在JSP 中还广泛使用 XML ,JSP 各个元素普遍都有等价的XML 语法格式,或 直接是 XML 的语法格式。
Java 5.0 自带所有和 XML 协同工作的基本 API ,包括javax.xml 标准扩展包,该包处理 用于 XML 的简单 API (Simple API for XML,SAX ) 和文档对象模型 (Document Object Model ,
·142·
DOM )以及扩展的风格表单语言(eXtensible Stylesheet Language ,XSL )转换。
JSP 的发展无疑将越来越强调XML 的语法格式, 读者可以从JSP 1.2及最新的JSP 2.0/2.1 语法规范中发现这一变化趋势。
为什么要使用 XML 相容的语法格式呢?主要是基于以下几个方面:
l 使用 XML 相容语法来构架 JSP ,当然不是因为 XML 是一个时髦的事物,而是作
为 XML 文档的 JSP 将会得到许多的好处。例如,一个标准的 XML 相容的 JSP 语 法将有助于JSP 的开发。
l JSP 文件的XML 语法使得JSP 文件的内容很容易被组织和管理。
l 可以使用XML 的开发和分析工具来开发和分析JSP , 仅仅需要更换 DTD 文件就可
以升级到最新版本的JSP 。
l XML 格式统一的语法更容易学习和使用。
关于 Web 中的XML 编程,本书第 11 章《利用JSP 技术进行 XML 编程》将重点介绍。
小贴士 关于 XML 的相关知识,读者可以到 https://www.doczj.com/doc/1a6880772.html,/TR/xml-infoset/查 阅详细资料;关于 javax.xml 包的详细信息,请访问:https://www.doczj.com/doc/1a6880772.html,/j2se/1.5.0/docs/api/。
6.2 JSP 指令元素(Directive Elements )
JSP 指令(Directive )是为 JSP 引擎而设计的,用于从JSP 发送一个信息到JSP 容器上。 JSP 指令主要用来提供整个JSP 网页相关的信息,设定 JSP 网页的相关属性,如:网页的编 码方式、语法、信息等。它们并不直接产生任何可见输出,而只是告诉引擎如何处理其余的 JSP 页面。 所有的指令都在JSP 整个文件范围内有效。 指令元素为翻译阶段提供了全局信息。 其语法格式为:
<%@ directiveName attribute="value"[ attribute2="value2" …]%>
JSP 指令元素有三个:page (页面)指令、include
(包含)指令和 taglib (标签)指令。 下面逐一介绍这三种指令元素。 小贴士 JSP 指令始终被括在“<%@”和“%>”标记中。顺便提示,这些标记符 号本身的字符之间,不要有任何空格。
6.2.1 page 指令 回顾例程 sample.jsp (参见清单 6.1)的前两行:
<%@ page contentType="text/html? charset=utf-8" language="java" import="java.sql.*" errorPage="" %>
<%@ page info="一个典型的 JSP 文件" %>
这就是出镜率颇高且通常在 JSP 页面顶部露脸的 page 指令。几乎在所有 JSP 页面顶部 都会看到它的身影,尽管它不是必须的。
page 指令的主要功能是设定整个JSP 网页的属性和相关功能。其语法格式如下:
<%@page attribute="value" attribute2="value2" … %> 如同所有的JSP 标签元素一样, page 指令也支持以XML 为基础的语法格式, 如下所示:
page 指令有 13 个属性,基本含义见表 6.2。
表6.2 JSP 页面指令的属性
·143· 属
性
描述 示 例 language
定义所使用的脚本语言,目前别无选择,只能使用Java language="java" import 主要定义该网页使用哪些Java API ,用“, ”隔开包或者类列 表 import="java.util.*"
import="java.sql.*, java.util.*"
extends 主要定义该网页产生的Servlet 是继承哪个父类。使用该属性
应当十分小心,因为服务器可能已经在用自定义的父类
session 指定一个Http 会话中本网页是否参与,默认值true session="true" buffer 指定JSP 到客户端输出流的缓存大小。 默认值和服务器有关, 但至少应该是8 KB ;none 表示不缓冲
buffer ="32KB"
autoFlush
默认值true 表明如果缓存已满则刷新它;false 值表示如果缓 存已满则抛出异常,很少使用。如果buffer="none",autoflush
不能取false 值。 autoflush="true"
isThreadSafe 用来设置 JSP 文件是否能多线程使用。默认值 true 表示该网 页能够同时处理多个用户的请求;取值false 表示一个JSP 一
次处理一个请求
isThreadSafe="true"
info
定义一个可以通过getServletInfo 方法提取的字符串 info="一个典型的JSP 文件" errorPage 指定一个 JSP 页面,所有未被当前页面捕获的异常均由该页 面处理
errorPage="error.jsp"
contentType 指定输出的MIME 类型。默认是text/html contentType="text/html?
charset=gb2312"
isErrorPage 指示当前页面是否可以作为另一 JSP 页面的错误处理页面。 默认值false
isErrorPage="false"
pageEncoding
JSP 页面的字符编码 pageEncoding="gb2312" isELIgnored 制定表达式语言EL 是否被忽略,如果为true ,则JSP 容器忽 略“${}”表达式的计算。默认值由web.xml 描述文件的版本
确定,Servlet 2.3以前的版本将忽略
isELIgnored="true"
小贴士
①这些页面指令可以单独使用,也可以同时使用几个。
②在同一个JSP 文件中,import 是唯一允许出现一次以上的属性。
关于 page 指令及 errorPage 、isErrorPage 属性的一个简单例子,请读者参阅第 7 章 7.7 节《exception 对象》。
6.2.2 include 指令
回顾清单 6.1 sample.jsp 第 37 行:
<%@ include file = "pub/copyright.html" %>
这就是一个典型的 include 指令。它通知 JSP 容器将指定位置(如 pub 目录)中的资源 (如 copyright.html 文件)内容内嵌到当前页面。其语法格式如下:
<%@ include file="relativeURL"%>
对应的 XML 相容的语法格式为:
include指令只有 file一个属性。被包含文件的路径名一般来说是指相对路径,不需要什 么端口、协议或域名,如果这个路径以“/”开头,那么这个路径主要是参照 JSP 应用的上 下关系路径;如果路径是以文件名或目录名开头,那么这个路径就是正在使用的JSP 文件的 当前路径。
include指令是静态包含其他文件, 被包含的文件可以是 html 文件、 jsp文件、 文本文件、 inc文件,或者只是一段Java 代码。如果被包含的文件中含有可执行代码,那么这个被包含 的文件中代码将会被执行。一旦被包含文件被执行,那么主JSP 文件的过程将会被恢复,继 续执行下一行。如果这个包含文件被改变,包含此文件的主JSP 文件将被重新编译。被包含 的文件或其执行的结果将会插入到主JSP 文件中 include指令相应的地方。
注意
①包含文件中不能使用、、
、等标记,因为这将会影响在 原 JSP 文件中同样的标记,有时会导致错误。②include 指令是静态包含其他文件。何谓静态包含?首先,参数 file 不能是一个变量 URL, 也不可以在 file所指定的文件名后接任何参数; 其次, 被包含的文件将会被插入到JSP 文件中去。
6.2.3 taglib 指令
taglib 指令是 JSP 1.1 增加的功能,它允许设计者自定义一个标签库及标签的前缀。设计 者首先要开发标签库,为标签库编写.tld配置文件,然后在JSP 页面中使用自定义标签。
在 JSP 规范中, 标签库得到了不断加强。 JSP 2.0 规范中增加了JSP 标准标签库 (JSTL)。
taglib 指令的语法格式如下:
<%@ taglib uri="tagLibraryURI" prefix="tagPrefix" %>
以 XML为基础的语法格式为:
其中, 属性 uri 用来表示标签描述符, 即告诉JSP 容器怎么找到标签描述文件合标签库; 属性 prefix 定义了在 JSP 页面里要引用这个标签时的前缀,但不能使用用 jsp、jspx、java、 javax、servlet、sun、sunw等作为前缀。
对于自定义标签库及标准标签库(JSTL),在第 12 章《应用JSTL和 EL简化 JSP 开发》 中将做进一步的简要介绍。
6.3 JSP脚本元素(Scripting Elements)
JSP脚本元素用来插入Java代码, 这些Java代码将出现在由当前JSP页面生成的Servlet 中。JSP 脚本元素主要包括如下三种:
l表达式(Expression) :计算表达式并输出其结果。
l脚本片段(Scriptlet) :把代码插入到 Servlet 的 service方法。
l声明(Declaration) :把声明加入到 Servlet 类(在任何方法之外)。
下面依次详细介绍这三种脚本元素。
6.3.1 表达式(Expression)
表达式在JSP 请求处理阶段计算它的值,执行之后会被自动转化为字符串,然后显示出
·144·
·145· 来。其语法格式如下:
<%= expression %> 等价的 XML 表达式如下:
expression
例如如示例 sample.jsp (清单 6.1)的第34 行:
<%="经过 for 循环之后,count 的值为:"+count %>
表达式的运算规则就是Java 表达式,其结果必须是一个字符串或者可以转换成字符串。 在编译成 Servlet 后,表达式实际上被转换成 out.print()方法中的内容。如上例在 Servlet 中变 成:
out.print("经过 for 循环之后,count 的值为:"+count )?
关于 out
对象及其功能和应用,将在第 7章 7.3 节详细介绍。 小贴士 上述 Servlet 代码参见下述文件的第 95 行:
%Catalina_HOME%\work\Catalina\localhost\jspBook\org\apache\jsp\ch06\sample_jsp.java 。 因而,可以在一行文本中插入表达式。通过使用表达式,可以使程序变的简洁。 注意
①JSP 表达式只能单独出现,即不能在<%=…%>中间出现多个表达式。
②使用表达式时,在<%=中间不能有任何的空格。例如:< %="abc"%>将原样输出, 而<% ="abc"%>将引发异常。
③不能用一个分号(;)作为表达式的结束符。因为表达式本身是一个式子,而非 Java 语句。
④JSP 表达式的<%=…%>和 expression 只能定义在一行上,而且表达式 expression 必须 能转换成字符串形式。需要的话,可以使用 toString()进行转换。
6.3.2 脚本片段(Scriptlet )
Scriptlet 元素即脚本片段,或称为代码片段,是一段在 Web 服务器响应请求时就会执行 的 Java 代码。这些代码片段,只要合乎Java 本身的标准语法即可。其语法格式为:
<%Java code fragment %>
等价的 XML
语法格式如下:
Java code fragment
一个 Scriptlet 能够包含多条 jsp 语句(并不局限于一行源代码)、方法、变量、表达式, 以及使用任何隐含的对象和任何用声明过的对象。当JSP 收到客户的请求时, Scriptlet 就会被执行,如果 Scriptlet 有显示的内容,这些显示的内容就被保存在 out 对象中。
JSP 脚本元素是 JSP 代码中使用最频繁的元素,特别是 Scriptlet ,在早期的 JSP 代码中 它占有主导地位。
注意
①编写 Scriptlet 时,任何文本、HTML 标记、JSP 元素都必须在 Scriptlet 之外。
②需要特别提醒读者的是,不能在 Scriptlet 中定义方法。
在脚本片段周围可能是原始的HTML 或 XML 语句, 在这些地方, 代码片段可以创建多
·146·
条件执行代码,或要用到另外一段代码。例如,清单 6.3 所示的例程 Scriptlets.jsp 使用表达 式和代码片段,显示 h1、h2、h3 和 h4 格式的的字符串“Hello world!” 。 清单6.3 Scriptlets 应用举例Scriptlets.jsp
6:
页面访问次数10: <% int counter = 4? %>
11: 使用输出循环标题1-标题4格式的字符串“Hello world!” :
12: <% /**
13: 在后一段的Scriptlet 中还要使用前一段Scriptlet 中定义的一个变量 14: */%>
15: <% for (int i=1? i<=counter? i++) { %>
16: >Hello world!>
17: <% } %>
例程 Scriptlets.jsp 的执行结果如图 6.7 所示。
图6.7 例程Scriptlets.jsp 的执行结果
想一想 既然在 Scriptlet 中不能定义方法,那么,方法定义在什么地方呢?
6.3.3 声明(Declaration )
在 JSP 程序中,声明(Declaration )是一段 Java 代码,它用来定义在JSP 程序中用到的 变量和方法。声明可以定义页面级变量以保存信息,或者定义JSP 页面的其余部分可能需要 的支持方法。如果你发现代码太多,通常最好把它们写成一个独立的 Java 类别。声明和 Scriptlet 非常类似,只不过它以“<%!”开头,其格式如下:
<%! V ariable declaration [, declaration? …] %>
或:
<%! Method declaration(paramType param, … ) %>
等价的 XML 表达式如下:
declaration
·147·
例如:
<%! int a, b, c?
%>
<%! int i = 0? Sting s="Hello world!"? %>
<%! Circle a = new Circle(2.0)? %>
说明
声明方法或变量时,请注意以下的一些规则:
①可以一次性声明多个变量和方法,但一定要以分号(;)结束变量声明,因为任何内 容都必须是有效的Java 语句。
②可以直接使用在<%@ page %>中被包含进来的已经声明的变量和方法,无需对其 重新声明。
③一个声明仅在一个页面中有效。如果想在每个页面都用到一些声明,最好把它们写成 一个单独的文件,然后用<%@ include %>或元素把它们包含进来。
6.3.4 Case :脚本元素和页面指令综合实例
由于声明不会有任何输出,因此它们往往和JSP 表达式或 Scriptlet 结合在一起使用。例 如,清单 6.4 所示的例程 accessCount.jsp 输出自从服务器启动(或 Servlet 类被改动并重新装 载以来)当前页面被请求的次数,并输出当前日期和时间。 清单6.4 JSP 脚本元素综合应用accessCount.jsp
6:
页面访问次数10: <%! private int accessCount = 0? %>
11: 自从服务器启动以来页面访问次数为:
12: <%= ++accessCount /** 使用表达式输出页面访问次数 **/ %>
13:
现在是:
14: <%@ page import = "java.util.Date, java.text.DateFormat" %> 15: <% //输出当前日期和时间
16: Date date = new Date()?
17: DateFormat mediumFormat =
DateFormat.getDateTimeInstance(DateFormat.MEDIUM,DateFormat.MEDIUM)? 18: out.println(mediumFormat.format(date))?
19: %>
这里需要着重说明的是:使用<%! …%>方式声明的变量为页面级的全局变量,即:如 果同时有多个用户浏览此 JSP 页面,他们将会共享此变量,如清单 6.4 中声明的整型变量 accessCount ,图 6.8 模拟了两个用户同时浏览此页面的状况,当再次刷新后端浏览窗口时, 该窗口显示的访问次数值将是38,如图 6.9 所示。因此,笔者强烈建议:请慎重使用<%! … %>声明变量,通常不要使用该方式。若要使用声明变量,一般直接在<%… %>中声明使用 即可。
试一试 请将第 10 行中的“! private ”删除,对比查看“声明”的功能。
·
148·
图6.8 两个用户同时浏览
accessCount.jsp 页面
图6.9 再次刷新后端的浏览器窗口
注意 因为在JSP 容器中, JSP 会在运行前被转换成Servlet 。 而对于每一个Servlet , 在运行的时候通常只会产生一个实例,如果此时多个客户同时访问某个 Servlet 的全局变量 (实例变量),可能会引发多线程问题。因而,在 JSP 中声明变量或方法的时候,要特别注 意同步问题。
6.4 JSP 动作元素(Action Elements )
JSP 动作元素(Action Elements )是使用 XML 语法写成的,它是在请求处理阶段起作 用的,它影响JSP 运行时的行为和发送给客户的应答。其语法格式为:
或者
……
prefix:tag>
JSP 规范定义了一系列的标准动作,它用 jsp 作为前缀。这些标准动作由所有的 JSP 容 器提供,而不管它们是如何实现的。从效果上来说,一个标准动作是能够嵌入到JSP 页面之 中的一个标记。在页面被编译成 Servlet 期间,当容器遇到这个标记时,就用相应于请求的 预定义的任务的Java 代码代替它。
目前,JSP 2.0 规范中,主要有 20项标准动作,可以简单地将它们分为 5类:
l用来存取 JavaBean 的 3 个动作元素:、、 。
l JSP 1.2 规范的 6 个动作元素:、、、 、、。
l主要用于 JSP 文档中的 6 个动作元素:、、、 、、,其中,是 JSP 2.0 新增
的元素。
l主要用来动态产生XML元素标签的值的3个动作元素: 、 、 ,这三个都是JSP 2.0 新增的元素。
l主要用在 Tag File中 2 个动作元素:、。
关于JavaBean的 3 个动作元素将在第 9 章《利用 JavaBean增强 JSP》中做详细介绍; 关于 Tag File的 2 个动作元素将在第 12 章 《应用JSTL和 EL简化 JSP 开发》 中做简要介绍; 下面重点讲解第 2 类中几个使用频繁的动作元素,其他的就暂且不再介绍了。
6.4.1 、标签
标签和、及等标签一起使用,以 key/value的形式为它们提供附加信息。其语法格式如下:
其中,name是属性相关联的关键字,value是属性的值,可以是request-time value。
例如:
注意:当与或一起使用时,被包含的网页或转向 后的网页会先看看 request 对象中除了原来的参数值之外,还有没有再增加的参数值。如果 有新增加参数值,则新的参数值再执行时,有较高的优先权。例如:
一个 request 对象有一个参数DBPassword=12345;另一个参数DBPassword= ssdlh是在 转向时所传递的参数,则网页中的 request 应该为 DBPassword=JSP。
标签只能用在内部,它没有属性(attribute)。
6.4.2 标签
1. include动作概述
回顾清单 6.1 sample.jsp第 14 行:
这就是一个简单的 include 动作。标签表示包含一个静态的或者动态的文 件。其语法格式如下:
或者:
·149·
……
其中:
①page="fileName "为相对路径,或者是代表相对路径的表达式。如果路径以“/”开头, 那么这个路径主要是参照JSP 应用的上下文关系路径;如果路径是以文件名或目录名开头, 那么这个路径就是正在使用的JSP 文件的当前路径。
②flush="true",在 JSP1.2 之前,必须使用 flush为 true,它默认值是 false。如果为 true, 当缓冲区满时,将会被清空。
③子句能让你传递一个或多个参数给动态文件,也可在一个页面中使用多
个来传递多个参数给动态文件。而 include指令(<%@ include …%>)只有
file
一个属性,无法直接向被包含文件传递参数。
说明
①可以包含一个静态文件或者动态文件,但结果是不尽相同的。如果包含 进来的只是静态文件, 那么只是把静态文件的内容加到 JSP 网页中; 如果包含的是动态文件, 那么这个被包含的文件也会被JSP 容器编译执行。
②从名称上不能直接判断一个文件是动态的还是静态的。例如,header.jsp 可能只是单 纯的包含一些信息而已,而不需要执行。能自动判断此文件是动态的还是静态 的。
③被包含的对象只有对 JspWriter 对象的访问权,并且它不能设置头或者 Cookie。如果 页面输出是缓冲的,则缓冲区的刷新要优先于包含的刷新。
④与 include指令(<%@ include …%>)不同的是,是动态包含,即在页 面真正被加载的时候才把指定的文件加进来,如果被包含的页面代码更改,在 包含页面的结果中会随之更新,即被包含的内容在 include 动作执行时才确定;而<%@ include…%>是静态编译,即包含的内容一经编译,就不能改变。
⑤的运行效率比<%@ include…%>的运行效率低,但可以动态增加内容。 如果被包含的文件经常改变,那么建议读者使用。
2. Case:include动作和 include指令的区别
下面举一个综合的例子,说明和<%@ include …%>
的应用以及它们的不
同,并深刻理解什么是静态包含什么是动态包含。
创建被包含文件 include-1.jsp,其代码如清单 6.5 所示。
清单6.5 被包含文件
include-1.jsp
1: <%!
2: String var1="China"?
3: %>
创建被包含文件 include-2.jsp,如清单 6.6 所示。
清单6.6 被包含文件
include-2.jsp
1: <%!
2: String var2="America"?
3: String var3="England"?
4: %>
小贴士上述文件 include-1.jsp 和 include-2.jsp 中的变量不要重复声明,因为它们 可能被同时包含进同一个文件。否则,将引发异常。
·150·
·
151·
创建文件 include-3A.jsp ,其代码如清单 6.7 所示。 清单6.7 包含文件
include-3A.jsp
1:
<%@ page language="java"%> 2: <% 3:
int j=1? 4:
if (j==1){ 5:
%>
6:
<%@ include file="include-1.jsp"%> 7:
<% 8:
}else{ 9:
%> 10:
<%@ include file="include-2.jsp"%> 11:
<%}%> 12: <%="
var1 = " + var1%><%="
var2 = " + var2%>
想一想 执行 include-3A.jsp 会出现什么结果?请选择:
a. 编译错误
b. 显示 var1 = China var2 = America
很多人理所当然的觉得肯定是a ,因为 j= =1,条件为“真” ,所以只包含 include-1.jsp , include-2.jsp 不会包含进来,但答案是 b 。include-3A.jsp 的执行结果如图 6.10 所示。
图6.10 include-3A.jsp 的执行结果
为什么会这样呢?因为<%@ include …%>要先于主JSP 文件的其他代码执行,所以两 个文件都会被包含进来!
(4)创建文件 include-3B.jsp ,如清单 6.8 所示。
清单6.8 包含文件include-3B.jsp
1:
<%@ page language="java"%> 2:
<% 3:
int j=1? 4:
String includeFile=""? 5:
if (j==1){ 6:
includeFile = "include-1.jsp"? 7:
}else{ 8:
includeFile = "include-2.jsp"? 9:
} 10:
%> 11:
·152·
12: <%="
var1 = " + var1%> 想一想 执行 include-3B.jsp 会出现什么结果?请选择:
a. 编译错误
b. 显示
var1 = China
选 b 吗?不,是 a. 编译错误!提示 var1 未定义。如图 6.11 所示。
图6.11 include-3B.jsp 的执行结果
为什么呢?因为是动态包含,相当于把包含文件与被包含文件分开编译。 小贴士 include 动作和 include 指令的根本区别在于:
①JSP 页面是把 include 指令元素(<%@ include file="" %>)所指定的页面的实际内容 (也就是代码段)加入到引入它的 JSP 页面中,合成一个文件后被 JSP 容器转化成一个 Servlet ,这只产生一个临时 class 文件和一个 servlet 源文件。
②动作元素()是在请求处理阶段引入的,会被JSP 容器生成两 个临时 class 文件和两个 Servlet 原文件。而引入的只是 Servlet 的输出结果,即 JspWriter 对 象的输出结果,而不是JSP 的源代码。
看一看 include 动作可以使用子元素向被包含的文件传递参数,请查 看本书综合实例“以物易物网”页眉文件 header.jsp 的调用情况。本章 6.7节将演示该示例。
6.4.3 标签
1. forward 动作概述
标签表示将客户端发送来的请求,转发到一个JSP 、Servlet 或者一个静态 html 文件。 请求被转向的资源必须位于与JSP 发送请求相同的上下文环境之中。 所谓上下文, 专业术语即 Context ,一言以蔽之,a Context 就是一个 Web 应用程序。该动作将立即执行, 它后面的代码不会执行。其语法格式如下:
或者:
·153·
……
说明
①page="relativeURL|<%=expression%>"表示 page 的值为一个字符串, 或者一个表达式, 它用于说明将要定向的文件或 URL 。这个文件通常示一个 JSP 文件、脚本片段 Scriptlets , 或者其他能够处理request 对象的文件,如一个 Servlet 。
②中,name 、value 为指定参数名及相应的参数值。参数被发送到一个动态 文件,参数可以是一个或多个值,而这个文件却必须是动态文件。要传递多个参数,则可以 在一个JSP 文件中使用多个将多个参数发送到一个动态文件中。
例如:
2. Case :forward
动作应用实例
现在举一个例子,说明的具体应用。
创建文件 forward1st.jsp ,其代码如清单 6.9 所示。 清单6.9 例程forward1st.jsp
6:
jsp: forward 应用举例-19:
<%out.println("这是forward 动作之前的一句话,在浏览器中能看到吗?")?%> 10:
·154· 器只是执行了对 forward1st.jsp 的请求, forward1st.jsp 到 forward2nd.jsp 的部分是在服务器内 执行的,浏览器并不知道服务器里到底发生了什么,它只知道自己获得的响应是 forward1st.jsp 发回来的,甚至不知道服务器还有个 forward2nd.jsp )
②例程 forward1st.jsp 中的第 9 行和第 12 行正常执行了吗? (没有看到 out 的输出结果! 但第 9、12行代码到底执行没有?第 7章 7.8 节还将深入讨论这个问题。 )
现在还有个新的问题: 如果在 forward1st.jsp 和 forward2nd.jsp 这两个页面之间需要传递 参数怎么办呢?可以使用标签,借助定义几个变量,然后在跳转的 新页面中使用 JSP 的内置对象 request 的 getParameter()方法读取这几个变量。关于 request 对象,将在第 7 章 7.3 节详细介绍。
使用 forward 动作,除了可以转向到普通的 URL (如:一个JSP 或 Servlet )外,它还有 一个特殊的功能:可以转向到WEB-INF 目录下的资源。例如,可以将一个JSP 文件(或者 其他资源)放到WEB-INF 目录下,则该文件无法通过浏览器直接访问。但是,通过 forward 动作,可以转向到 WEB-INF 中的这个 JSP 文件。这样,可以将一些需要保护的资源放在 WEB-INF 目录下,这些资源只有在JSP 中使用 forward 才能访问到。
6.5 JSP 和 Servlet 的关系
JSP 的发展基于Servlet 技术, 它简化了Servlet 的开发, 但JSP 本质上就是Servlet 。
Servlet 拙于页面显示,却善于业务逻辑操作和数据运算。在MVC (Model-View-Controller )开发模 式中,二者扮演着不同的重要角色。
6.5.1 Servlet/JSP 的发展和特点
1997 年 6 月,Sun 公司推出了 Servlet 1.0 规范;1998年 4 月又推出JSP 0.90 规范,年 底发布了 EJB 1.0 标准。从JSP 0.9 公开问世到最新的 JSP 2.1 规范,JSP 技术的发展可谓日 新月异。其中,最具里程碑的是 J2EE 1.4 正式发布以后,Servlet 从 2.3 更新到 2.4,JSP 从 1.2
更新到了2.0;针对J2SE 5.0,2005年 9 月,升级到 Servlet 2.5/JSP 2.1;2007 年 6 月, Sun 向 JCP (Java Community Process )提交了 Servlet 3.0 规范——JSR 315 (Java Specification Request )。
简言之,Sun 公司首先推出了 Servlet ,又推出了 JSP 。之后,将二者并行推进,齐头发 展。
6.5.2 JSP 和 Servlet 关系剖析
想一想 回顾第 2章 2.1 节中的“JSP 技术原理”部分,思考一下JSP 的工作原理。
1. JSP 本质上就是 Servlet
Sun 公司首先推出了 Servlet ,其功能非常强大,体系设计也很完善。但 Servlet 也不是 没有缺点, 和传统的 CGI 等方式相同, Servlet 是利用输出 HTML 语句来实现动态网页的 (看 看清单 6-2 吧)。特别是在 Microsoft 公司的 ASP 技术出现之后,Servlet 的这个特点显得非 常笨拙,对于复杂布局或者显示页面更是如此。如果用 Java Servlet 来开发整个网站,动态 部分和静态页面的整合过程简直就是一场噩梦。为此,Sun 在 Servlet 技术之上推出了 JSP (JavaServer Pages )。
JSP 正式发布于 1999 年 6 月,比 Servlet 的推出晚了两年还多。JSP 在执行时,也是先