本文基于 Java Web 程序设计任务教程(978-7-115-43936-9)进行编写,本文内容可能与课堂教学会存在偏差,请以课堂实际为准
第一章 网页开发基础
第二章 Java Web 概述
2.1 XML 基础
2.1.1 XML 概述
-
什么是 XML
XML 是 Extensible Markup Language 的缩写,它是一种类似于 HTML 的标记语言,称为可 扩展标记语言。所谓可扩展,指的是用户可以按照 XML 规则自定义标记。
-
XML 与 HTML 的比较
XML 和HTML 都是标记文本,它们在结构上大致相同,都是以标记的形式来描述信息。但 实际上它们有着本质的区别,为了让读者不产生混淆,接下来对 HTML 和 XML 进行比较,具体 如下。
- HTML 被设计出是用来显示数据的,XML 被设计出是为了传输和存储数据的。
- HTML 是不区分大小写的,而 XML 是严格区分大小写的。
- HTML可以有多个根元素,而格式良好的 XML 有且只能有一个根元素。
- HTML中,空格是自动过滤的,而 XML 中空格则不会自动删除。
- HTML中的标记是预定义的,而 XML 中的标记可以根据需要自己定义,并且可扩展。
总之,XML 不是 HTML 的升级,也不是 HTML 的替代产品,虽然两者有些相似,但它们的 应用领域和范围完全不同。在大多数 Web 应用程序中,XML 用于传输数据,而 HTML 用于格式 化并显示数据。HTML 规范的最新版本是 HTML5,但其常见版本是 HTML 4.01。此外,HTML 还有一个常见版本 — XHTML (EXtensible HyperText Markup Language,即可扩展超文本标 签语言),它是 XML 应用被重新定义的 HTML,是更严格更纯净的 HTML 版本。
2.1.2 XML 语法
-
文档声明
XML 文档第一行以 XML 声明开始,用来表述文档的一些信息,如:
<?xml version="1.0" encoding="UTF-8"?> -
元素定义
XML 元素指的是从(且包括)开始标签直到(且包括)结束标签的部分。
一个元素可以包含:
- 其他元素
- 文本
- 属性
- 或混合以上所有…
<bookstore><book category="CHILDREN"><title>Harry Potter</title><author>J K. Rowling</author><year>2005</year><price>29.99</price></book><book category="WEB"><title>Learning XML</title><author>Erik T. Ray</author><year>2003</year><price>39.95</price></book></bookstore>在上面的实例中,
和 都有 元素内容,因为他们包含其他元素。 元素也有属性(category=“CHILDREN”)。 、<author>、<year> 和 <price> 有文本内容,因为他们包含文本。 XML 命名规则
XML 元素必须遵循以下命名规则:
- 名称可以包含字母、数字以及其他的字符
- 名称不能以数字或者标点符号开始
- 名称不能以字母 xml(或者 XML、Xml 等等)开始
- 名称不能包含空格
- 可使用任何名称,没有保留的字词。
-
属性定义
XML元素具有属性,类似 HTML。
属性(Attribute)提供有关元素的额外信息。
XML 属性
在 HTML 中,属性提供有关元素的额外信息:
<img src="computer.gif"><a href="demo.html">属性通常提供不属于数据组成部分的信息。在下面的实例中,文件类型与数据无关,但是对需要处理这个元素的软件来说却很重要:
<file type="gif">computer.gif</file>XML 属性必须加引号
属性值必须被引号包围,不过单引号和双引号均可使用。比如一个人的性别,person 元素可以这样写:
<person sex="female">或者这样也可以:
<person sex='female'>如果属性值本身包含双引号,您可以使用单引号,就像这个实例:
<gangster name='George "Shotgun" Ziegler'>或者您可以使用字符实体:
<gangster name="George "Shotgun" Ziegler"> -
注释
在 XML 中编写注释的语法与 HTML 的语法很相似。
<!-- This is a comment -->
2.1.3 DTD 约束
-
什么是 DTD 约束
文档类型定义(DTD)可定义合法的 XML 文档构建模块。它使用一系列合法的元素来定义文档的结构。
DTD 可被成行地声明于 XML 文档中,也可作为一个外部引用。
-
DTD 的引入
内部的 DOCTYPE 声明
假如 DTD 被包含在您的 XML 源文件中,它应当通过下面的语法包装在一个 DOCTYPE 声明中:
<!DOCTYPE root-element [element-declarations]>带有 DTD 的 XML 文档实例:
<?xml version="1.0"?><!DOCTYPE note [<!ELEMENT note (to,from,heading,body)><!ELEMENT to (#PCDATA)><!ELEMENT from (#PCDATA)><!ELEMENT heading (#PCDATA)><!ELEMENT body (#PCDATA)>]><note><to>Tove</to><from>Jani</from><heading>Reminder</heading><body>Don't forget me this weekend</body></note>以上 DTD 解释如下:
- !DOCTYPE note (第二行)定义此文档是 note 类型的文档。
- !ELEMENT note (第三行)定义 note 元素有四个元素:“to、from、heading,、body”
- !ELEMENT to (第四行)定义 to 元素为 “#PCDATA” 类型
- !ELEMENT from (第五行)定义 from 元素为 “#PCDATA” 类型
- !ELEMENT heading (第六行)定义 heading 元素为 “#PCDATA” 类型
- !ELEMENT body (第七行)定义 body 元素为 “#PCDATA” 类型
外部文档声明
假如 DTD 位于 XML 源文件的外部,那么它应通过下面的语法被封装在一个 DOCTYPE 定义中:
<!DOCTYPE root-element SYSTEM "filename">这个 XML 文档和上面的 XML 文档相同,但是拥有一个外部的 DTD:
<?xml version="1.0"?><!DOCTYPE note SYSTEM "note.dtd"><note><to>Tove</to><from>Jani</from><heading>Reminder</heading><body>Don't forget me this weekend!</body></note>这是包含 DTD 的 “note.dtd” 文件:
<!ELEMENT note (to,from,heading,body)><!ELEMENT to (#PCDATA)><!ELEMENT from (#PCDATA)><!ELEMENT heading (#PCDATA)><!ELEMENT body (#PCDATA)> -
DTD 约束语法
-
元素定义
在一个 DTD 中,元素通过元素声明来进行声明。
声明一个元素
在 DTD 中,XML 元素通过元素声明来进行声明。元素声明使用下面的语法:
<!ELEMENT element-name category>或
<!ELEMENT element-name (element-content)>空元素
空元素通过类别关键词EMPTY进行声明:
<!ELEMENT element-name EMPTY>带有任何内容的元素
通过类别关键词 ANY 声明的元素,可包含任何可解析数据的组合:
<!ELEMENT element-name ANY>只有 PCDATA 的元素
只有 PCDATA 的元素通过圆括号中的 #PCDATA 进行声明:
<!ELEMENT element-name (#PCDATA)>带有子元素(序列)的元素
带有一个或多个子元素的元素通过圆括号中的子元素名进行声明:
<!ELEMENT element-name (child1)>或
<!ELEMENT element-name (child1,child2,...)>实例:
<!ELEMENT note (to,from,heading,body)>当子元素按照由逗号分隔开的序列进行声明时,这些子元素必须按照相同的顺序出现在文档中。在一个完整的声明中,子元素也必须被声明,同时子元素也可拥有子元素。“note” 元素的完整声明是:
<!ELEMENT note (to,from,heading,body)><!ELEMENT to (#PCDATA)><!ELEMENT from (#PCDATA)><!ELEMENT heading (#PCDATA)><!ELEMENT body (#PCDATA)>声明只出现一次的元素
<!ELEMENT element-name (child-name)>实例:
<!ELEMENT note (message)>上面的例子声明了:message 子元素必须出现一次,并且必须只在 “note” 元素中出现一次。
声明最少出现一次的元素
<!ELEMENT element-name (child-name+)>声明出现零次或多次的元素
<!ELEMENT element-name (child-name*)>声明出现零次或一次的元素
<!ELEMENT element-name (child-name?)>声明”非…/即…”类型的内容
实例:
<!ELEMENT note (to,from,header,(message|body))>上面的例子声明了:“note” 元素必须包含 “to” 元素、“from” 元素、“header” 元素,以及非 “message” 元素即 “body” 元素。
-
属性定义
在 DTD 中,属性通过 ATTLIST 声明来进行声明。
声明属性
属性声明使用下列语法:
<!ATTLIST element-name attribute-name attribute-type attribute-value>DTD 实例:
<!ATTLIST payment type CDATA "check">XML 实例:
<payment type="check" />以下是 属性类型的选项:
类型 描述 CDATA 值为字符数据 (character data) `(en1 en2 ID 值为唯一的 id IDREF 值为另外一个元素的 id IDREFS 值为其他 id 的列表 NMTOKEN 值为合法的 XML 名称 NMTOKENS 值为合法的 XML 名称的列表 ENTITY 值是一个实体 ENTITIES 值是一个实体列表 NOTATION 此值是符号的名称 xml: 值是一个预定义的 XML 值 默认属性值可使用下列值 :
值 解释 值 属性的默认值 #REQUIRED 属性值是必需的 #IMPLIED 属性不是必需的 #FIXED value 属性值是固定的 默认属性值
DTD:<!ELEMENT square EMPTY><!ATTLIST square width CDATA "0">合法的 XML:<square width="100" />在上面的例子中,“square” 被定义为带有 CDATA 类型的 “width” 属性的空元素。如果宽度没有被设定,其默认值为0 。
#REQUIRED
语法
<!ATTLIST element-name attribute-name attribute-type #REQUIRED>实例
DTD:<!ATTLIST person number CDATA #REQUIRED>合法的 XML:<person number="5677" />非法的 XML:<person />假如您没有默认值选项,但是仍然希望强制作者提交属性的话,请使用关键词 #REQUIRED。
#IMPLIED
语法
<!ATTLIST element-name attribute-name attribute-type #IMPLIED>实例
DTD:<!ATTLIST contact fax CDATA #IMPLIED>合法的 XML:<contact fax="555-667788" />合法的 XML:<contact />假如您不希望强制作者包含属性,并且您没有默认值选项的话,请使用关键词 #IMPLIED。
#FIXED
语法
<!ATTLIST element-name attribute-name attribute-type #FIXED "value">实例
DTD:<!ATTLIST sender company CDATA #FIXED "Microsoft">合法的 XML:<sender company="Microsoft" />非法的 XML:<sender company="W3Schools" />如果您希望属性拥有固定的值,并不允许作者改变这个值,请使用 #FIXED 关键词。如果作者使用了不同的值,XML 解析器会返回错误。
列举属性值
语法
<!ATTLIST element-name attribute-name (en1|en2|..) default-value>实例
DTD:<!ATTLIST payment type (check|cash) "cash">XML 例子:<payment type="check" />或<payment type="cash" />如果您希望属性值为一系列固定的合法值之一,请使用列举属性值。
-
2.1.4 Schema 约束
-
什么是 Schema 约束
同 DTD 一样,XML Schema 也是一种用于定义和描述 XML 文档结构与内容的模式语言,它 的出现克服了 DTD 的局限性。接下来,通过 XML Schema 与 DTD 的比较,将 XML Schema 所具有的一些显著优点进行列举,具体如下。
- DTD 采用的是非 XML 语法格式,缺乏对文档结构、元素、数据类型等全面的描述。而 XML Schema 采用的是 XML 语法格式,而且它本身也是一种 XML 文档,因此,XML Schema 语法格式比 DTD 更好理解。
- XML 有非常高的合法性要求,虽然 DTD 和 XML Schema 都用于对 XML 文档进行描述,都被用作验证 XML 合法性的基础。但是,DTD 本身合法性的验证必须采用另外一套机制,而XML Schema 则采用与 XML 文档相同的合法性验证机制。
- XML Schema 对名称空间支持得非常好,而 DTD 几乎不支持名称空间。
- DTD 支持的数据类型非常有限。例如,DTD 可以指定元素中必须包含字符文本(PCDATA),但无法指定元素中必须包含非负整数(nonNegativelnteger),而 XML Schema 比 XML DTD 支持更多的数据类型,包括用户自定义的数据类型。
- DTD 定义约束的能力非常有限,无法对 XML 实例文档作出更细致的语义限制,例如, 无法很好地指定一个元素中的某个子元素必须出现 7~12 次;而 XML Schema 定义约束的能力非常强大,可以对 XML 实例文档作出细致的语义限制。
-
名称空间
名称空间本质上是一个 URI(通常写成 URL 形式,但不一定是可访问的网站),用来标识某个 XML 或 XSD 中名称的“所属域”。
在 XML 和 XSD 中,由于不同的组织可能定义相同名字的元素,例如
name、id等,如果没有名称空间,会产生歧义。通过 namespace,你可以区分:
<a:name>是 A 组织定义的元素<b:name>是 B 组织定义的元素
常见的几个命名空间
① XSD 语言本身的命名空间
这是最重要的一个,用于引用 XML Schema 定义语言的内置类型与元素:
xmlns:xsd="<http://www.w3.org/2001/XMLSchema>"常用于定义 XSD 文档头:
<xsd:schema xmlns:xsd="<http://www.w3.org/2001/XMLSchema>">其中:
xsd是前缀(prefix),你可以用其他名字,如xshttp://www.w3.org/2001/XMLSchema是标准 Schema 定义的命名空间 URI(固定)
常用内置类型例如:
xsd:stringxsd:intxsd:date
② XML Schema 实例命名空间
用于在 XML 文档中使用 schemaLocation、type 等属性:
xmlns:xsi="<http://www.w3.org/2001/XMLSchema-instance>"常见用法:
xsi:schemaLocation="<http://example.com/person> person.xsd"
为什么 XML Schema 需要名称空间?
✔ 避免命名冲突
不同组织可以定义同名的元素和类型,但通过不同 namespace 隔离。
✔ 清晰地引用其他 Schema
例如,一个 schema 可以使用另一个 schema 中的类型:
<xs:import namespace="<http://example.com/address>" schemaLocation="address.xsd"/>✔ 支持模块化设计
可以把复杂的 schema 拆分为多个模块,例如基础类型模块、业务模块等。
如何定义和使用命名空间?
在 XSD 中定义自身的目标名称空间(targetNamespace)
<xsd:schemaxmlns:xsd="<http://www.w3.org/2001/XMLSchema>"targetNamespace="<http://example.com/person>"xmlns="<http://example.com/person>"elementFormDefault="qualified">解释:
targetNamespace:此 XSD 定义的元素/类型属于哪个命名空间xmlns="...":默认前缀elementFormDefault="qualified":XML 中必须显示使用 namespace(推荐)
在 XML 中引用 XSD 的名称空间
XML 文档实例通常这样写:
<person xmlns="<http://example.com/person>"xmlns:xsi="<http://www.w3.org/2001/XMLSchema-instance>"xsi:schemaLocation="<http://example.com/person> person.xsd"><name>Tom</name><age>20</age></person>自定义名称空间
<? xml version="1.0" encoding="UTF-8"?><it315:书架 xmlns:it315="http://www.it315.org/xmlbook/schema"><it315:书><it315:书名>JavaScript网页开发</it315:书名><it315:作者>张孝祥</it315:作者><it315:售价>28.00元</it315:售价></it315:书></it315:书架> -
引入 Schema 文档
在 XML 文档中引入(关联)XML Schema(XSD)通常是为了让 XML 文档能够被验证是否符合某个结构或类型定义。引入 Schema 的方式主要有两种:
- 使用
xsi:schemaLocation或xsi:noNamespaceSchemaLocation(最常用) - 通过命名空间与 Schema 的匹配关系进行解析器配置(程序级)
下面重点介绍 XML 文档内部常用的第一种方式。
使用
xsi:schemaLocation引入 Schema(有命名空间)这是最常见、最推荐的方法。
1. 前提:XML 文档的元素使用一个目标名称空间(targetNamespace)
假设你的 XSD:
<xsd:schema xmlns:xsd="<http://www.w3.org/2001/XMLSchema>"targetNamespace="<http://example.com/person>"xmlns="<http://example.com/person>"elementFormDefault="qualified"><xsd:element name="person" type="PersonType"/><xsd:complexType name="PersonType"><xsd:sequence><xsd:element name="name" type="xsd:string"/><xsd:element name="age" type="xsd:int"/></xsd:sequence></xsd:complexType></xsd:schema>2. XML 文档中这样引入 Schema
<person xmlns="<http://example.com/person>"xmlns:xsi="<http://www.w3.org/2001/XMLSchema-instance>"xsi:schemaLocation="<http://example.com/person> person.xsd"><name>Tom</name><age>20</age></person>解释:
-
xmlns="<http://example.com/person>"→ 文档根元素属于这个命名空间。 -
xmlns:xsi="<http://www.w3.org/2001/XMLSchema-instance>"→ 引入 xsi 前缀,用于 schema 相关属性(如 schemaLocation)。 -
xsi:schemaLocation的格式是:namespaceURI schema文件路径即:
命名空间与XSD文件路径成对出现。
例如:
xsi:schemaLocation="<http://example.com/person> person.xsd<http://example.com/address> address.xsd"
二、使用
xsi:noNamespaceSchemaLocation(无命名空间)如果 XSD 没有指定
targetNamespace(无命名空间),则使用这个属性。假设 XSD:
<xsd:schema xmlns:xsd="<http://www.w3.org/2001/XMLSchema>"><xsd:element name="note"><xsd:complexType><xsd:sequence><xsd:element name="to" type="xsd:string"/><xsd:element name="from" type="xsd:string"/></xsd:sequence></xsd:complexType></xsd:element></xsd:schema>XML:
<note xmlns:xsi="<http://www.w3.org/2001/XMLSchema-instance>"xsi:noNamespaceSchemaLocation="note.xsd"><to>Alice</to><from>Bob</from></note>解释:
- 使用 noNamespaceSchemaLocation
- 只需要提供 XSD 文件路径,无需命名空间 URI
- 使用
-
Schema 语法
-
元素定义
XML Schema 可定义 XML 文件的元素。
简易元素指那些只包含文本的元素。它不会包含任何其他的元素或属性。
什么是简易元素?
简易元素指那些仅包含文本的元素。它不会包含任何其他的元素或属性。
不过,“仅包含文本”这个限定却很容易造成误解。文本有很多类型。它可以是 XML Schema 定义中包括的类型中的一种(布尔、字符串、数据等等),或者它也可以是您自行定义的定制类型。
您也可向数据类型添加限定(即 facets),以此来限制它的内容,或者您可以要求数据匹配某种特定的模式。
定义简易元素
定义简易元素的语法:
<xs:element name="xxx" type="yyy"/>此处 xxx 指元素的名称,yyy 指元素的数据类型。XML Schema 拥有很多内建的数据类型。
最常用的类型是:
- xs
- xs
- xs
- xs
- xs
- xs
实例
这是一些 XML 元素:
<lastname>Refsnes</lastname><age>36</age><dateborn>1970-03-27</dateborn>这是相应的简易元素定义:
<xs:element name="lastname" type="xs:string"/><xs:element name="age" type="xs:integer"/><xs:element name="dateborn" type="xs:date"/>简易元素的默认值和固定值
简易元素可拥有指定的默认值或固定值。
当没有其他的值被规定时,默认值就会自动分配给元素。
在下面的例子中,默认值是 “red”:
<xs:element name="color" type="xs:string" default="red"/>固定值同样会自动分配给元素,并且您无法规定另外一个值。
在下面的例子中,固定值是 “red”:
<xs:element name="color" type="xs:string" fixed="red"/> - xs
-
属性的定义
所有的属性均作为简易类型来声明。
什么是属性?
简易元素无法拥有属性。假如某个元素拥有属性,它就会被当作某种复合类型。但是属性本身总是作为简易类型被声明的。
如何声明属性?
定义属性的语法是
<xs:attribute name="xxx" type="yyy"/>在此处,xxx 指属性名称,yyy 则规定属性的数据类型。XML Schema 拥有很多内建的数据类型。
最常用的类型是:
- xs
- xs
- xs
- xs
- xs
- xs
实例
这是带有属性的 XML 元素:
<lastname lang="EN">Smith</lastname>这是对应的属性定义:
<xs:attribute name="lang" type="xs:string"/>属性的默认值和固定值
属性可拥有指定的默认值或固定值。
当没有其他的值被规定时,默认值就会自动分配给元素。
在下面的例子中,默认值是 “EN”:
<xs:attribute name="lang" type="xs:string" default="EN"/>固定值同样会自动分配给元素,并且您无法规定另外的值。
在下面的例子中,固定值是 “EN”:
<xs:attribute name="lang" type="xs:string" fixed="EN"/>可选的和必需的属性
在默认的情况下,属性是可选的。如需规定属性为必选,请使用 “use” 属性:
<xs:attribute name="lang" type="xs:string" use="required"/>对内容的限定
当 XML 元素或属性拥有被定义的数据类型时,就会向元素或属性的内容添加限定。
假如 XML 元素的类型是 “xs
”,而其包含的内容是类似 “Hello World” 的字符串,元素将不会(通过)验证。 - xs
-
限定
限定(restriction)用于为 XML 元素或者属性定义可接受的值。对 XML 元素的限定被称为 facet。
对值的限定
下面的例子定义了带有一个限定且名为 “age” 的元素。age 的值不能低于 0 或者高于 120:
<xs:element name="age"><xs:simpleType><xs:restriction base="xs:integer"><xs:minInclusive value="0"/><xs:maxInclusive value="120"/></xs:restriction></xs:simpleType></xs:element>对一组值的限定
如需把 XML 元素的内容限制为一组可接受的值,我们要使用枚举约束(enumeration constraint)。
下面的例子定义了带有一个限定的名为 “car” 的元素。可接受的值只有:Audi, Golf, BMW:
<xs:element name="car"><xs:simpleType><xs:restriction base="xs:string"><xs:enumeration value="Audi"/><xs:enumeration value="Golf"/><xs:enumeration value="BMW"/></xs:restriction></xs:simpleType></xs:element>上面的例子也可以被写为:
<xs:element name="car" type="carType"/><xs:simpleType name="carType"><xs:restriction base="xs:string"><xs:enumeration value="Audi"/><xs:enumeration value="Golf"/><xs:enumeration value="BMW"/></xs:restriction></xs:simpleType>注意: 在这种情况下,类型 “carType” 可被其他元素使用,因为它不是 “car” 元素的组成部分。
对一系列值的限定
如需把 XML 元素的内容限制定义为一系列可使用的数字或字母,我们要使用模式约束(pattern constraint)。
下面的例子定义了带有一个限定的名为 “letter” 的元素。可接受的值只有小写字母 a - z 其中的一个:
<xs:element name="letter"><xs:simpleType><xs:restriction base="xs:string"><xs:pattern value="[a-z]"/></xs:restriction></xs:simpleType></xs:element>下一个例子也定义了带有一个限定的名为 “initials” 的元素。可接受的值是大写或小写字母 a - z 其中的三个:
<xs:element name="initials"><xs:simpleType><xs:restriction base="xs:string"><xs:pattern value="[a-zA-Z][a-zA-Z][a-zA-Z]"/></xs:restriction></xs:simpleType></xs:element>下一个例子定义了带有一个限定的名为 “choice 的元素。可接受的值是字母 x, y 或 z 中的一个:
<xs:element name="choice"><xs:simpleType><xs:restriction base="xs:string"><xs:pattern value="[xyz]"/></xs:restriction></xs:simpleType></xs:element>对一系列值的其他限定
下面的例子定义了带有一个限定的名为 “letter” 的元素。可接受的值是 a - z 中零个或多个字母:
<xs:element name="letter"><xs:simpleType><xs:restriction base="xs:string"><xs:pattern value="([a-z])*"/></xs:restriction></xs:simpleType></xs:element>下面的例子定义了带有一个限定的名为 “gender” 的元素。可接受的值是 male 或者 female:
<xs:element name="gender"><xs:simpleType><xs:restriction base="xs:string"><xs:pattern value="male|female"/></xs:restriction></xs:simpleType></xs:element>下面的例子定义了带有一个限定的名为 “password” 的元素。可接受的值是由 8 个字符组成的一行字符,这些字符必须是大写或小写字母 a - z 亦或数字 0 - 9:
<xs:element name="password"><xs:simpleType><xs:restriction base="xs:string"><xs:pattern value="[a-zA-Z0-9]{8}"/></xs:restriction></xs:simpleType></xs:element>对空白字符的限定
如需规定对空白字符(whitespace characters)的处理方式,我们需要使用 whiteSpace 限定。
这个例子定义了带有一个限定的名为 “address” 的元素。这个 whiteSpace 限定被设置为 “replace”,这意味着 XML 处理器将移除所有空白字符(换行、回车、空格以及制表符):
<xs:element name="address"><xs:simpleType><xs:restriction base="xs:string"><xs:whiteSpace value="replace"/></xs:restriction></xs:simpleType></xs:element>这个例子也定义了带有一个限定的名为 “address” 的元素。这个 whiteSpace 限定被设置为 “collapse”,这意味着 XML 处理器将移除所有空白字符(换行、回车、空格以及制表符会被替换为空格,开头和结尾的空格会被移除,而多个连续的空格会被缩减为一个单一的空格):
<xs:element name="address"><xs:simpleType><xs:restriction base="xs:string"><xs:whiteSpace value="collapse"/></xs:restriction></xs:simpleType></xs:element>对长度的限定
如需限制元素中值的长度,我们需要使用 length、maxLength 以及 minLength 限定。
本例定义了带有一个限定且名为 “password” 的元素。其值必须精确到 8 个字符:
<xs:element name="password"><xs:simpleType><xs:restriction base="xs:string"><xs:length value="8"/></xs:restriction></xs:simpleType></xs:element>这个例子也定义了带有一个限定的名为 “password” 的元素。其值最小为 5 个字符,最大为 8 个字符:
<xs:element name="password"><xs:simpleType><xs:restriction base="xs:string"><xs:minLength value="5"/><xs:maxLength value="8"/></xs:restriction></xs:simpleType></xs:element>数据类型的限定
限定 描述 enumeration 定义可接受值的一个列表 fractionDigits 定义所允许的最大的小数位数。必须大于等于0。 length 定义所允许的字符或者列表项目的精确数目。必须大于或等于0。 maxExclusive 定义数值的上限。所允许的值必须小于此值。 maxInclusive 定义数值的上限。所允许的值必须小于或等于此值。 maxLength 定义所允许的字符或者列表项目的最大数目。必须大于或等于0。 minExclusive 定义数值的下限。所允许的值必需大于此值。 minInclusive 定义数值的下限。所允许的值必需大于或等于此值。 minLength 定义所允许的字符或者列表项目的最小数目。必须大于或等于0。 pattern 定义可接受的字符的精确序列。 totalDigits 定义所允许的阿拉伯数字的精确位数。必须大于0。 whiteSpace 定义空白字符(换行、回车、空格以及制表符)的处理方式。 -
复杂类型
XML Schema(XSD)中的复杂类型(complexType)用于定义由多个子元素、属性或混合内容(文本 + 子元素)组成的数据结构。它是 XSD 中构建结构化 XML 的核心概念。
什么是复杂类型(complexType)?
在 XML Schema 中,元素分为两类:
- 简单类型 simpleType:只包含文本内容(字符串、数字、日期等)
- 复杂类型 complexType:可以包含 ✔ 子元素 ✔ 属性 ✔ 文本(可选) ✔ 元素与文本混合内容
复杂类型用于构建结构化的数据。例如:
<person><name>Tom</name><age>20</age></person>这类结构就需要使用
complexType。复杂类型的 4 种基本类型
XML Schema 中复杂类型的内容模型主要分为 四类:
- 仅包含元素(Element-only Content)
- 仅包含文本(Simple Content)
- 混合内容(Mixed Content)
- 空内容(Empty Content)
下面逐一介绍。
1. Element-only(sequence) 内容(只包含子元素)
特点:
- 复杂类型只由子元素组成
- 不允许直接出现文本
- 最常见的结构化类型
例:
XML:
<person><name>Tom</name><age>20</age></person>XSD 定义:
<xs:complexType name="PersonType"><xs:sequence><xs:element name="name" type="xs:string"/><xs:element name="age" type="xs:int"/></xs:sequence></xs:complexType>2. Simple Content(简单内容:文本 + 属性)
特点:
- 元素内容是文本(simpleType)
- 可以添加 属性
- 不允许出现子元素
用于扩展或限制一个简单类型,使其拥有属性。
XML 示例:
<price currency="USD">100</price>XSD:
<xs:complexType name="PriceType"><xs:simpleContent><xs:extension base="xs:decimal"><xs:attribute name="currency" type="xs:string"/></xs:extension></xs:simpleContent></xs:complexType>
3. Mixed Content(混合内容:文本 + 元素)
特点:
- 可以包含文本 + 子元素混合
- 常用于具有自然语言内容的结构(如 XHTML、文档内容)
XML 示例:
<para>This is a <em>very</em> important message.</para>XSD:
<xs:complexType mixed="true"><xs:sequence><xs:element name="em" type="xs:string" minOccurs="0" maxOccurs="unbounded"/></xs:sequence></xs:complexType>
4. Empty Content(空内容:只有属性,没有子元素和文本)
特点:
- 元素内不能有文本
- 不能有子元素
- 但可以有属性
- 常用于标记类元素(类似 HTML 的
<br />)
XML 示例:
<line break="true"/>XSD:
<xs:complexType name="LineType"><xs:attribute name="break" type="xs:boolean" use="required"/></xs:complexType>此类型的元素必须是空的:
<line break="true"/>不能写成:
<line break="true">xxx</line> <!-- 不允许 -->
-
2.2 程序开发体系结构
2.2.1 C/S 体系架构(客户端/服务器)
2.2.2 B/S 体系架构(浏览器/服务器)
2.3 Tomcat
2.3.1 Tomcat 简介
2.3.2 Tomcat 的安装和启动
Tomcat 的目录结构:
- bin: 用于存放 Tomcat 的可执行文件和脚本文件(扩展名为 bat 的文件),如 tomcat7.exe、 startup.bat.
- conf: 用于存放 Tomcat 的各种配置文件,如 web.xml、server.xml。
- lib: 用于存放 Tomcat 服务器和所有 Web 应用程序需要访问的 JAR 文件。
- logs: 用于存放 Tomcat 的日志文件。
- temp: 用于存放 Tomcat 运行时产生的临时文件。
- webapps: Web 应用程序的主要发布目录,通常将要发布的应用程序放到这个目录下
- work: Tomcat的工作目录,JSP 编译生成的 Servlet 源文件和字节码文件放到这个目录下。
2.3.3 Tomcat 诊断
第三章 HTTP 协议
3.1 HTTP 概述
3.1.1 HTTP 的概念
HTTP 协议的特点:
- 支持客户端(浏览器就是一种Web客户端)/服务器模式。
- 简单快速:客户端向服务器请求服务时,只需传送请求方式和路径。常用的请求方式有 GET、POST 等,每种方式规定了客户端与服务器联系的类型不同。由于HTTP简单,使得 HTTP 服务器的程序规模小,因而通信速度很快。
- 灵活:HTTP 允许传输任意类型的数据,正在传输的数据类型由 Content-Type 加以标记。
- 无状态:HTTP 是无状态协议。无状态是指协议对于事务处理没有记忆能力,如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。
3.1.2 HTTP 1.0 和 HTTP 1.1
- HTTP 1.0:客户端与服务器建立连接后,每次只能处理一个 HTTP 请求,导致客户端与服务器交互耗时,影响网页的访问速度
- HTTP 1.1:支持持久连接,在一个 TCP 连接上可以传送多个 HTTP 请求和响应
3.1.3 HTTP 消息
3.2 HTTP 请求消息
3.2.1 HTTP 请求行
<方法> <请求目标> <HTTP版本>示例:
GET /products?id=10 HTTP/1.1请求方法:
以下是常见的 HTTP 请求方法列表:
| 方法 | 描述 |
|---|---|
| GET | 从服务器获取资源。用于请求数据而不对数据进行更改。例如,从服务器获取网页、图片等。 |
| POST | 向服务器发送数据以创建新资源。常用于提交表单数据或上传文件。发送的数据包含在请求体中。 |
| PUT | 向服务器发送数据以更新现有资源。如果资源不存在,则创建新的资源。与 POST 不同,PUT 通常是幂等的,即多次执行相同的 PUT 请求不会产生不同的结果。 |
| DELETE | 从服务器删除指定的资源。请求中包含要删除的资源标识符。 |
| PATCH | 对资源进行部分修改。与 PUT 类似,但 PATCH 只更改部分数据而不是替换整个资源。 |
| HEAD | 类似于 GET,但服务器只返回响应的头部,不返回实际数据。用于检查资源的元数据(例如,检查资源是否存在,查看响应的头部信息)。 |
| OPTIONS | 返回服务器支持的 HTTP 方法。用于检查服务器支持哪些请求方法,通常用于跨域资源共享(CORS)的预检请求。 |
| TRACE | 回显服务器收到的请求,主要用于诊断。客户端可以查看请求在服务器中的处理路径。 |
| CONNECT | 建立一个到服务器的隧道,通常用于 HTTPS 连接。客户端可以通过该隧道发送加密的数据。 |
3.2.2 HTTP 请求头
HTTP 请求头主要用于:
- 描述客户端信息(如浏览器、操作系统)
- 描述请求内容(如数据格式、编码方式)
- 描述对响应的期望(如语言、压缩方式)
- 支持身份验证、缓存、跨域、连接管理等高级功能
HTTP 请求头的分类
HTTP 请求头一般可以分为 四类:
1. 通用头(General Headers)
客户端和服务器都可以使用,描述报文本身的性质,与具体资源无关。
常见的例子:
| Header | 说明 |
|---|---|
Cache-Control | 控制缓存策略,如 no-cache |
Connection | 控制连接选项,如 keep-alive |
Date | 报文创建时间 |
2. 请求头(Request Headers)
提供客户端信息、请求上下文、期望行为等。
常见例子:
| Header | 说明 |
|---|---|
| Host | 指定服务器域名(HTTP/1.1 必须) |
| User-Agent | 客户端信息(浏览器、系统) |
| Accept | 希望接受的 MIME 类型,如 text/html |
| Accept-Language | 希望的语言,如 zh-CN |
| Accept-Encoding | 接受的压缩格式,如 gzip |
| Referer | 从哪个页面跳转过来 |
| Authorization | 携带认证信息 |
| Range | 请求部分内容 |
| If-Modified-Since / If-None-Match | 条件请求、配合缓存 |
3. 实体头(Entity Headers)
描述请求体(若存在)的信息。
常见例子:
| Header | 说明 |
|---|---|
| Content-Type | 请求体的数据格式,如 application/json |
| Content-Length | 请求体长度 |
| Content-Encoding | 请求体的压缩方式 |
| Content-Language | 请求体语言 |
4. Cookie / CORS / 特殊用途头
Cookies
如果使用 cookie 机制:
Cookie: session_id=abc123; theme=light
CORS(跨域)头
主要用于浏览器跨域请求:
| Header | 用途 |
|---|---|
Origin | 请求来自哪个域 |
Access-Control-Request-Method | 预检请求的目标方法 |
Access-Control-Request-Headers | 预检请求携带哪些自定义头 |
一个完整示例
GET /api/user?id=1 HTTP/1.1Host: example.comUser-Agent: Mozilla/5.0Accept: application/jsonAccept-Language: zh-CN,zh;q=0.9Accept-Encoding: gzip, deflateConnection: keep-aliveReferer: <https://example.com/home>Authorization: Bearer xxxx.yyyy.zzzzCookie: session_id=abc1233.3 HTTP 响应信息
3.3.1 HTTP 响应状态行
<HTTP版本> <状态码> <状态描述>示例:
HTTP/1.1 200 OK下面是常见的 HTTP 状态码:
- 1xx(信息性状态码):表示接收的请求正在处理。
- 2xx(成功状态码):表示请求正常处理完毕。
- 3xx(重定向状态码):需要后续操作才能完成这一请求。
- 4xx(客户端错误状态码):表示请求包含语法错误或无法完成。
- 5xx(服务器错误状态码):服务器在处理请求的过程中发生了错误。
3.3.2 HTTP 响应头
HTTP 响应头的作用
服务器通过响应头向客户端提供:
- 返回内容的类型、长度、编码方式
- 缓存策略(是否可缓存、缓存时长)
- 连接控制(keep-alive 等)
- 跨域(CORS)授权信息
- Cookie 设置
- 重定向信息
- 服务器类型、性能优化信息等
HTTP 响应头的分类
HTTP 响应头可以分为 四大类:
1. 通用头(General Headers)
请求和响应都可使用,描述报文整体特性。
常见响应中的通用头:
| Header | 说明 |
|---|---|
| Cache-Control | 缓存控制,如 no-store、max-age=600 |
| Connection | 连接方式(如 keep-alive) |
| Date | 响应生成时间 |
2. 响应头(Response Headers)
服务器独有,用于描述服务器状态、位置、跨域等信息。
常见例子:
| Header | 说明 |
|---|---|
| Server | 服务器软件信息,如 nginx |
| Location | 重定向地址(配合 3xx 状态码) |
| Set-Cookie | 设置 Cookie |
| Access-Control-Allow-Origin | CORS:允许哪 origin 访问 |
| Access-Control-Allow-Methods | CORS:允许哪些方法 |
| WWW-Authenticate | 告诉客户端认证方式(401 响应) |
3. 实体头(Entity Headers)
描述响应体(body)的格式、大小等属性。
| Header | 说明 |
|---|---|
| Content-Type | 响应体的数据类型(如 text/html、application/json) |
| Content-Length | 响应体长度(字节) |
| Content-Encoding | 响应体压缩方式(如 gzip) |
| Content-Language | 内容语言(如 zh-CN) |
| Content-Disposition | 附件下载标记,如 attachment; filename="a.pdf" |
4. Cookie / CORS / 安全相关头
Cookie 设置
服务器用 Set-Cookie 设置 Cookie:
Set-Cookie: session_id=abc123; HttpOnly; Secure; Max-Age=3600安全相关头
提高网站安全性:
| Header | 作用 |
|---|---|
| Strict-Transport-Security | 强制 HTTPS(HSTS) |
| X-Frame-Options | 防点击劫持 |
| X-Content-Type-Options | 禁止 MIME 类型猜测 |
| Content-Security-Policy | CSP 内容安全策略 |
跨域(CORS)头
常见 CORS 响应头:
| Header | 说明 |
|---|---|
| Access-Control-Allow-Origin | 允许的域 |
| Access-Control-Allow-Headers | 允许带哪些请求头 |
| Access-Control-Allow-Credentials | 是否允许带 Cookie |
| Access-Control-Max-Age | 预检结果缓存时间 |
完整的 HTTP 响应示例
HTTP/1.1 200 OKDate: Tue, 08 Oct 2024 10:00:00 GMTServer: nginx/1.22Content-Type: application/json; charset=utf-8Content-Length: 54Cache-Control: max-age=600Set-Cookie: uid=12345; HttpOnly; SecureAccess-Control-Allow-Origin: <https://example.com>
{"id":1,"name":"Alice","email":"alice@example.com"}第四章 Servlet 技术
4.1 Servlet 基础
4.1.1 Servlet 概述
Servlet 是运行在 Web 服务器端的应用程序,使用 Java 语言编写。Servlet 对象主要封装了对 HTTP 请求的处理,并且它的运行需要 Servlet 容器(Tomcat)的支持。
Servlet 容器将 Servlet 动态加载到服务器上,与 HTTP 协议相关的 Servlet 使用 HTTP 请求和 HTTP 响应与客户端进行交互。

4.1.2 Servlet 的特点
- 功能强大:Servlet 采用 Java 语言编写,可以调用 JavaAPI 中的对象和方法,此外,Servlet 对象对 Web 应用进行了封装,提供了 Servlet 对 Web 应用的编程接口,还可以对 HTTP 请求进行相应的处理
- 可移植:Java 语言是跨平台的
- 性能高效:Servlet 对象在 Servlet 容器启动时被初始化,当 Servlet 对象第一次被请求时,Servlet 容器将 Servlet 实例化,此后 Servlet 对象常驻内存
- 安全性高:Servlet 使用了 Java 的安全框架,同时 Servlet 容器还可以为 Servlet 提供额外的安全功能,它的安全性是非常高的
- 可扩展:Java 语言是面向对象的编程语言,而 Servlet 由 Java 语言编写。在业务处理逻辑中,可以通过封装、继承等特性扩展实际的业务需要
4.1.3 Servlet 接口

Servlet 接口的抽象类:GenericServlet
Servlet 接口的实现类:HttpServlet 实际开发中的 Servlet 类都继承此类

4.2 Servlet 开发入门
4.2.1 实现 Servlet 程序
实例:
package com.example.servlet;
import java.io.*;
import jakarta.servlet.http.*;import jakarta.servlet.annotation.*;
// 将当前类声明为一个名为 helloServlet 的 Servlet,并映射到 URL 路径 /hello-servlet。@WebServlet(name = "helloServlet", value = "/hello-servlet")public class HelloServlet extends HttpServlet { private String message;
public void init() { message = "Hello World!"; }
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { response.setContentType("text/html");
// 从 HttpServletResponse 中取得一个 PrintWriter 对象,使用它向浏览器输出字符内容。 PrintWriter out = response.getWriter(); out.println("<html><body>"); out.println("<h1>" + message + "</h1>"); out.println("</body></html>"); }
public void destroy() { }}4.2.2 Servlet 的配置
-
使用 web.xml 配置 Servlet
基本结构
配置 Servlet 需要两个部分:
- 注册 Servlet
- 为 Servlet 设置 URL 映射
对应 web.xml 写法如下:
<servlet><servlet-name>helloServlet</servlet-name><servlet-class>com.example.HelloServlet</servlet-class></servlet><servlet-mapping><servlet-name>helloServlet</servlet-name><url-pattern>/hello-servlet</url-pattern></servlet-mapping>各部分含义
1.
<servlet>:注册 Servlet<servlet><servlet-name>helloServlet</servlet-name><servlet-class>com.example.HelloServlet</servlet-class></servlet>说明:
- servlet-name 只是一个名称标识,与类名无关,但必须与 mapping 中一致。
- servlet-class
Servlet 的完整类路径(需继承
HttpServlet)。
2.
<servlet-mapping>:URL 映射<servlet-mapping><servlet-name>helloServlet</servlet-name><url-pattern>/hello-servlet</url-pattern></servlet-mapping>url-pattern是用户访问路径,例如:
<http://localhost:8080/项目名/hello-servlet>会被映射到
HelloServlet。 -
使用 @WebServlet 注解配置 Servlet
@WebServlet是 Servlet 3.0 开始提供的注解,用来 代替 web.xml 中的<servlet>与<servlet-mapping>配置。 使用它可以让开发者 无需编写 web.xml 就能完成 Servlet 注册与 URL 映射,更加简洁直观。@WebServlet 的作用
将一个继承 HttpServlet 的 Java 类注册为 Servlet,并为它指定访问路径(URL pattern)。
例如:
@WebServlet("/hello-servlet")public class HelloServlet extends HttpServlet { ... }相当于在 web.xml 中写:
<servlet><servlet-name>helloServlet</servlet-name><servlet-class>com.example.HelloServlet</servlet-class></servlet><servlet-mapping><servlet-name>helloServlet</servlet-name><url-pattern>/hello-servlet</url-pattern></servlet-mapping>@WebServlet 的常用属性
@WebServlet注解中包含多个可配置项👇1. value / urlPatterns(必选其一)
指定 URL 映射路径,两者等价,任选其一。
@WebServlet("/hello")等价于:
@WebServlet(urlPatterns = "/hello")也可配置多个路径:
@WebServlet(urlPatterns = {"/hello", "/hi"})2. name
Servlet 在容器中的名称(可选),类似于 web.xml 中的
<servlet-name>。@WebServlet(name = "helloServlet", urlPatterns = "/hello")3. loadOnStartup
指定服务器启动时是否提前加载该 Servlet。
@WebServlet(urlPatterns = "/hello", loadOnStartup = 1)含义:
值 含义 负数或省略 第一次访问时才创建 Servlet 对象(默认) 0 或正数 服务器启动时立即创建 Servlet,值越小优先级越高 4. initParams(初始化参数)
用于为 Servlet 提供初始化数据,相当于 web.xml 的
<init-param>。@WebServlet(urlPatterns = "/config",initParams = {@WebInitParam(name = "username", value = "admin"),@WebInitParam(name = "password", value = "123456")})在 Servlet 中可以这样取:
String user = getInitParameter("username");5. asyncSupported
是否支持异步 Servlet (Servlet 3.0 新特性)。
@WebServlet(urlPatterns = "/async", asyncSupported = true)完整示例
package com.example;import jakarta.servlet.annotation.WebServlet;import jakarta.servlet.http.*;import java.io.IOException;@WebServlet(name = "helloServlet", urlPatterns = "/hello-servlet")public class HelloServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws IOException {response.setContentType("text/html;charset=UTF-8");response.getWriter().write("<h1>Hello from @WebServlet!</h1>");}}访问路径:
<http://localhost:8080/你的项目名/hello-servlet>
4.2.3 Servlet 的生命周期

- 初始化阶段: Servlet 容器解析 HTTP 请求,并以单例模式初始化/获取 Servlet 实例对象(init 在 Servlet 生命周期内只调用一次)
- 运行阶段: 对于每一次请求,Servlet 容器都会构建 ServletRequest 和 ServletReponse 对象传递给 Servlet 的 service() 方法
- 销毁阶段: Servlet 容器调用 Servlet 的 destroy() 方法
4.3 ServletConfig 和 ServletContext
4.3.1 ServletConfig 接口

实例:
// 通过 @WebInitParam 配置 ServletConfig@WebServlet(..., initParams = {@WebInitParam(name = "encoding", value = "UTF-8")})public class TestServlet02 extends HttpServlet { protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out = response.getWriter(); // 获得 ServletConfig对象 ServletConfig config= this.getServletConfig(); // 获得参数名为encoding对应的参数值 String param = config.getInitParameter("encoding"); out.println("encoding="+param); }}4.3.2 ServletContext 接口
当 Servlet 容器启动时,会为每个 Web 应用创建一个唯一的 ServletContext 对象代表当前 Web应用,该对象不仅封装了当前 Web 应用的所有信息,而且实现了多个 Servlet 之间数据的 共享。
-
获取 Web 应用程序的初始化参数
在 web.xml 中配置 Servlet 的初始化信息:
<context-param><param-name>参数名A</param-name><param-value>参数值A</param-value></context-param><context-param><param-name>参数名B</param-name><param-value>参数值B</param-value></context-param>使用 ServletContext 接口获取 web.xml 的配置信息
public class TestServlet03 extends HttpServlet {public void doGet (HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {...// 得到ServletContext 对象ServletContext context = this.getServletContext();// 得到包含所有初始化参数名的Enumeration对象Enumeration<String> paramNames = context.getInitParameterNames();out.println ("all the paramName and paramValue are following:");// 遍历所有的初始化参数名,得到相应的参数值并打印while (paramNames.hasMoreElements()) {String name = paramNames.nextElement();// 通过参数名从 context 中获取 web.xml 中的初始化参数String value = context.getInitParameter(name);out.println(name + ": " + value);out.println("<br />");}} -
实现多个 Servlet 对象共享数据

实例:
@WebServlet(name = "TestServlet05", urlPatterns = "/TestServlet05")public class TestServlet05 extends HttpServlet {public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {ServletContext context = this.getServletContext();// this.getServletContext()方法获取本应用的ServletContext对象// 用户不能用new创建新的ServletContext对象// 本例中,ServletContext对象是作为域对象(即公用存储空间)使用// 通过setAttribute()方法设置属性值,相当于存储。context.setAttribute("data", "123456");}}@WebServlet(name = "TestServlet06", urlPatterns = "/TestServlet06")public class TestServlet06 extends HttpServlet {public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {PrintWriter out = response.getWriter();ServletContext context = this.getServletContext();// 通过getAttribute()方法获取在TestServlet05通过setAttribute()存入的属性值// getAttribute()方法获得的数据类型是Object型,可根据需要进行强制转换类型String data = (String) context.getAttribute("data");out.println(data);}} -
获取 Web 应用下的资源文件

ServletContext提供了多种方式来“获得资源路径(resource path)”,关键在于它操作的是 Web 应用的根目录(WebApp Root) 下的资源,而不是磁盘上的绝对路径。下面从机制与常用 API 两方面介绍。ServletContext 获得资源路径的机制
ServletContext代表整个 Web 应用的运行环境,它通过 Servlet 容器(如 Tomcat、Jetty) 提供的上下文来定位资源。核心思想:
- 所有路径都 相对于 Web 应用根目录(即
webapp/,部署后通常是/)。 - Servlet 容器会把这些路径映射到:
- 文件系统路径(exploded deployment)比如 Tomcat 下展开的目录
- 打包在 WAR 中的资源(非展开部署),通过类加载器读取
主要 API 及其工作方式
-
getRealPath(String path)把 Web 应用内的相对路径 转换为 服务器文件系统上的实际路径。
String realPath = servletContext.getRealPath("/WEB-INF/web.xml");工作原理:
-
若容器是以“展开(exploded)”方式部署 Web 应用:
-
它返回真实的磁盘路径,例如:
/usr/local/tomcat/webapps/myapp/WEB-INF/web.xml
-
-
若应用以 未展开的 WAR 方式运行(如部分 Jetty 或特殊配置):
- 可能返回
null(资源被压缩在 WAR 里,没对应文件系统路径)
- 可能返回
-
-
getResource(String path)返回表示 Web 应用根路径下资源的 URL。
URL url = servletContext.getResource("/images/a.webp");工作原理:
- 容器根据路径找到资源,并返回:
file:前缀(若资源位于文件系统)jar:前缀(若资源位于 WAR 包)
- 不依赖于真实文件路径,因此比
getRealPath()更可靠
- 容器根据路径找到资源,并返回:
-
getResourceAsStream(String path)以 输入流 形式读取资源,是实际开发中最常用的方式。
InputStream in = servletContext.getResourceAsStream("/WEB-INF/config.properties");工作原理:
- 容器根据路径找到资源,返回 InputStream
- 不管资源是否在文件系统、JAR、WAR,都能读取
- 不返回路径,只返回流,更通用
-
getResourcePaths(String path)列举某路径下所有资源(目录或文件)。
Set<String> paths = servletContext.getResourcePaths("/WEB-INF/");工作原理:
- 容器扫描 Web 根路径(包含 WAR 内部)
- 返回该目录下所有资源的路径集合
示例:最常用的读取方式
config.properties Company = itcastAddress = BeijingInputStream in = servletContext.getResourceAsStream("/WEB-INF/classes/config.properties");Properties props = new Properties();props.load(in);out.println("Company=" + pros.getProperty("Company") + "<br />");out.println("Address=" + pros.getProperty("Address") + "<br />");为什么推荐?
- 不依赖真实磁盘路径
- WAR/JAR 内资源都能读
- 在所有 Servlet 容器中都稳定工作
- 所有路径都 相对于 Web 应用根目录(即
4.4 HttpServletResponse 对象
4.4.1 发送状态码相关的方法
setStatus(int status)只设置状态码,不改变响应内容,不会触发错误页面sendError(int sc)设置状态码 并触发错误处理流程,通常会跳转到 Web 容器的错误页面sendError(int code, String message)
4.4.2 发送响应消息头相关的方法

4.4.3 发送响应消息体相关的方法
getOutputStream()getWriter()
4.5 HttpServletResponse 应用
4.5.1 实现请求重定向
response.sendRedirect(String url);4.5.2 动手实践:解决中文输出乱码问题
HttpServletResponse 默认使用 ISO-8859-1 码表,因此不能解码中文,解决方案:
// 设置 HttpServletReponse 编码方式response.setCharacterEncoding("utf-8");// 通知浏览器网页编码方式response.setHeader("Content-Type", "text/html;charset=utf-8");或使用
response.setContentType("text/html;charset=UTF-8");4.6 HttpServletRequest对象
4.6.1 获取请求行信息的相关方法

4.6.2 获取请求头的相关方法

4.6.3 请求转发
/*请求转发forward()方法,可带request和response作为参数转到另一个Servlet重定向response.sendRedirect()没有这个功能RequestDispatcher:请求转发接口getRequestDispatcher()参数表示转发的目标地点,应是一个web资源:包括Servlet(URL模式)和JSP重定向可以定向到外部URL,转发不能转到外部URL,所以以下写法错误:RequestDispatcher dispatcher = request.getRequestDispatcher("www.baidu.com");*/RequestDispatcher dispatcher = getRequestDispatcher(String Path);dispatcher.forward(request, response);
// 或使用匿名对象getRequestDispatcher(String Path).forward(request, response);实例:
// 将一组键值型数据存储到 request 域对象中request.setAttribute("username", "张三");//获取请求转发对象,参数是发送的目的地,与 action 或重定向参数相似request.getRequestDispatcher("ResultServlet").forward(request, response);4.6.4 获取请求参数

4.6.5 通过 Request 对象传递数据
public void setAttribute(String name, Object o);public Object getAttribute(String name);public void removeAttribute(String name);public java.util.Enumeration getAttributeNames();
4.6.6 动手实践:解决请求参数中的中文乱码问题
request.setCharacterEncoding("utf-8");第五章 会话及会话技术
5.1 会话概述
5.2 Cookie 对象
5.2.1 什么是 Cookie
5.2.2 Cookie API
-
构造方法
public Cookie(String name, String value) -
常用方法

其中,
setPath(String uri)和getPath()方法 上面的这两个方法是针对 Cookie 的 Path 属性的。如果创建的某个 Cookie 对象没有设置 Path 属性,那么该 Cookie 只对当前访问路径所属的目录及其子目录有效。如果想让某个 Cookie 项对站点的所有目录下的访问路径都有效,应调用 Cookie 对象的setPath()方法将其 Path 属性设置为“/”。setDomain(String pattern)和getDomain()方法 上面的这两个方法是针对 Cookie 的 domain 属性的。domain 属性是用来指定浏览器访问的 域。例如,雪萌天文台的域为snowy.moe。那么,当设置 domain 属性时,其值必须以“.”开头,如domain = .snowy.moe。默认情况下,domain属性的值为当前主机名,浏览器在访问当前主机下的资源时,都会将 Cookie 信息回送给服务器。需要注意的是,domain 属性的值是不区分大小写的。
实例:
@WebServlet(name = "LastAccessServlet",urlPatterns = "/LastAccessServlet")public class LastAccessServlet extends HttpServlet { private static final long serialVersionUID = 1L; private String encodeDate(String dateStr) throws IOException { System.out.println("编码前:"+dateStr); dateStr=URLEncoder.encode(dateStr, "utf-8"); System.out.println("编码后:"+dateStr);
return dateStr; }
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,IOException { //指定客户端输出内容的编码方式UTF-8,防止发生乱码 response.setContentType("text/html;charset=utf-8"); //获取客户端所有cookie,每个cookie数据是一个键值型数据 Cookie[] cookies=request.getCookies(); //定义flag的boolean变量,用于判断cookies是否为空 boolean flag=false; //遍历cookie数组 if(cookies!=null){ for(Cookie cookie:cookies) { //获取cookie的名称 String name=cookie.getName();
//判断名称是否有lastTime自定义的cookie if(!"lastTime".equals(name)){ continue; } flag=true;
//获取cookie的value String value=cookie.getValue(); value = encodeDate(value); response.getWriter().write("欢迎回来,您上次访问时间为:"+value);
//设置cookie的value //获取当前时间的字符串,重新设置cookie的值,重新发送cookie Date date=new Date(); SimpleDateFormat timesdf=new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss"); String str_time=timesdf.format(date); str_time = encodeDate(str_time); cookie.setValue(str_time);
//设置cookie存活时间 cookie.setMaxAge(60*60*24*30); //一个月
//加入当前客户端中的cookie response.addCookie(cookie);
break;
} } //如果cookies中没有时间,也就是没有访问过 else if(cookies.length==0 || flag==false){ //设置cookie的value //获取当前时间的字符串,重新设置cookie的值,重新发送cookie Date date=new Date(); SimpleDateFormat sdf=new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss"); String str_date=sdf.format(date); str_date = encodeDate(str_date);
//如果是第一次访问网页,就创建lastTime这个cookie Cookie cookie=new Cookie("lastTime",str_date);
//设置cookie存活时间 cookie.setMaxAge(60*60*24*30);//一个月
//存入客户端cookies response.addCookie(cookie);
response.getWriter().write("您好,欢迎您首次访问"); } } public void doPost(HttpServletRequest request,HttpServletResponse response)throws ServletException,IOException{ this.doPost(request,response); }}5.3 Session 对象
5.3.1 什么是 Session
5.3.2 HttpSession API
获取与当前请求相关的 HttpSession 对象: public HttpSession getSession(boolean create = True)
| 方法声明 | 功能描述 |
|---|---|
String getId() | 用于返回与当前 HttpSession 对象关联的会话标识号 |
long getCreationTime() | 返回 Session 创建的时间,这个时间是创建 Session 的时间与 1970 年 1 月 1 日 00:00:00 之间时间差的毫秒表示形式 |
long getLastAccessedTime() | 返回客户端最后一次发送与 Session 相关请求的时间,这个时间是发送请求的时间与 1970 年 1 月 1 日 00:00:00 之间时间差的毫秒表示形式 |
void setMaxInactiveInterval(int interval) | 用于设置当前 HttpSession 对象可空闲的以秒为单位的最长时间,也就是修改当前会话的默认超时间隔 |
boolean isNew() | 判断当前 HttpSession 对象是否是新创建的 |
void invalidate() | 用于强制使 Session 对象无效 |
ServletContext getServletContext() | 用于返回当前 HttpSession 对象所属的 Web 应用程序对象,即代表当前 Web 应用程序的 ServletContext 对象 |
void setAttribute(String name, Object value) | 用于将一个对象与一个名称关联后存储到当前的 HttpSession 对象中 |
String getAttribute(String name) | 用于从当前 HttpSession 对象中返回指定名称的属性对象 |
void removeAttribute(String name) | 用于从当前 HttpSession 对象中删除指定名称的属性 |
5.3.3 Session 的生命周期
- Session 生效:在用户第一次范围 JSP、 Servlet 资源时创建,或调用
request.getSession(true)强制生成 - Session 失效:用户请求间隔超过限制,或调用
session.invalidate() - 设置失效时间:
-
web.xml
<session-config><session-timeout>30</session-timeout></session-config> -
手动设置
session.setMaxInactiveInterval(30*60) // -1 为永不过期
-
第六章 JSP 技术
6.1 JSP 概述
6.1.1 什么是 JSP
JSP 全名是 Java Server Pages,它是建立在 Servlet 规范之上的动态网页开发技术。在 JSP 文件中,HTML 代码与 Java 代码共同存在,其中,HTML 代码用来实现网页中静态内容的显示,Java 代码用来实现网页中动态内容的显示。为了与传统 HTML 有所区别,JSP 文件的扩展名为 .jsp 。
JSP 技术所开发的 Web 应用程序是基于 Java 的,它可以用一种简捷而快速的方法从 Java 程序生成Web 页面,其使用上具有如下几点特征。
- 跨平台:由于 JSP 是基于 Java 语言的,它可以使用 Java API,所以它也是跨平台的。
- 业务代码相分离:在使用 JSP 技术开发 Web 应用时,可以将界面的开发与应用程序的开发分离开。开发人员使用 HTML 来设计界面,使用 JSP 标签和脚本来动态生成页面上的内容。在服务器端,JSP 引擎(或容器,本书中指 Tomcat)负责解析 JSP 标签和脚本程序,生成所请求的内容,并将执行结果以 HTML 页面的形式返回到浏览器。
- 组件重用:JSP 中可以使用 JavaBean 编写业务组件,也就是使用一个 JavaBean 类封装业务处理代码或者作为一个数据存储模型,在JSP页面中,甚至在整个项目中,都可以重复使用这个JavaBean。
- 预编译:预编译就是在用户第 1 次通过浏览器访问 JSP 页面时,服务器将对 JSP 页面代码进行编译,并且仅执行一次编译。编译好的代码将被保存,在用户下一次访问时,会直接执行编译好的代码。
6.1.2 编写 JSP 文件
<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head> <title>JSP Scriptlets</title></head>
<body><%! //JSP声明:声明变量和方法int a = 1, b = 2; //定义两个变量a,b:相当于全局变量,在整个web应用有效%>
<% //JSP代码段:嵌入Java代码,语法完全一致 int c = 2, d = 3; /*定义两个变量c,d: 相当于局部变量,仅在本文件内有效。*/ out.println(a+b);%><!--这是HTML注释-->
<%--这是JSP注释--%>
<br /><%--JSP表达式,功能类似于out.print();--%><%= c+d %><br />
</body>
</html>6.1.3 JSP 运行原理

6.2 JSP 基本语法
6.2.1 JSP 页面的基本构成
6.2. JSP 脚本元素
JSP 脚本元素是指嵌套在 <% 和 %> 之中的一条或多条 Java 程序代码。通过 JSP 脚本元素可以将 Java 代码嵌入 HTML 页面中,所有可执行的 Java 代码,都可以通过 JSP 脚本来执行。
-
JSP Scriptlets
<% java 代码(变量、方法、表达式等)%>实例:
<%/*定义两个变量c,d:相当于局部变量,仅在本文件内有效。*/int c = 2, d = 3;out.println(a+b);%> -
JSP 声明语句 JSP Scriptlets可以定义属性,但不能定义方法。而 JSP 的声明语句可用于声明变量和方法,作用域在整个 JSP 页面中。它以 “<%!“ 开始,以 “%>” 结束,其语法格式如下所示。
<%! 定义变量/方法的语句 %>实例:
<%!public int print() { //定义print方法int a = 1, b = 2; //定义两个变量a,breturn a+b;}%> -
JSP表达式 JSP表达式(expression) 用于将程序数据输出到客户端,它将要输出的变量或者表达式直接封装在以 “<%=” 开头和以“ %>” 结尾的标记中,其基本的语法格式如下所示。
<%= expression %>实例:
<%= c+d %>
6.2.3 JSP 注释
<!-- 这个是HTML注释:浏览器源码可以看到 --><%-- 这个是JSP注释:浏览器源码不可以看到 --%><%//与Java一样的注释// 需要放在代码段中,否则变成输出内容
//(1)单行注释
/*(2)多行注释*/
/** (3)提示文档 * 说明内容*/
%>
// 动态注释:在浏览器源码中可以看到动态计算的注释内容<!--<%=new Date()%>-->6.3 JSP 指令
6.3.1 page 指令
在 JSP 页面中,经常需要对页面的某些特性进行描述,例如,页面的编码方式、JSP 页面采用的语言等,这时,可以通过 page 指令来实现。page 指令的具体语法格式如下所示。
<%@ page 属性名1="属性值1"属性名2="属性值2" ... %>
在上面的语法格式中,page 用于声明指令名称,属性用来指定 JSP 页面的某些特性。page 指令提供了一系列与 JSP 页面相关的属性

示例:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%><%@ page import="java.awt.* " %><%@ page import="java.util.* ","java.awt.* "%>6.3.2 include 指令
在实际开发时,有时需要在 JSP 页面静态包含一个文件,例如 HTML 文件、文本文件等,这时,可以通过 include 指令来实现。include 指令的具体语法格式如下所示。
<%@ include file="被包含的文件地址"%>
include 指令只有一个 file 属性,该属性用来指定插入到 JSP 页面目标位置的文件资源。需要注意的是,插入文件的路径一般不以“/”开头,而是使用相对路径。
示例:
<%@ page contentType="text/html; charset=UTF-8" import="java.util.Date"%><%@ page import="java.text.SimpleDateFormat" %><html><head><title>date</title></head><body>当前时间是:<% Date date = new Date(); //日期类,显示当前日期和时间 SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String today =df.format(date); out.println(today);%></body></html><%@ page language="java" contentType="text/html; charset=UTF-8"%><html><head> <title>欢迎你</title></head><body> <%@ include file="date.jsp"%></body></html>关于 include 指令的具体应用,有很多问题需要注意,接下来,将这些问题进行列举,具体如下:
- 被引入的文件必须遵循 JSP 语法,其中的内容可以包含静态 HTML、JSP 脚本元素和 JSP 指令等普通 JSP 页面所具有的一切内容。
- 除了指令元素之外,被引入的文件中的其他元素都被转换成相应的 Java 源代码,然后插入当前JSP 页面所翻译成的 Servlet 源文件中,插入位置与 include 指令在当前 JSP 页面中的位置保持一致。
- file 属性的设置值必须使用相对路径,如果以“/”开头,表示相对于当前 Web 应用程序的根目录(注意不是站点根目录),否则,表示相对于当前文件。需要注意的是,这里的 file 属性指定的相对路径是相对于文件(file),而不是相对于页面(page)。
- 为了使整个页面的层次结构不产生冲突,建议在被包含页面中将 等标签,因为在包含页面的文件中已经指定了这些标签
6.3.3 taglib 指令
taglib 指令用于引入自定义标签库(Tag Library),使 JSP 页面能够使用 JSTL 或自定义标签,从而减少脚本表达式、提升可读性与可维护性。
基本语法
<%@ taglib uri="URI_of_tag_library" prefix="yourPrefix" %>属性说明:
| 属性 | 说明 |
|---|---|
| uri | 标签库的唯一标识(可以是真实 URL 或逻辑 URI)。 |
| prefix | 在 JSP 中使用标签时的前缀。 |
典型示例(JSTL)
引入 JSTL Core 标签库:
<%@ taglib uri="<http://java.sun.com/jsp/jstl/core>" prefix="c" %>随后就可以使用:
<c:if test="${user != null}"> 欢迎你!</c:if>使用自定义标签库
如果你自己定义了一个 TLD 文件,例如 mytags.tld 放在 WEB-INF/tlds/ 下:
<%@ taglib uri="/WEB-INF/tlds/mytags.tld" prefix="m" %>然后就能使用自定义标签:
<m:hello name="沐沐" />uri 的两种形式
(1)标准标签库使用 URL 形式
如 JSTL、Spring 标签库:
uri="<http://www.springframework.org/tags>"(2)本地 TLD 使用相对路径或逻辑 URI
- 逻辑 URI(web.xml 中
<taglib>注册) - 真实路径(相对 WEB-INF 的 TLD 文件)
例:
uri="/WEB-INF/tlds/mytags.tld"6.4 JSP 动作元素
JSP 动作元素用来控制 JSP 的行为,执行一些常用的 JSP 页面动作。通过动作元素可以实现使用多行Java 代码能够实现的效果,如包含页面文件、实现请求转发等。
6.4.1 包含文件元素 jsp:include
在 JSP 页面中,为了把其他资源的输出内容插入到当前 JSP 页面的输出内容中,JSP 技术提供了 jsp
<jsp:include page="relativeURL" flush="true|false" />
- page 属性用于指定被引入资源的相对路径
- flush 属性用于指定是否将当前页面的输出内容刷新到客户端,默认情况下,flush 属性的值为false(fluse = False → 子页面完成编译渲染后才同时输出全部页面内容)
jsp
| 特性 | <%@ include %> 静态包含 | <jsp:include> 动态包含 |
|---|---|---|
| 执行时机 | 编译时 | 运行时 |
| 包含方式 | 直接把文件内容合并 | 独立执行后插入结果 |
| 性能 | 较好(无运行时开销) | 略低(每次请求都包含) |
| 是否支持参数 | ❌ 不支持 | ✔ 支持 |
| 修改是否立即生效 | ❌ 需要重新编译主 JSP | ✔ 立即生效 |
| 适合内容 | 静态内容 | 动态内容 |
除此之外,应用 include 指令时由于两个文件最终会合成一个文件,所以两个页面不能有重复变量名&方法,否则会造成命名冲突,jsp:include 无此限制
6.4.2 请求转发元素 jsp:forward
jsp
<jsp:forward page="relativeURL" />
在上述语法格式中,page 属性用于指定请求转发到的资源的相对路径,该路径是相对于当前 JSP 页面的 URL。
请求转发的过程对用户/浏览器是无感的,URL 不会变化。
6.5 JSP 隐式对象
6.5.1 隐式对象的概述
在 JSP 页面中,有一些对象需要频繁使用,如果每次都重新创建这些对象则会非常麻烦。为了简化 Web 应用程序的开发,JSP2.0 规范中提供了 9 个隐式(内置)对象,它们是 JSP 默认创建的,可以直接在JSP页面中使用。

6.5.2 out 对象
out 对象并不等同于 PrintWriter,执行过程中会将内容先写入缓冲区,等到页面渲染结束后才会写入缓冲区的部分,因此 out.println() 语句执行的优先级低于 response.getWriter().println()

如果要限制缓冲区大小为 0 ,可以在 page 指令中传入配置参数:
<%@ page language="java" contentType="text/html; charset=UTF-8" buffer="0kb" %>6.5.3 pageContext 对象
在 JSP 页面中,使用 pageContext 对象可以获取 JSP 的其他 8 个隐式对象。pageContext 对象是 javax.servlet.jsp.PageContext 类的实例对象,它代表当前 JSP 页面的运行环境,并提供了一系列用于获取其他隐式对象的方法。

pageContext 对象不仅提供了获取隐式对象的方法,还提供了存储数据的功能。pageContext 对象存储数据是通过操作属性来实现的

其中,参数 scope 指定的是属性的作用范围。pageContext 对象的作用范围有 4 个值,具体如下:
- pageContext.PAGE_SCOPE:表示页面范围
- pageContext.REQUEST_SCOPE:表示请求范围
- pageContext.SESSION_SCOPE:表示会话范围
- pageContext.APPLICATION_SCOPE:表示Web应用程序范围
6.5.4 exception 对象
exception 对象只有在错误处理页面( <%@ page isErrorPage="true" %> )才可使用
示例:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isErrorPage="true"%><!DOCTYPE html><html><head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>error page</title></head><body> <!-- 显示异常信息 --> <%=exception.getMessage()%><br /></body></html>第七章 EL 和 JSTL
7.1 EL(Expression Language, 表达式语言)
7.1.1 EL 的语法格式
${Expression}
示例:
<%@ page language="java" contentType="text/html; charset=utf-8" %><html><head></head><body> 用户名:<%=request.getAttribute("username")%><br/> 密 码:<%=request.getAttribute("password")%><br/> <hr/>
使用EL:<br/> 用户名:${username}<br/> 密 码:${password}<br/></body></html>特点:
- EL 可以与 JavaScript 语句结合使用
- EL 可以自动进行类型转换
- EL 不仅可以访问一般变量,还可以访问 JavaBean 中的属性、嵌套属性和集合对象
- 在 EL 中可以执行算术运算、逻辑运算、关系运算和条件运算等
- 在 EL 中可以获取 pageContext 对象进而获取其他内置对象
- 在使用 EL 进行除法运算时如果除数为 0,返回 +Infinity 而不是抛出错误
- 在 EL 中可以访问 JSP 的作用域(request, session, application, page)
7.1.2 EL 中的标识符
标识符定义规范:
- 不能以数字开头。
- 不能是EL中的保留字,如 and, or, gt。
- 不能是EL隐式对象,如 pageContext。
- 不能包含单引号(‘)、双引号(“)、减号(-)和正斜杠(/)等特殊字符。
7.1.3 EL 中的关键字
- and
- or
- not
- eq(等于)
- ne(不等于)
- lt(小于)
- gt(大于)
- le(小于等于)
- ge(大于等于)
- true
- false
- null
- instanceof
- empty(用于判断一个变量是否为空)
- div(除)
- mod(取余)
7.1.4 EL 中的变量
7.1.5 EL 中的常量
7.1.6 EL 访问数据
-
点运算符(.)
${customer.name} -
中括号运算符([])
EL 表达式中的方括号运算符与点运算符的功能相同,都用于访问 JSP 页面中某些对象的属性。当获取的属性名中包含一些特殊符号,如“-”或“?”等并非字母或数字的符号,就只能使用方括号运算符来访问该属性,其语法格式如下。
${user["My-Name"]}
7.1.7 EL 中的运算符
-
算术运算符
如 + - * / 。但 EL 中的 + 不能实现字符串之间的连接,它会将两个字符串变量自动转换成数值型,如果失败将抛出异常
-
条件运算符
${A?B:C}
7.2 EL 隐式对象

7.2.1 pageContext 对象
7.2.2 Web 域相关对象
示例:
<%@ page language="java" contentType="text/html; charset=utf-8" %><html><head></head><body> <% pageContext.setAttribute("userName", "itcast"); %> <% request.setAttribute("bookName", "Java Web"); %> <% session.setAttribute("userName", "itheima"); %> <% application.setAttribute("bookName", "Java 基础"); %>
表达式\${pageScope.userName}的值为:${pageScope.userName} <br/> 表达式\${requestScope.bookName}的值为:${requestScope.bookName} <br/> 表达式\${sessionScope.userName}的值为:${sessionScope.userName} <br/> 表达式\${applicationScope.bookName}的值为:${applicationScope.bookName} <br/> 表达式\${userName}的值为:${userName}</body></html>7.2.3 访问环境信息的隐式对象
示例:
<%@ page language="java" contentType="text/html; charset=utf-8"%><html><head></head><body style="text-align: center;"> <form action="${pageContext.request.contextPath}/param.jsp"> // 文件自身 num1:<input type="text" name="num1"><br /> num2:<input type="text" name="num"><br /> num3:<input type="text" name="num"><br /> <br /> <input type="submit" value="提交" /> <input type="submit" value="重置" /><hr /> </form> num1:${param.num1}<br /> // 不存在时返回空字符串而不是 null num2:${paramValues.num[0]}<br /> num3:${paramValues.num[1]}<br /></body></html>7.2.4 Cookie 对象
用法:
获取cookie对象的信息: ${cookie.userName} // javax.servlet.http.Cookie@1234abc获取cookie对象的名称: ${cookie.userName.name}获取cookie对象的值: ${cookie.userName.value}7.3 JSTL
7.3.1 什么是 JSTL
JSTL 是一个不断完善的开放源代码的 JSP 标签库,从 JSP 1.1 规范开始 JSP 就支持使用自定义标签。
JSTL 包含 5 类标准标签库,其引用方法如下:
<%@ taglib prefix="prefix" url="jstl_url" %>

- Core 是一个核心标签库,它包含了实现 Web 应用中通用操作的标签。例如,用于输出文本内容的 <c
> 标签、用于条件判断的 <c > 标签、用于迭代循环的 <c > 标签。 - I18N 是一个国际化/格式化标签库,它包含实现 Web 应用程序的国际化标签和格式化标签。例如,设置 JSP 页面的本地信息、设置 JSP 页面的时区、使日期按照本地格式显示等。
- SQL 是一个数据库标签库,它包含了用于访问数据库和对数据库中的数据进行操作的标签。例如,从数据库中获得数据库连接、从数据库表中检索数据等。由于在软件分层开发模型中,JSP页面仅作为表示层,一般不会在 JSP 页面中直接操作数据库,因此,JSTL 中提供的这套标签库不经常使用。
- XML 是一个操作 XML 文档的标签库,它包含对 XML 文档中的数据进行操作的标签。例如,解析XML 文件、输出 XML 文档中的内容,以及迭代处理 XML 文档中的元素。XML 广泛应用于 Web 开发,使用 XML 标签库处理 XML 文档更加简单方便。
- Functions 是一个函数标签库,它提供了一套自定义 EL 函数,包含了 JSP 网页制作者经常要用到的字符串操作。例如,提取字符串中的子字符串、获取字符串的长度等。
7.3.2 JSTL 的下载和使用
7.4 JSTL 中的 Core 标签库
7.4.1 表达式标签
-
<c
> 标签 用于将一段文本内容或表达式结果输出到客户端
语法:
<c:out value="value" [default="defaultValue"] [escapeXml="{true|false}"]/>或使用标签体指定默认值
<c:out value="value" [escapeXml="{true|false}"]>defaultValue</c:out>其中,
- value 属性用于指定输出的文本内容。
- default 属性用于指定当 value 属性为 null 时所输出的默认值,该属性是可选的(方括号中的属性都是可选的)。
- escapeXml 属性用于指定是否将>、<、&、、“等特殊字符进行 HTML 编码转换后再进行输出,默认值为 true。
-
<c
> 标签 语法
<c:remove var="变量名" scope="作用域" />属性说明
属性 必须 说明 var是 要移除的变量名 scope否 指定作用域: page、request、session、application示例
从所有作用域中删除变量
<c:remove var="username" />效果:在所有作用域中找到名为
username的属性并移除。
7.4.2 流程控制标签
-
<c
> 标签 语法:
<c:if test="testCondition" var="result" [scope="{page|request|session|application}"] />或使用标签体指定输出内容:
<c:if test="testCondition" var="result" [scope="{page|request|session|application}"] />body content</c:if>其中,
- test 属性用于设置逻辑表达式。
- var 属性用于指定逻辑表达式中变量的名字。
- scope 属性用于指定 var 变量的作用范围,默认值为page。如果属性 test 的计算结果为 true 那么标签体脚本将被执行,否则标签体不会被执行。
示例
<c:set value="1" var="visitCount" property="visitCount" /><c:if test="${visitCount==1 }">This is you first visit. Welcome to the site!</c:if>
第八章 JavaBean 技术与 JSP 开发模型
8.1 JavaBean 技术
8.1.1 JavaBean 概述
JavaBean 是 Java 开发语言中一个可以重复使用的软件组件,它本质上就是一个 Java 类。为了规范JavaBean 的开发,Sun 公司发布了 JavaBean 的规范,它要求一个标准的 JavaBean 组件需要遵循一定的编码规范,具体如下。
- 它必须具有一个公共的、无参的构造方法,这个方法可以是编译器自动产生的默认构造方法。
- 它提供公共的 setter 方法和 getter 方法,让外部程序设置和获取 JavaBean 的属性。
8.1.2 JavaBean 种类
- 可视化的 JavaBean:实现一些可视化页面
- 非可视化的 JavaBean:封装业务对象
package cn.itcast;import java.io.Serializable;
public class User implements Serializable { private static final long seralVersionUID = 1L; private String username; private String password; public User() { } public User(String username, String password) { this.username = username; this.password = password; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; }}<%@ page import="cn.itcast.User"%> // 导入 src/User.java 中的 User 类<%@ page contentType="text/html;charset=UTF-8" language="java"%>
<!-- 文件8-3 --><html><head> <title>结果提示</title></head><body><div align="center"> <% //接收index.jsp发送来的数据 String username = request.getParameter("username"); String password = request.getParameter("password"); User user = new User(username,password); //实例化JavaBean
//验证用户名和密码 if (user.getUsername().equals("lisi")&&user.getPassword().equals("123")){ out.print("恭喜您,登录成功!"+user.getUsername()); } else{ out.print("请输入正确的用户名和密码!"); } %> <br/><br/> <a href="index.jsp">返回</a></div></body></html>8.1.3 JavaBean 的应用
获取 JavaBean 属性信息 & 赋值
package cn.itcast;public class Student { private String name = "张三"; private int age = 20; private String sex = "男";
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public String getSex() { return sex; }
public void setSex(String sex) { this.sex = sex; }}<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html><head> <title>学生信息</title></head><body>
<!-- 初始化 JavaBean 对象: id 可以看作类变量名 --><jsp:useBean id="student" class="cn.itcast.Student"></jsp:useBean>
<!-- 设置 JavaBean 对象属性: name 可以看作类变量名 --><jsp:setProperty name="student" property="name" value="小明"></jsp:setProperty><jsp:setProperty name="student" property="age" value="18"></jsp:setProperty><jsp:setProperty name="student" property="sex" value="男"></jsp:setProperty>
<div> <ul> <li> <!-- 获得 JavaBean 对象属性: name 可以看作类变量名 --> 姓名:<jsp:getProperty name="student" property="name"></jsp:getProperty> </li> <li> 年龄:<jsp:getProperty name="student" property="age"></jsp:getProperty> </li> <li> 性别:<jsp:getProperty name="student" property="sex"></jsp:getProperty> </li> </ul></div></body></html>自动接收表单参数
<%request.setCharacterEncoding("UTF-8");%><jsp:useBean id="book" class="cn.itcast.Book" scope="page"> <!--【property="*"】表示用接收的数据一次性填入JavaBean的属性--> <jsp:setProperty name="book" property="*"></jsp:setProperty></jsp:useBean>8.2 动手实践:使用 JavaBean 解决中文乱码
package cn.itcast;import java.io.UnsupportedEncodingException;
//Javabean:实现字符编码转换功能public class CharactorEncoding { public CharactorEncoding() { } /** * 本项目用表单传递中文参数,可能出现中文乱码 * 对出现中文乱码的字符进行转码处理 * @param str 要转码的字符串 * @return text 编码后的字符串 */ public String toString(String str) { String text= ""; if (str!=null && !"".equals(str)){ try{ //字符转码处理常用方法:按字节把ISO-8859-1编码转为UTF-8编码 //调用String的一个构造方法,用str调用String的getBytes方法进行转换。 text = new String(str.getBytes("ISO-8859-1"),"UTF-8"); }catch (UnsupportedEncodingException e){ } } return text; }}<jsp:useBean id="email" class="cn.itcast.Email"></jsp:useBean><jsp:useBean id="encoding" class="cn.itcast.CharactorEncoding"></jsp:useBean>
<!--【property="*"】表示用接收表单发来的参数数据,一次性接收所有参数, 并填入相应的JavaBean的属性,要求变量名一定要相对应(大小写也对应),否则出错。 --><jsp:setProperty name="email" property="*"></jsp:setProperty><div align="center"> <div id="container"> <div id="title"> <%=encoding.toString(email.getTitle())%> </div> <hr> <div id="content"> <%=encoding.toString(email.getContent())%> </div> </div></div>8.3 JSP 开发模型
-
JSP Model1
JSP Model1 采用 JSP+JavaBean 的技术,将页面显示和业务逻辑分开。其中,JSP 实现流程控制和页面显示,JavaBean 对象封装数据和业务逻辑。

-
JSP Model2
JSP Model2 架构模型采用 JSP+Servlet+JavaBean 的技术,此技术将原本 JSP 页面中的流程控制代码提取出来,封装到 Servlet 中,从而实现了整个程序页面显示、流程控制和业务逻辑的分离。实际上,JSP Model2 模型就是 MVC(模型 Model - 视图 View - 控制器 Controller)设计模式。其中,控制器的角色是由 Servlet 实现的,视图的角色是由 JSP 页面实现的,模型的角色是由JavaBean 实现的

8.4 MVC 设计模式
- 模型 模型(Model)负责管理应用程序的业务数据、定义访问控制以及修改这些数据的业务规则。当模型的状态发生改变时,它会通知视图发生改变,并为视图提供查询模型状态的方法。
- 视图 视图(View)负责与用户进行交互,它从模型中获取数据向用户展示,同时也能将用户请求传递给控制器进行处理。当模型的状态发生改变时,视图会对用户界面进行同步更新,从而保持与模型数据的一致性。
- 控制器 控制器(Controller)是负责应用程序中处理用户交互的部分,它负责从视图中读取数据,控制用户输入,并向模型发送数据。

第九章 Servlet 的高级特性
9.1 Filter
9.1.1 什么是 Filter

9.1.2 Filter 相关 API
-
Filter 接口

-
FilterConfig 接口

-
FilterChain 接口
FilterChain 接口中的 doFilter() 方法用于调用 Filter 链中的下一个过滤器/客户端
9.1.3 Filter 的生命周期
9.1.4 实现 Filter
package cn.itcast.chapter09.filter;
//WebFilter注解,与WebServlet注解作用和格式相似@WebFilter(filterName = "MyFilter", urlPatterns = "/MyServlet") //拦截MyServlet的请求//@WebFilter(filterName = "MyFilter", urlPatterns = "/*") //拦截用户所有请求
public class MyFilter implements Filter { public void init(FilterConfig fConfig) throws ServletException { // 过滤器对象在初始化时调用,可以配置一些初始化参数 }
// doFilter是过滤器的核心方法 // 用于拦截用户的请求,如果和当前过滤器的拦截路径匹配,该方法会被调用 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain //过滤器链 ) throws IOException, ServletException { PrintWriter out=response.getWriter(); out.println("Hello MyFilter");
chain.doFilter(request, response); // 如果没有这句,拦截以后就不会再执行Servlet } public void destroy() { // 过滤器对象在销毁时自动调用,释放资源 }}其中关于 @WebFilter 注解,有以下参数:
| 参数 | 类型 | 说明 |
|---|---|---|
filterName | String | 过滤器名称,通常用作标识。若不指定,默认是类名。 |
urlPatterns / value | String[] | 指定过滤器拦截的 URL 模式,如 "/login"、"*.jsp"。value 是 urlPatterns 的别名,二者等效。 |
servletNames | String[] | 指定要拦截的 Servlet 名称,而不是 URL。可选,如果同时指定 urlPatterns,二者都是有效的拦截条件。 |
dispatcherTypes | DispatcherType[] | 指定拦截器类型,常用类型包括: - REQUEST:普通请求 - FORWARD:请求转发 - INCLUDE:包含请求 - ERROR:错误处理 - ASYNC:异步请求 |
initParams | WebInitParam[] | 初始化参数,提供给过滤器使用。可在 Filter 的 init(FilterConfig config) 方法中获取。示例:config.getInitParameter("param1") |
9.1.5 Filter 映射
9.1.6 Filter 链

在一个 Web 应用程序中可以注册多个 Filter 程序,每个 Filter 程序都可以针对某一个 URL 进行拦截。如果多个 Filter 程序都对同一个 URL 进行拦截,那么这些 Filter 就会组成一个 Filter 链(也叫过滤器链)。Filter 链用 FilterChain 对象来表示,FilterChain 对象中有一个 doFilter() 方法,该方法的作用就是让 Filter 链上的当前过滤器放行,使请求进入下一个 Filter。
9.2 Listener
9.2.1 Listener 概述
在程序开发中,经常需要对某些事件进行监听,如监听鼠标单击事件、监听键盘按下事件等,此时就需要使用监听器,监听器在监听的过程中会涉及几个重要组成部分,具体如下。
- 事件(Event):用户的一个操作,如单击一个按钮、调用一个方法、创建一个对象等。
- 事件源:产生事件的对象。
- 事件监听器(Listener):负责监听发生在事件源上的事件。
- 事件处理器:监听器的成员方法,当事件发生的时候会触发对应的处理器(成员方法)。
当用户进行一个操作触发事件源上的事件时,就会被事件监听器监听到。当监听器监听到事件发生时,相应的事件处理器就会对发生的事件进行处理。
事件监听器在进行工作时,可分为几个步骤,具体如下。
- 将监听器绑定到事件源,也就是注册监听器。
- 事件发生时会触发监听器的成员方法,即事件处理器,传递事件对象。
- 事件处理器通过事件对象获得事件源,并对事件源进行处理。
9.2.2 Listener 的 API

9.3 Servlet 3.0 新特性
1. 注解(Annotation)支持
Servlet 3.0 开始,你不再必须在 web.xml 中声明所有 Servlet、Filter、Listener。这极大减少了 XML 配置量。
三个主要注解
@WebServlet
用于直接把一个类声明为 Servlet:
@WebServlet( name = "MyServlet", urlPatterns = {"/test", "/hello"})public class MyServlet extends HttpServlet {}@WebFilter
声明过滤器:
@WebFilter("/test")public class MyFilter implements Filter {}@WebListener
声明监听器:
@WebListenerpublic class MyListener implements ServletContextListener {}注解带来的益处
| 以前(Servlet 2.5) | Servlet 3.0 起 |
|---|---|
全部必须写在 web.xml | 直接用注解自动注册 |
| 配置复杂、容易出错 | 更直观、类与配置统一 |
| 扩展困难 | 更易于模块化开发,例如引入第三方 jar |
📝 框架(如 Spring Boot)正是利用这一机制自动加载组件。
2. 异步处理(Async Processing)支持
Servlet 3.0 引入 异步非阻塞请求处理模型,将每一个请求转交给一个异步线程进行业务处理,也可以让一个请求在 Servlet 线程释放后继续执行后台任务,提升服务器吞吐量(尤其适合长轮询、聊天系统、I/O 密集任务)。
开启异步支持
在 Servlet/Filter 上开启:
@WebServlet(urlPatterns = "/async", asyncSupported = true)public class AsyncServlet extends HttpServlet { ... }基本使用流程
在处理请求时启动异步
public void doGet(HttpServletRequest req, HttpServletResponse resp) { AsyncContext asyncContext = req.startAsync();
asyncContext.start(() -> { try { Thread.sleep(2000); // 模拟耗时操作 resp.getWriter().write("异步处理完成"); } catch (Exception e) { e.printStackTrace(); } asyncContext.complete(); // 完成 });}异步请求生命周期
- 客户端发起请求
- Servlet 从容器线程池中获取线程开始处理
- 调用
request.startAsync()⬇️ 容器释放当前线程给线程池 - 开启异步任务(例如新线程、线程池等)
- 异步任务执行完成后调用
asyncContext.complete() - 容器重新向客户端返回响应
异步处理带来的好处
| 同步模型 | 异步模型 |
|---|---|
| Servlet 线程被长期占用 | Servlet 线程迅速释放 |
| 连接长时间阻塞 | 可扩大并发能力 |
| 不适合长轮询/推送 | 很适合 Web 推送、消息处理 |
异步监听器(AsyncListener)
可以监听以下事件:
onComplete()onTimeout()onError()onStartAsync()
示例:
asyncContext.addListener(new AsyncListener() { public void onComplete(AsyncEvent event) { } public void onTimeout(AsyncEvent event) { } public void onError(AsyncEvent event) { } public void onStartAsync(AsyncEvent event) { }});9.4 文件的上传和下载
9.4.1 文件上传原理
Web 文件上传控件
<% -- 指定表单数据的enctype属性以及提交方式 -- %><form enctype="multipart/form-data" method="post"> <% -- 指定标记的类型和文件域的名称 -- %> 选择上传文件 :<input type="file" name="myfile"/><br /></form>当浏览器通过表单提交上传文件,由于文件数据都附带在 HTTP 请求消息体中,并且采用 MIME 类型(多用途互联网邮件扩展类型)进行描述,在后台使用 request 对象提供的 getlnputStream() 方法可以读取到客户端提交过来的数据。但由于用户可能会同时上传多个文件,而在 Servlet 端直接读取上传数据,并分别解析出相应的文件数据是一项非常麻烦的工作。为了方便处理用户上传数据,Apache 组织提供了一个开源组件 Commons-FileUpload。该组件可以方便地将 “multipart/form-data” 类型请求中的各种表单域解析出来,并实现一个或多个文件的上传,同时也可以限制上传文件的大小等内容。其性能十分优异,使用极其简单。

9.4.2 认识 Commons-FileUpload 组件
-
FileItem 接口
Fileltem 接口在 Commons-FileUpload 组件中被实现,其主要用于封装单个表单字段元素的数据,一个表单字段元素对应一个 Fileltem 对象。Commons-FileUpload 组件在处理文件上传的过程中,将每一个表单域(包括普通的文本表单域和文件域)封装在一个 Fileltem 对象中。
为了便于讲解,在此将 Fileltem 接口的实现类称为 Fileltem 类,Fileltem 类实现了 Serializable 接口,因此,支持序列化操作。在 Fileltem 类中定义了许多获取表单字段元素的方法,具体如下
boolean isFormField()方法isFormField()方法用于判断 Fileltem 类对象封装的数据是一个普通文本表单字段,还是一个文件表单字段,如果是普通文本表单字段,则返回 true,否则返回 false。String getName()方法getName()方法用于获得文件上传字段中的文件名。如果 Fileltem 类对象对应的是普通文本表单字段,getName() 方法将返回 null; 否则,只要浏览器将文件的字段信息传递给服务器, getName()方法就会返回一个字符串类型的结果,如: “Sunset.jpg”。String getFieldName()方法getFieldName()方法用于获得表单字段元素描述头的 name 属性值,也是表单标签 name 属性的值。例如 “name=file1” 中的“file1”。void write(File file)方法write()方法用于将 Fileltem 对象中保存的主体内容保存到某个指定的文件中。如果 Fileltem 对象中的主体内容是保存在某个临时文件中,那么该方法顺利完成后,临时文件有可能会被清除。另外,该方法也可将普通表单字段内容写入到一个文件中,但它主要用于将上传的文件内容保存 到本地文件系统中。
-
DiskFileItemFactory 类
DiskFileltemFactory 类用于将请求消息实体中的每一个文件封装成单独的 Fileltem 对象。如果上传的文件比较小,将直接保存在内存中,如果上传的文件比较大,则会以临时文件的形式,保存在磁盘的临时文件夹中。默认情况下,文件保存在内存还是硬盘临时文件夹的临界值是 10240,即10KB

-
ServletFileUpload 类
ServletFileUpload 类是 Apache 组件处理文件上传的核心高级类,通过使用
parseRequest(HttpServletRequest)方法可以将 HTML 中每个表单提交的数据封装成一个 Fileltem 对象,然后以 List 列表的形式返回。
9.4.3 Commons-FileUpload 组件下载
9.4.4 动手实践:实现文件上传
9.4.5 文件下载原理
设置响应头:
//设定接收程序处理数据的方式Content-Disposition: attachment;//设定实体内容的MIME类型Content-Type: application/x-msdownload9.4.6 动手实践:实现文件下载
第十章 JDBC
10.1 什么是 JDBC
JDBC 的全称是 Java 数据库连接(Java Database Connectivity),它是一套用于执行 SQL 语句的 Java API。应用程序可通过这套 API 连接到关系型数据库,并使用 SQL 语句来完成对数据库中数据的查询、更新、新增和删除的操作。

10.2 JDBC 的常用 API
10.2.1 Driver 接口
10.2.2 DriverManager 类
DriverManager 类用于加载 JDBC 驱动并且创建与数据库的连接。

10.2.3 Connection 接口
Connection 接口代表 Java 程序和数据库的连接,只有获得该连接对象后才能访问数据库,并操作数据表。

10.2.4 Statement 接口
Statement 接口用于执行静态的 SQL 语句,并返回一个结果对象,该接口的对象通过 Connection 实例的 createStatement() 方法获得。利用该对象把静态的 SQL 语句发送到数据库编译执行,然后返回数据库的处理结果。

10.2.5 PreparedStatement 接口
Statement 接口封装了 JDBC 执行 SQL 语句的方法,可以完成 Java 程序执行 SQL 语句的操作。然而,在实际开发过程中往往需要将程序中的变量作为 SQL 语句的查询条件,而使用 Statement 接口操作这些 SQL 语句会过于繁琐,并且存在安全方面的问题。针对这一问题,JDBC API 提供了扩展的PreparedStatement 接口。 PreparedStatement 是 Statement 的子接口,用于执行预编译的 SQL 语句。该接口扩展了带有参数SQL语句的执行操作,应用该接口中的 SQL 语句可以使用占位符“?”来代替其参数

示例:
String sql = "INSERT INTO users (id, name, email) VALUES (?, ?, ?) ";PreparedStatement preStmt = conn.prepareStatement(sql) ;preStmt.setInt(1,1); // 使用参数与 SQL 类型相匹配的方法preStmt.setString(2,"zhangsan"); // 使用参数与 SQL 类型相匹配的方法preStmt.setObject(3,"zs@sina.com"); // 使用 setObject() 方法设置参数preStmt.executeUpdate();10.2.6 ResultSet 接口
ResultSet 接口用于保存 JDBC 执行查询时返回的结果集,该结果集封装在一个逻辑表格中。在 ResultSet 接口内部有一个指向表格数据行的游标(或指针),ResultSet 对象初始化时,游标在表格的第 1 行之前,调用 next() 方法可将游标移动到下一行。如果下一行没有数据,则返回 false。在应用程序中经常使用 next() 方法作为 WHILE 循环的条件来迭代 ResultSet 结果集。
| 方法名称 | 功能描述 |
|---|---|
getString(int columnIndex) | 用于获取指定字段的 String 类型的值,参数 columnIndex 代表字段的索引 |
getString(String columnName) | 用于获取指定字段的 String 类型的值,参数 columnName 代表字段的名称 |
getInt(int columnIndex) | 用于获取指定字段的 int 类型的值,参数 columnIndex 代表字段的索引 |
getInt(String columnName) | 用于获取指定字段的 int 类型的值,参数 columnName 代表字段的名称 |
getDate(int columnIndex) | 用于获取指定字段的 Date 类型的值,参数 columnIndex 代表字段的索引 |
getDate(String columnName) | 用于获取指定字段的 Date 类型的值,参数 columnName 代表字段的名称 |
next() | 将游标从当前位置向下移一行 |
absolute(int row) | 将游标移动到此 ResultSet 对象的指定行 |
afterLast() | 将游标移动到此 ResultSet 对象的末尾,即最后一行之后 |
beforeFirst() | 将游标移动到此 ResultSet 对象的开头,即第 1 行之前 |
previous() | 将游标移动到此 ResultSet 对象的上一行 |
last() | 将游标移动到此 ResultSet 对象的最后一行 |
10.3 实现 JDBC 程序
-
加载并注册数据库驱动
DriverManager.registerDriver(Driver driver); -
通过 DriverManager 获取数据库连接
Connection conn = DriverManager.getConnection(String url, String user, String pwd);// url eg: jdbc:mysql://hostname:port/databasename?serverTimezone=GMT%2B8 -
通过 Connection 对象获取 Statement 对象
Connection 创建 Statement的方式有如下3种。
- createStatement():创建基本的Statement对象。
- prepareStatement():创建PreparedStatement 对象。
- prepareCall():创建CallableStatement对象。
Statement stmt = conn.createStatement(); -
使用 Statement 对象执行 SQL 语句
所有的 Statement 都有如下 3 种执行 SQL 语句的方法。
- execute(): 可以执行任何SQL语句。
- executeQuery(): 通常执行查询语句,执行后返回代表结果集的 ResultSet 对
- executeUpdate(): 主要用于执行 DML 和 DDL 语句。执行 DML 语句,如 INSERT、 UPDATE 或 DELETE 时,返回受 SQL 语句影响的行数,执行 DDL 语句返回 0。
-
操作 ResultSet 结果集
-
关闭连接,释放资源
示例:
public class Example01 { public static void main(String[] args) throws SQLException { //声明几个JDBC的对象变量 Statement stmt = null; //语句对象 ResultSet rs = null; //结果集对象 Connection conn = null; //连接对象 try { // 1. 注册数据库的驱动程序 Class.forName("com.mysql.cj.jdbc.Driver"); //MySQL8.0的驱动 //Class.forName("com.mysql.jdbc.Driver");MySQL5.5的驱动 //如果使用mysql8.0,注意应配置相应的驱动mysql-connector-java-8.0.15.jar
// 2.通过DriverManager获取数据库连接 // 数据库连接字 String url = "jdbc:mysql://localhost:3306/jdbc?serverTimezone=GMT%2B8"; // localhost也可以写ip: 127.0.0.1;3306是MySQL的常用端口号 // 此处jdbc表示要打开的数据库名,根据实际设置 // serverTimezone=GMT%2B8表示设置服务器时间为东八区时间,即北京时间; // serverTimezone=UTC表示全球标准时间 // mysql8.0必须设置,否则数据返回的时间会出错;5.5则无需设置
String username = "root"; // 数据库用户名 String password = "root"; // 数据库用户密码
// 获取数据库连接,如果出错会抛出异常进行处理 conn = DriverManager.getConnection(url, username,password); // DriverManager.getConnection的参数格式一:三参数
// DriverManager.getConnection的参数格式二:单参数 // conn=DriverManager.getConnection(url+"?user=root&password=root&characterEncoding=UTF-8"); // 上面的连接方式可以解决数据库连接中文乱码问题
// 3.通过Connection对象获取Statement对象(需对数据库操作的SQL语句对象) stmt = conn.createStatement();
// 4.使用Statement执行SQL语句。 String sql = "select * from users";
// 执行SQL得到查询结果,放入结果集对象rs rs = stmt.executeQuery(sql);
// 5. 处理和输出结果集 System.out.println("id | name | password | email | birthday"); // while循环,运行到 rs 没有下一行为止 while (rs.next()) { //使用 getString 方法前需要先rs.next() int id = rs.getInt("id"); // 通过列名获取指定字段的值 String name = rs.getString(2); // 通过列序号获取指定字段的值 String psw = rs.getString(3); String email = rs.getString("email"); Date birthday = rs.getDate("birthday"); System.out.println(id + " | " + name + " | " + psw + " | " + email + " | " + birthday); //输出一条记录的数据,然后再循环到下一条 } } catch (ClassNotFoundException e) { e.printStackTrace(); } finally{ // 6.回收数据库资源,按顺序关闭各项资源 if(rs!=null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } rs = null; } if(stmt!=null) { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } stmt = null; } if(conn!=null) { try { conn.close(); //一般写这句就够 } catch (SQLException e) { e.printStackTrace(); } conn = null; } } }}10.4 PreparedStatement对象
Statement 对象每次执行 SQL 语句时,都会对其进行编译。当相同的 SQL 语句执行多次时,Statement 对象就会使数据库频繁编译相同的 SQL 语句,从而降低数据库的访问效率。
为了解决上述问题,Statement 提供了一个子类 PreparedStatement。PreparedStatement 对象可以对 SQL 语句进行预编译,预编译的信息会存储在 PreparedStatement 对象中。当相同的SQL 语句再次执行时,程序会使用 PreparedStatement 对象中的数据,而不需要对 SQL 语句再次编译去查询数据库,这样就大大地提高了数据的访问效率。
// 要执行的SQL语句,select的where分句或insert的Value()中的值可以用?暂时代替,在执行语句前补齐// 值以外的其它部分,如表名、字段名等,不能用?代替String sql = "INSERT INTO users(id,name,password,email,birthday)"+ "VALUES(?,?,?,?,?)";// 1.创建执行SQL语句的PreparedStatement对象preStmt = conn.prepareStatement(sql);//格式有区别stmt = conn.createStatement();
// 2.为SQL语句中的参数赋值preStmt.setString(1, "5");preStmt.setString(2, "zl");preStmt.setString(3, "123456");preStmt.setString(4, "zl@sina.com");preStmt.setString(5, "1989-12-23");
// 3.执行预编译对象,这时executeUpdate()或executeQuery()不需要参数;preStmt.executeUpdate();10.5 ResultSet 对象
ResultSet 是 JDBC 中用于 封装 SQL 查询结果 的对象。
当你执行 SELECT 查询时,例如:
ResultSet rs = statement.executeQuery("SELECT * FROM users");返回的 rs 就是一个 结果集的可滚动游标,你可以通过这个游标读取每一行、每一列的数据。
ResultSet 的核心特性
-
结果集是按行存储的游标(Cursor)
你需要使用
.next()将游标移动到下一行:while (rs.next()) {String name = rs.getString("name");int age = rs.getInt("age");}rs.next()→ 如果有下一行返回 true,否则 false。- 第一行数据在第一次调用
.next()后才能访问。
-
按列读取数据
可通过 列名 或 列索引(从 1 开始)读取:
String name = rs.getString("name");int age = rs.getInt(2);常用方法包括:
数据类型 方法示例 String getString()int getInt()long getLong()double getDouble()boolean getBoolean()Date getDate()Timestamp getTimestamp() -
ResultSet 的类型
用于定义光标是否可滚动、是否可更新。
(1)光标类型(TYPE)
类型 说明 TYPE_FORWARD_ONLY只能从前往后移动(默认) TYPE_SCROLL_INSENSITIVE可前后滚动,但对数据库变化不敏感 TYPE_SCROLL_SENSITIVE可滚动,且能感知数据库变化 使用方式:
Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);(2)并发性(CONCUR)
类型 说明 CONCUR_READ_ONLY只读(默认) CONCUR_UPDATABLE可更新当前行并写回数据库 -
ResultSet 的常用方法
移动光标
方法 说明 next()移动到下一行 previous()移动到上一行(需滚动类型) first()移动到第一行 last()移动到最后一行 absolute(n)移动到第 n 行 relative(n)相对移动 n 行 获取元数据(列数、列名等)
ResultSetMetaData meta = rs.getMetaData();int columnCount = meta.getColumnCount();常用于通用查询。
可更新 ResultSet
在
CONCUR_UPDATABLE模式下,你可以:rs.updateString("name", "NewName");rs.updateRow(); // 写回数据库插入数据:
rs.moveToInsertRow();rs.updateString("name", "Alice");rs.updateInt("age", 25);rs.insertRow(); -
关闭 ResultSet
使用完必须关闭,避免资源泄漏:
rs.close();stmt.close();conn.close();通常使用 try-with-resources:
try (ResultSet rs = stmt.executeQuery(...)) {// ...}
第十一章 数据库连接池与 DBUtils 工具
11.1 数据库连接池
11.1.1 什么是数据库连接池
为了避免频繁地创建数据库连接,数据库连接池技术应运而生。数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用现有的数据库连接,而不是重新建立。

数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中,当应用程序访问数据库时并不是直接创建 Connection,而是向连接池“申请”一个 Connection。如果连接池中有空闲的 Connection ,则将其返回,否则创建新的 Connection。使用完毕后,连接池会将该 Connection 回收,并交付其他的线程使用,以减少创建和断开数据库接的次数,提高数据库的访问效率。
11.1.2 DataSource 接口
为了获取数据库连接对象(Connection),JDBC 提供了 javax.sql.DataSource 接口,它负责与数据库建立连接,并定义了返回值为 Connection 对象的方法,具体如下。
Connection getConnection()Connection getConnection(String username, String password)
11.1.3 DBCP 数据库连接池
DBCP 是数据库连接池(DataBase Connection Pool)的简称,是 Apache 组织下的开源连接池实现,也是 Tomcat 服务器使用的连接池组件。
BasicDataSource 是 DataSource 接口的实现类,主要包括设置数据源对象的方法
| 方法名称 | 功能描述 |
|---|---|
void setDriverClassName(String driverClassName) | 设置连接数据库的驱动名称 |
void setUrl(String url) | 设置连接数据库的路径 |
void setUsername(String username) | 设置数据库的登录账号 |
void setPassword(String password) | 设置数据库的登录密码 |
void setInitialSize(int initialSize) | 设置数据库连接池初始化的连接数目 |
void setMaxActive(int maxIdle) | 设置数据库连接池最大活跃的连接数目 |
void setMinIdle(int minIdle) | 设置数据库连接池最小闲置的连接数目 |
Connection getConnection() | 从连接池中获取一个数据库连接 |
BasicDataSourceFactory 是创建 BasicDataSource 对象的工厂类,它包含一个返回值为 BasicDataSource 对象的方法 createDataSource(),该方法通过读取配置文件的信息生成数据源 对象并返回给调用者。
-
通过 BasicDataSource 类直接创建数据源对象 在使用 BasicDataSource 类创建一个数据源对象时,需要手动给数据源对象设置属性值,,然后获取数据库连接对象。
public class Example01 {public static DataSource ds = null;static {// 获取DBCP数据库连接池实现类对象BasicDataSource bds = new BasicDataSource();// 设置连接数据库需要的配置信息bds.setDriverClassName("com.mysql.cj.jdbc.Driver");bds.setUrl("jdbc:mysql://localhost:3306/jdbc?serverTimezone=GMT%2B8");bds.setUsername("root");bds.setPassword("root");// 设置连接池的初始化连接参数bds.setInitialSize(5);ds = bds;}public static void main(String[] args) throws SQLException {// 获取数据库连接对象Connection conn = ds.getConnection();//获取数据库连接信息DatabaseMetaData metaData = conn.getMetaData();//打印数据库连接信息System.out.println(metaData.getURL()+",UserName="+metaData.getUserName()+","+metaData.getDriverName());}} -
通过读取配置文件创建数据源对象 除了使用 BasicDataSource 直接创建数据源对象外,还可以使用 BasicDataSourceFactory 工厂类读取配置文件,创建数据源对象,然后获取数据库连接对象。
## dbcpconfig.properties## 连接设置driverClassName=com.mysql.cj.jdbc.Driverurl=jdbc:mysql://localhost:3306/jdbc?serverTimezone=GMT%2B8username=rootpassword=root## 初始化连接initialSize=5## 最大空闲连接maxIdle=10public class Example02 {public static DataSource ds = null;static {// 新建一个配置文件对象Properties prop = new Properties();try {// 通过类加载器找到文件路径,读取配置文件InputStream in = new Example02().getClass().getClassLoader().getResourceAsStream("dbcpconfig.properties");// 把文件以输入流的形式加载到配置对象中prop.load(in);// 创建数据源对象ds = BasicDataSourceFactory.createDataSource(prop);} catch (Exception e) {throw new ExceptionInInitializerError(e);}}public static void main(String[] args) throws SQLException {}}
11.1.4 C3P0 数据库连接池
C3PO 是目前最流行的开源数据库连接池之一,它实现了 DataSource 数据源接口,支持 JDBC2 和 JDBC3 的标准规范,易于扩展并且性能优越,著名的开源框架 Hibernate 和 Spring 都支持该数据源。在使用 C3PO 数据源开发时,需要了解 C3P0 中 DataSource 接口的实现类ComboPooledDataSource,它是 C3PO 的核心类,提供了数据源对象的相关方法

当使用 C3PO 数据源时,首先需要创建数据源对象,创建数据源对象可以使用 ComboPooledDataSource 类,该类有两个构造方法,分别是 ComboPooledDataSource() 和 ComboPooledDataSource(String configName)。接下来,通过两个案例来讲解如何通过上述构造方法创建数据源对象,具体如下。
-
通过 ComboPooledDataSource() 构造方法创建数据源对象 使用 ComboPooledDataSource() 构造方法创建数据源对象,需要手动给数据源对象设置属性值,然后获取数据库连接对象
import com.mchange.v2.c3p0.ComboPooledDataSource;public class Example03 {public static DataSource ds = null;// 初始化C3P0数据库连接池static {ComboPooledDataSource cpds = new ComboPooledDataSource();// 设置连接数据库需要的配置信息try {cpds.setDriverClass("com.mysql.cj.jdbc.Driver");cpds.setJdbcUrl("jdbc:mysql://localhost:3306/jdbc?serverTimezone=GMT%2B8");cpds.setUser("root");cpds.setPassword("root");//设置连接池的参数cpds.setInitialPoolSize(5);cpds.setMaxPoolSize(15);ds = cpds;} catch (Exception e) {throw new ExceptionInInitializerError(e);}}public static void main(String[] args) throws SQLException {// 获取数据库连接对象System.out.println(ds.getConnection());}} -
通过读取配置文件创建数据源对象 使用 ComboPooledDataSource(String configName) 构造方法读取 c3p0-config.xml 配 置文件,创建数据源对象,然后获取数据库连接对象。
<?xml version="1.0" encoding="UTF-8"?><c3p0-config><default-config><property name="driverClass">com.mysql.cj.jdbc.Driver</property><property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbc?serverTimezone=GMT%2B8</property><property name="user">root</property><property name="password">root</property><property name="checkoutTimeout">30000</property><property name="initialPoolSize">10</property><property name="maxIdleTime">30</property><property name="maxPoolSize">100</property><property name="minPoolSize">10</property><property name="maxStatements">200</property></default-config><named-config name="itcast"><property name="driverClass">com.mysql.cj.jdbc.Driver</property><property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbc?serverTimezone=GMT%2B8</property><property name="user">root</property><property name="password">root</property><property name="initialPoolSize">5</property><property name="maxPoolSize">15</property></named-config></c3p0-config>import com.mchange.v2.c3p0.ComboPooledDataSource;public class Example04 {public static DataSource ds = null;// 初始化C3P0数据库连接池static {// 使用c3p0-config.xml配置文件中的named-config节点中name属性的值ComboPooledDataSource cpds = new ComboPooledDataSource("itcast");ds = cpds;}public static void main(String[] args) throws SQLException {System.out.println(ds.getConnection());}}
11.2 DBUtils 工具
11.2.1 DBUtils 工具介绍
为了更加简单地使用 JDBC ,Apache 组织提供了一个 DBUtils 工具,它是操作数据库的一个 组件,实现了对 JDBC 的简单封装,可以在不影响性能的情况下极大地简化 JDBC 的编码工作量。
DBUtils 主要有三个作用:
- 写数据。DBUtils 可以编写 SQL 语句对数据表进行增、删、改操作
- 读数据。DBUtils 可以将从数据表中读取的数据结果集转换成 Java 常用类集合,以方便对结果进行处理
- 优化性能。在使用 DBUtils 的基础上,程序可以使用数据源、JNDI、数据库连接池等技术减少代码冗余
11.2.2 DBUtils 类
DbUtils 是 Apache Commons DbUtils 库中的一个工具类,包含大量静态方法,用于简化 JDBC 编程,尤其是关闭连接、处理事务等重复性操作。
| 分类 | 方法 | 功能 |
|---|---|---|
| 关闭资源 | closeQuietly() | 安全关闭 ResultSet / Statement / Connection |
| 关闭资源 | close() | 直接关闭,不吞异常 |
| 事务 | commitAndClose() | 提交并关闭连接 |
| 事务 | rollback() / rollbackAndClose() | 回滚事务(可加关闭连接) |
| 驱动加载 | loadDriver() | 显式加载 JDBC 驱动 |
| 工具 | enable()/disable() | 控制内部日志(较少使用) |
11.2.3 QueryRunner 类
QueryRunner 类简化了执行 SQL 语句的代码,它与 ResultSetHandler 组合在一起就能完成 大部分的数据库操作,大大地减少了编码量。
QueryRunner 类提供了带有一个参数的构造方法,该方法以 javax.sql.DataSource 作为参数 传递到 QueryRunner 的构造方法中来获取 Connection 对象。针对不同的数据库操作, QueryRunner 类提供了几种常见的方法,具体如下。
int execute(Connection conn, String sql, Object... params)—— 执行DDL语句、存储过程调用语句等<T> List<T> execute(Connection conn, String sql, ResultSetHandler<T> rsh, Object... params)—— 执行DDL语句、存储过程调用语句等int update(Connection conn, String sql, Object... params)—— 执行DML语句,有占位符参数int update(Connection conn, String sql, Object params)—— 执行DML语句,只有一个占位符参数int update(Connection conn, String sql)—— 执行DML语句,没有占位符参数<T> T query(Connection conn, String sql, ResultSetHandler<T> rsh, Object... params)—— 执行DQL语句,有占位符参数<T> T query(Connection conn, String sql, ResultSetHandler<T> rsh)—— 执行DQL语句,没有占位符参数
11.2.4 ResultSetHandler 接口
ResultSetHandler 接口用于处理 ResultSet 结果集,它可以将结果集中的数据转为不同的形式。根据结果集中数据类型的不同,ResultSetHandler 提供了几种常见的实现类,具体如下。
- BeanHandler:将结果集中的第 1 行数据封装到一个对应的 JavaBean 实例中。
- BeanListHandler:将结果集中的每一行数据都封装到一个对应的 JavaBean 实例中,并存放到 List里。
- ScalarHandler:将结果集中某一条记录的其中某一列的数据存储成 Object 对象。
另外,在 ResultSetHandler 接口中,提供了一个单独的方法 handle(java.sql.ResultSet rs), 如果上述实现类没有提供想要的功能,可以通过自定义一个实现 ResultSetHandler 接口的类,然后通过重写 handle() 方法,实现结果集的处理。
11.2.5 ResultSetHandler 实现类
-
BeanHandler 和 BeanListHandler
BeanHandler 和 BeanListHandler 实现类是将结果集中的数据封装到对应的 JavaBean 实例中,这也是实际开发中最常用的结果集处理方法。
public class BaseDao {// 优化查询public static Object query(String sql, ResultSetHandler<?> rsh,Object... params) throws SQLException {Connection conn = null;PreparedStatement pstmt = null;ResultSet rs = null;try {// 获得连接conn = JDBCUtils.getConnection();// 预编译sqlpstmt = conn.prepareStatement(sql);// 将参数设置进去for (int i = 0; params != null && i < params.length; i++){pstmt.setObject(i + 1, params[i]);}// 发送sqlrs = pstmt.executeQuery();// 让调用者去实现对结果集的处理Object obj = rsh.handle(rs);return obj;} catch (Exception e) {e.printStackTrace();}finally {// 释放资源JDBCUtils.release(rs, pstmt, conn);}return rs;}}public class ResultSetTest1 {public static void testBeanHandler() throws SQLException {BaseDao basedao = new BaseDao();String sql = "select * from user where id=?";User user = (User) basedao.query(sql,new BeanHandler(User.class), 1);System.out.print("id为1的User对象的name值为:" + user.getName());}public static void main(String[] args) throws SQLException {testBeanHandler();}}public class ResultSetTest2 {public static void testBeanListHandler() throws SQLException {BaseDao basedao = new BaseDao();String sql = "select * from user ";ArrayList<User> list = (ArrayList<User>) basedao.query(sql,new BeanListHandler(User.class));for (int i = 0; i < list.size(); i++) {System.out.println("第" + (i + 1) + "条数据的username值为:"+ list.get(i).getName());}}public static void main(String[] args) throws SQLException {testBeanListHandler();}} -
ColumnListHandler 和 ScalarHandler
1. ColumnListHandler
作用
- 将 某一列的所有值 封装成一个
List<Object>返回。 - 常用于查询某一列(如所有用户名、所有 ID 等)。
构造方法
new ColumnListHandler() // 默认取第一列new ColumnListHandler("columnName") // 指定列名new ColumnListHandler(int columnIndex) // 指定列索引(从 1 开始)示例
QueryRunner runner = new QueryRunner(dataSource);List<Object> names = runner.query("SELECT name FROM users",new ColumnListHandler<String>("name"));返回结果示例:
["Alice", "Bob", "Charlie"]适用场景
- 只需要一列结果,并且需要该列的所有行数据。
2. ScalarHandler
作用
- 获取查询结果中的 第一行第一列的值。
- 一般用于执行 聚合函数(如 COUNT、MAX、MIN、AVG)或只需要单个值的查询。
构造方法
new ScalarHandler() // 默认取第一行第一列new ScalarHandler(int columnIndex)new ScalarHandler(String columnName)示例
QueryRunner runner = new QueryRunner(dataSource);Long count = runner.query("SELECT COUNT(*) FROM users",new ScalarHandler<Long>());返回结果示例:
10适用场景
- 查询总数量:
COUNT(*) - 查询最大值 / 最小值 / 平均值
- 查询单个字段且只返回一条记录
总结对比
Handler 返回内容 常用场景 ColumnListHandler 一列的所有值组成的 List 查询用户列表、ID 列、名称列等 ScalarHandler 第一行第一列的单个值 COUNT、MAX 等聚合函数;单值查询 - 将 某一列的所有值 封装成一个
第十二章 Ajax
12.1 Ajax 概述
AJAX = 异步 JavaScript 和 XML。
AJAX 是一种用于创建快速动态网页的技术。
通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。
传统的网页(不使用 AJAX)如果需要更新内容,必需重载整个网页面。
优势:
- 请求数据量少:Ajax 请求只需要得到必要数据,对不需要更新的数据不做请求
- 请求分散:Ajax 是按需请求,请求是异步形式,可在任意时刻发出。
- 用户体验优化:Ajax 数据请求响应时间短,数据传送速度快
12.2 jQuery 框架
12.2.1 初识 jQuery
jQuery 是一个 JavaScript 函数库。
jQuery 是一个轻量级的”写的少,做的多”的 JavaScript 库。
jQuery 库包含以下功能:
- HTML 元素选取
- HTML 元素操作
- CSS 操作
- HTML 事件函数
- JavaScript 特效和动画
- HTML DOM 遍历和修改
- AJAX
- Utilities
12.2.2 jQuery 的常用操作
1. DOM 操作
jQuery 使得操作 DOM 变得更加简单和跨浏览器兼容。
1.1 选择器(Selectors)
jQuery 选择器用于查找页面中的元素。
-
选择所有元素:
$("*") -
通过 ID 选择:
$("#elementId") -
通过类名选择:
$(".className") -
通过标签名选择:
$("div") -
组合选择器:
$("div, p") // 选择所有 div 和 p 元素 -
后代选择器:
$("div p") // 选择 div 下的所有 p 元素
1.2 操作元素
-
获取和设置内容:
-
获取元素内容:
var content = $("#elementId").text(); // 获取文本var htmlContent = $("#elementId").html(); // 获取 HTML 内容 -
设置元素内容:
$("#elementId").text("New Text"); // 设置文本$("#elementId").html("<strong>New HTML</strong>"); // 设置 HTML
-
-
获取和设置属性:
-
获取属性:
var href = $("a").attr("href"); -
设置属性:
$("a").attr("href", "<https://www.example.com>");
-
1.3 DOM 操作
-
添加/删除/替换元素:
-
添加子元素:
$("ul").append("<li>New Item</li>"); // 在列表末尾添加新项$("ul").prepend("<li>First Item</li>"); // 在列表开头添加新项 -
删除元素:
$("li").remove(); // 删除所有 li 元素 -
替换元素:
$("p").replaceWith("<div>New content</div>");
-
-
添加/移除类:
$("p").addClass("highlight"); // 添加类$("p").removeClass("highlight"); // 移除类$("p").toggleClass("highlight"); // 切换类
2. 事件处理
jQuery 简化了事件的绑定与移除。
2.1 绑定事件
-
点击事件:
$("#myButton").click(function() {alert("Button clicked!");}); -
鼠标事件:
$("#myDiv").mouseover(function() {$(this).css("background-color", "yellow");}).mouseout(function() {$(this).css("background-color", "white");});
2.2 事件委托
-
事件委托可以通过在父元素上绑定事件来处理动态添加的子元素。
$("#parentDiv").on("click", ".childDiv", function() {alert("Child div clicked");});
2.3 阻止事件的默认行为
$("a").click(function(event) { event.preventDefault(); // 阻止链接跳转});3. 动画效果
jQuery 提供了简单的动画效果,例如显示、隐藏、滑动、淡入淡出等。
3.1 淡入淡出
-
淡入:
$("#myDiv").fadeIn(); // 显示元素,带有渐变效果 -
淡出:
$("#myDiv").fadeOut(); // 隐藏元素,带有渐变效果 -
渐变效果:
$("#myDiv").fadeTo("slow", 0.5); // 逐渐改变透明度
3.2 滑动效果
-
滑动显示:
$("#myDiv").slideDown(); // 元素向下滑动并显示 -
滑动隐藏:
$("#myDiv").slideUp(); // 元素向上滑动并隐藏
3.3 动画
jQuery 允许自定义动画效果。
$("#myDiv").animate({ width: "200px", height: "100px"}, 1000); // 1 秒钟完成宽度和高度的动画4. Ajax 操作
jQuery 提供了方便的 Ajax 操作方法,可以异步地从服务器获取数据并更新页面。
4.1 发送 GET 请求
$.get("data.json", function(data, status, xhr) { // xhr = XMLHttpRequest console.log(data); // 成功回调函数});4.2 发送 POST 请求
$.post("submit.php", { name: "John", age: 30 }, function(response) { console.log(response);});4.3 使用 $.ajax()
更复杂的 Ajax 请求可以通过 $.ajax() 方法发送:
$.ajax({ url: "data.json", type: "GET", success: function(data) { console.log(data); }, error: function(xhr, status, error) { console.error("Error: " + error); }});5. jQuery 方法链
jQuery 支持方法链式调用,即多个方法可以在一行代码中连写。
$("#myDiv") .css("color", "red") .fadeIn() .slideUp() .slideDown();12.2.3 jQuery 中的 load() 方法
在 jQuery 中,load() 方法是一个用于 加载数据 或 加载内容到指定元素 的非常便捷的方法。它主要有两种常见的用法:
-
通过 URL 加载 HTML 内容
load()方法允许你从服务器加载 HTML 内容并将其插入到指定的元素中。语法:
$(selector).load(url, data, callback);url:请求的 URL 地址,可以是同域或跨域的资源(在同域下更常用)。data(可选):要发送到服务器的数据(例如,表单数据或其他信息),可以是一个对象或查询字符串。callback(可选):当请求成功完成后,执行的回调函数。
示例:
$("#result").load("data.html"); // 加载 data.html 的内容并插入到 #result 元素中如果你想发送数据,可以这么做:
$("#result").load("data.html", { id: 123 }); // 向服务器发送 { id: 123 } 数据并加载返回的内容回调函数:
可以通过
load()方法的第三个参数指定回调函数,这个回调函数会在内容加载并插入到 DOM 中之后执行。$("#result").load("data.html", function(response, status, xhr) {if (status == "success") {alert("内容加载成功!");}});注意:
-
默认情况下,
load()会加载整个 HTML 内容,包括头部、主体和尾部,但如果只想加载一部分 HTML,你可以通过指定一个 选择器 来限定只加载某个部分。例如,只加载
#content部分:$("#result").load("data.html #content");这样会只加载
data.html文件中的#content元素的内容。
-
通过事件绑定加载
load()也可以作为事件处理方法来绑定一个 加载事件,不过这个用法已经不常用了,通常情况下会使用 jQuery 的on()来代替。$(selector).load(function() {alert("加载完成!");});但是,这种方式在现代开发中很少使用,更多的是用于
ajax()的请求回调处理。常见的使用场景
- 动态加载内容:比如在页面中点击按钮加载新的文章、图片、评论等。
- AJAX 页面更新:在不刷新整个页面的情况下,从服务器获取内容并动态展示。
- 局部更新页面:比如表单提交后,更新页面的某一部分,而不需要刷新整个页面。
例子:通过按钮点击动态加载内容
假设你有一个按钮,点击后加载新的内容到一个
<div>中:HTML:
<button id="loadButton">加载内容</button><div id="content"></div>JavaScript:
$("#loadButton").click(function() {$("#content").load("newContent.html");});
性能考虑
虽然 load() 方法很方便,但如果页面内容需要频繁地从服务器加载,或者每次都需要加载大量数据时,最好使用 更优化的异步请求,比如 $.ajax(),这样你可以灵活地控制数据格式、错误处理等,而 load() 方法较为简洁,功能也有限。
总结
load() 是一个简洁且实用的方法,可以轻松地将 HTML 内容从服务器加载到页面中指定的元素,尤其适用于单页面应用(SPA)或者需要动态内容加载的场景。它通过内部使用 AJAX 进行异步加载,使得页面无需重新加载就可以更新部分内容。
12.2.4 发送 GET 和 POST 请求
12.3 JSON 数据格式
12.4 Ajax 的基础操作
$.ajax() 是 jQuery 提供的一个功能强大的方法,用于执行 AJAX 请求,它使得在不重新加载页面的情况下从服务器获取数据或向服务器发送数据变得非常简单。与 $.get()、$.post() 等方法相比,$.ajax() 提供了更多的配置选项和更高的灵活性,适用于复杂的异步请求。
基本语法
$.ajax({ url: "your-url", // 请求的 URL type: "GET", // 请求方法 (GET, POST, PUT, DELETE, etc.) data: {}, // 发送到服务器的数据 (通常是一个对象或查询字符串) dataType: "json", // 期望的响应数据类型 (如: json, xml, html, text, script) success: function(response) { // 请求成功时的回调函数 // 处理成功的响应数据 }, error: function(xhr, status, error) { // 请求失败时的回调函数 // 处理错误 }, complete: function(xhr, status) { // 请求完成时的回调函数(无论成功或失败) // 处理完成后的操作 }});常用参数说明
-
url:
- 说明:请求的 URL 地址,可以是相对路径或绝对路径。
-
type:
- 说明:请求的方法,常用的有
GET、POST、PUT、DELETE等。默认是GET。
- 说明:请求的方法,常用的有
-
data:
-
说明:要发送到服务器的数据,通常为一个对象或查询字符串。
-
例子:
data: { username: "John", password: "12345" }或者:
data: "username=John&password=12345"
-
-
dataType:
- 说明:期望服务器返回的响应数据格式。常见的值包括:
json:返回 JSON 格式的数据(最常用)。xml:返回 XML 格式的数据。html:返回 HTML 内容。text:返回纯文本。script:返回 JavaScript 代码并执行它(常用于 JSONP 请求)。
- 说明:期望服务器返回的响应数据格式。常见的值包括:
-
success:
-
说明:请求成功时的回调函数。接收返回的数据作为参数。
-
例子:
success: function(data, status, xhr) {console.log(data);}
-
-
error:
-
说明:请求失败时的回调函数,通常用于处理请求错误。
-
例子:
error: function(xhr, status, error) {console.error("请求失败:", error);}
-
-
complete:
-
说明:无论请求成功与否都会调用的回调函数,常用于清理工作,如隐藏加载动画。
-
例子:
complete: function(xhr, status) {console.log("请求完成");}
-
-
timeout:
- 说明:设置请求的超时时间(毫秒)。如果请求在规定时间内没有响应,则会触发
timeout回调函数。
- 说明:设置请求的超时时间(毫秒)。如果请求在规定时间内没有响应,则会触发
-
beforeSend:
-
说明:在发送请求之前调用的回调函数,通常用于设置请求头、显示加载动画等。
-
例子:
beforeSend: function(xhr) {// 可以在这里修改请求头xhr.setRequestHeader("Authorization", "Bearer token");}
-
使用示例
1. 基本的 GET 请求
$.ajax({ url: "<https://api.example.com/data>", type: "GET", dataType: "json", success: function(response) { console.log("请求成功:", response); }, error: function(xhr, status, error) { console.error("请求失败:", error); }});2. POST 请求,发送 JSON 数据
$.ajax({ url: "<https://api.example.com/login>", type: "POST", data: JSON.stringify({ username: "John", password: "12345" }), contentType: "application/json", // 指定发送数据的格式是 JSON dataType: "json", // 期望接收到的响应类型是 JSON success: function(response) { console.log("登录成功:", response); }, error: function(xhr, status, error) { console.error("登录失败:", error); }});3. 处理超时
$.ajax({ url: "<https://api.example.com/data>", type: "GET", dataType: "json", timeout: 5000, // 设置请求超时为 5 秒 success: function(response) { console.log("数据加载成功:", response); }, error: function(xhr, status, error) { if (status === "timeout") { console.log("请求超时!"); } else { console.log("请求失败:", error); } }});4. 上传文件
var formData = new FormData();formData.append("file", $("#fileInput")[0].files[0]);
$.ajax({ url: "<https://api.example.com/upload>", type: "POST", data: formData, contentType: false, // 不设置内容类型 processData: false, // 不处理数据 success: function(response) { console.log("文件上传成功:", response); }, error: function(xhr, status, error) { console.error("文件上传失败:", error); }});5. 设置请求头
$.ajax({ url: "<https://api.example.com/data>", type: "GET", beforeSend: function(xhr) { xhr.setRequestHeader("Authorization", "Bearer myToken123"); }, success: function(response) { console.log("请求成功:", response); }, error: function(xhr, status, error) { console.error("请求失败:", error); }});高级用法
-
全局事件:你可以使用
$.ajaxSetup()来设置全局 AJAX 配置,这样每次发起 AJAX 请求时,都会使用这些设置。$.ajaxSetup({timeout: 5000, // 所有 AJAX 请求都设置超时时间为 5 秒dataType: "json", // 默认期望返回的响应是 JSON 格式beforeSend: function(xhr) {xhr.setRequestHeader("Authorization", "Bearer myToken123");}}); -
串联多个 AJAX 请求:你可以通过回调函数或
Promise来串联多个 AJAX 请求,进行复杂的异步操作。$.ajax({url: "<https://api.example.com/data1>",type: "GET",success: function(response1) {$.ajax({url: "<https://api.example.com/data2>",type: "GET",success: function(response2) {console.log(response1, response2);}});}});