Html页面标签

注:Java 文件是需要先编译,再由 java 虚拟机跑起来。但 HTML 文件它不需要编译,直接由浏览器进行解析执行。

html页面标签的书写规范

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!-- DOCTYPE:告诉浏览器使用什么规范(默认是html) -->
<!DOCTYPE html>

<!-- 语言 zh中文 en英文 -->
<html lang="zh">

<!-- head标签代表网页头部 -->
<head>

<!-- meta 描述性标签,表示用来描述网站的一些信息 -->
<!-- 一般用来做SEO:搜索殷勤优化 -->
<meta charset="UTF-8">
<meta name="keywords" content="cess,javaweb">
<meta name="description" content="一起来学习java">

<!-- 网站标题 -->
<title>Title</title>
</head>

<!-- body代表主体 -->
<body>
Hello World!
</body>

</html>

<!-- 这是 html 注释,可以在页面右键查看源代码中看到 -->

html标签介绍

1.标签的格式:

​ <标签名>封装的数据

2.标签名大小写不敏感。

3.标签拥有自己的属性。

​ i. 分为基本属性:bgcolor=”red” 可以修改简单的样式效果

​ ii. 事件属性: onclick=”alert(‘你好!’);” 可以直接设置事件响应后的代码。

4.标签又分为,单标签和双标签。

​ i. 单标签格式: <标签名 /> br 换行 hr 水平线

​ ii. 双标签格式: <标签名> …封装的数据…</标签名>**

html标签语法

  1. 标签不能交叉嵌套
  2. 标签必须正确关闭
  3. 属性必须有值,属性值必须加引号
  4. 注释不能 嵌套
  5. 浏览器会自动修复一些错误
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!-- ①标签不能交叉嵌套 -->
正确:<div><span>早安,尚硅谷</span></div>
错误:<div><span>早安,尚硅谷</div></span>
<hr />
<!-- ②标签必须正确关闭 -->
<!-- i.有文本内容的标签: -->
正确:<div>早安,尚硅谷</div>
错误:<div>早安,尚硅谷
<hr />
<!-- ii.没有文本内容的标签: -->
正确:<br />
错误:<br>
<hr />
<!-- ③属性必须有值,属性值必须加引号 -->
正确:<font color="blue">早安,尚硅谷</font>
错误:<font color=blue>早安,尚硅谷</font>
错误:<font color>早安,尚硅谷</font>
<hr />
<!-- ④注释不能嵌套 -->
正确:<!-- 注释内容 --> <br/>
错误:<!-- <!-- 这是错误的 html 注释 --> -->
<hr />
注意事项:
1.html 代码不是很严谨。有时候标签不闭合,也不会报错。

常用html标签

font标签

需求 1:在网页上显示 我是字体标签 ,并修改字体为 宋体,颜色为红

1
2
3
4
5
       <!--font修改字体标签 ,
color修改字体颜色
size修改字体大小 1-7,
face修改字体-->
<font color="red" size="3" face="黑体">字体标签演示</font>

特殊字符

需求 1:把 换行标签 变成文本 转换成字符显示在页面上

常见的特殊字符表

image-20211112152919632>-

1
2
3
4
5
6
7
8
<body>
<!-- &lt; 用于转义 < less than
&gt; 用于转义 > greater than
&nbsp; 用于转义 空格
-->
我是 &lt;br&gt;标签<br/>
Monkey&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; King
</body>

标题标签

标题标签是 h1 到 h6

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<body>
<!--
标题标签 h1 - h6 h1最大,h6最小
默认左对齐
标题属性 align
left 左对齐
center 居中
right 右对齐
-->
<h1 align="left">标题</h1>
<h2 align="center">标题</h2>
<h3 align="right">标题</h3>
<h4>标题</h4>
<h5>标题</h5>
<h6>标题</h6>
</body>

超链接(重点!!!)

1
2
3
4
5
6
7
8
9
10
11
<body>
<!--
a 是超链接标签
href属性 表示跳转的连接
target属性设置哪个目标进行跳转
_self 表示当前页面(默认值)
_blank 表示打开新页面来进行跳转
-->
<a href="http://www.baidu.com" target="_self">百度</a><br>
<a href="http://www.baidu.com" target="_blank">百度</a>
</body>

列表标签

分为无序列表和有序列表

1
2
3
4
5
6
7
8
9
10
11
<!--
无序列表 标签 ul
li是列表项
有序列表 标签 ol --》order list
属性 type可以修改列表项前面的符号-->
<ul type="none">
<li>刘德华</li>
<li>郭富城</li>
<li>张学友</li>
<li>黎明</li>
</ul>

img标签

img 标签可以在 html 页面上显示图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!--需求1:使用img标签显示一张美女的照片。并修改宽高,和边框属性

img标签是图片标签,用来显示图片
src属性可以设置图片的路径
width属性设置图片的宽度
height属性设置图片的高度
border属性设置图片边框大小
alt属性设置当指定路径找不到图片时,用来代替显示的文本内容

在JavaSE中路径也分为相对路径和绝对路径.
相对路径:从工程名开始算

绝对路径:盘符:/目录/文件名

在web中路径分为相对路径和绝对路径两种
相对路径:
. 表示当前文件所在的目录
.. 表示当前文件所在的上一级目录
文件名 表示当前文件所在目录的文件,相当于 ./文件名 ./ 可以省略

绝对路径:
正确格式是: http://ip:port/工程名/资源路径

错误格式是: 盘符:/目录/文件名
-->

<img src="../imgs/3.jpg" alt="没有该图片" border="20px" height="268" width="200">
<img src="../imgs/10.jpg" alt="没有该图片" border="1px" height="268" width="200">
<img src="../imgs/50.jpg" alt="没有该图片" border="1px" height="268" width="200">
<img src="../imgs/3.jpg" alt="没有该图片" border="1px" height="268" width="200">
<img src="13.jpg" alt="没有该图片" border="1px" height="268" width="200">

表格标签(!!!)

需求 1:做一个 带表头的 ,三行,三列的表格,并显示边框

需求 2:修改表格的宽度,高度,表格的对齐方式,单元格间距。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!--
需求1:做一个 带表头的 ,三行,三列的表格,并显示边框
需求2:修改表格的宽度,高度,表格的对齐方式,单元格间距。

table 标签是表格标签
border 设置表格标签
width 设置表格宽度
height 设置表格高度
align 设置表格相对于页面的对齐方式
cellspacing 设置单元格间距

tr 是行标签
th 是表头标签
td 是单元格标签
align 设置单元格文本对齐方式
b是加粗标签
-->
<table align="center" border="1px" height="300" width="300" cellspacing="0">
<tr>
<th>1.1</th>
<th>1.2</th>
<th>1.3</th>
</tr>
<tr>
<td>2.1</td>
<td>2.2</td>
<td>2.3</td>
</tr>
<tr>
<td>3.1</td>
<td>3.2</td>
<td>3.3</td>
</tr>
</table>

跨行跨列表格

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<!--需求 1:新建一个五行,五列的表格,第一行,第一列的单元格要跨两列,第二行第一列的单元格跨两行,第四行第四
列的单元格跨两行两列。

colspan 属性设置跨列
rowspan 属性设置跨行
-->
<table height="300" width="300" border="1" cellspacing="0">
<tr>
<td colspan="2">1.1</td>
<td>1.3</td>
<td>1.4</td>
<td>1.5</td>
</tr>
<tr>
<td rowspan="2">2.1</td>
<td>2.2</td>
<td>2.3</td>
<td>2.4</td>
<td>2.5</td>
</tr>
<tr>
<td>3.2</td>
<td>3.3</td>
<td>3.4</td>
<td>3.5</td>
</tr>
<tr>
<td>4.1</td>
<td>4.2</td>
<td>4.3</td>
<td rowspan="2" colspan="2">4.4</td>
</tr>
<tr>
<td>5.1</td>
<td>5.2</td>
<td>5.3</td>
</tr>

iFrame框架标签(内嵌窗口)

1
2
3
4
5
6
7
8
9
10
11
12
13
<!--iframe 标签可以在页面上开辟一个小区域显示一个单独的页面
iframe 和 a 标签组合使用的步骤:
1 在 iframe 标签中使用 name 属性定义一个名称
2 在 a 标签的 target 属性上设置 iframe 的 name 的属性值
-->

<b>我是一个页面上的单独一个区域页面</b><br/>
<iframe src="标题标签.html" width="500" height="400" name="abc"></iframe>
<ul>
<li><a href="img标签.html" target="abc">img标签.html</a></li>
<li><a href="列表标签.html" target="abc">列表标签.html</a></li>
<li><a href="表格标签.html" target="abc">表格标签.html</a></li>
</ul>

表单标签 ( 重点!!!)

什么是表单?

表单就是 html 页面中,用来收集用户信息的所有元素集合.然后把这些信息发送给服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<!--
需求 1:创建一个个人信息注册的表单界面。包含用户名,密码,确认密码。性别(单选),兴趣爱好(多选),国籍(下
拉列表)。
隐藏域,自我评价(多行文本域)。重置,提交。
-->

<!--
form标签就是表单
input type=text 是文件输入框 value设置默认显示内容
input type=password 是密码输入框 value设置默认显示内容
input type=radio 是单选框 name属性可以对其进行分组 checked="checked"表示默认选中
input type=checkbox 是复选框 checked="checked"表示默认选中
input type=reset 是重置按钮 value属性修改按钮上的文本
input type=submit 是提交按钮 value属性修改按钮上的文本
input type=button 是按钮 value属性修改按钮上的文本
input type=file 是文件上传域
input type=hidden 是隐藏域 当我们要发送某些信息,而这些信息,不需要用户参与,就可以使用隐藏域(提交的时候同时发送给服务器)

select 标签是下拉列表框
option 标签是下拉列表框中的选项 selected="selected"设置默认选中

textarea 表示多行文本输入框 (起始标签和结束标签中的内容是默认值)
rows 属性设置可以显示几行的高度
cols 属性设置每行可以显示几个字符宽度
-->

<form>
用户名:<input type="text" value="张三" /><br/>
密码:<input type="password" /> </br>
确认密码:<input type="password" /> </br>
性别:<input type="radio" name="sex" checked="checked"/><input type="radio" name="sex"/><br/>
兴趣爱好:<input type="checkbox" checked="checked"/>Java<input type="checkbox" checked="checked"/>C++
<input type="checkbox" />Python<br/>
国籍:<select>
<option>-请选择国国籍-</option>
<option selected="selected">中国</option>
<option>美国</option>
<option>小日本</option>
</select><br/>
自我评价:<textarea rows="10" cols="10">我是默认值</textarea><br/>
<input type="reset" value="恢复默认设置" />
<input type="submit" value="确认并提交"><br/>
<input type="button" value="这是一个按钮"/><br/>
<input type="file"/>
<input type="hidden" name = "abc" value="jlg"/>
</form>

表单格式化

将上一个表单放到table表里面,这样的显示效果更好

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<form>
<h1 align="center">用户注册</h1>
<table align="center">
<tr>
<td>用户名:</td>
<td><input type="text" value="张三" /></td>
</tr>
<tr>
<td>密码:</td>
<td><input type="password" /></td>
</tr>
<tr>
<td>确认密码:</td>
<td><input type="password" /></td>
</tr>

.....

</table>

</form>

表单提交的细节

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<!--
form标签是表单标签
action属性设置提交的服务器地址
method属性设置提交的方式GET(默认值)或POST

表单提交的时候,数据没有发送给服务器的三种情况:
1、表单项没有name属性值
2、单选、复选(下拉列表中的option标签)都需要添加value属性,以便发送给服务器
3、表单项不在提交的form标签中

GET请求的特点是:
1、浏览器地址栏中的地址是:action属性[+?+请求参数]
请求参数的格式是:name=value&name=value
2、不安全
3、它有数据长度的限制

POST请求的特点是:
1、浏览器地址栏中只有action属性值
2、相对于GET请求要安全
3、理论上没有数据长度的限制
-->
<form action="http://localhost:8080" method="post">
<h1 align="center">用户注册</h1>
<table align="center">
<tr>
<td>用户名:</td>
<td><input type="text" value="张三" name="userName"/></td>
</tr>
<tr>
<td>密码:</td>
<td><input type="password" name="pwd"/></td>
</tr>
<tr>
<td>性别:</td>
<td><input type="radio" name="sex" checked="checked" value="boy"/>
<input type="radio" name="sex" value="girl"/></td>
</tr>
<tr>
<td>兴趣爱好:</td>
<td><input name="hobby" type="checkbox" checked="checked" value="java"/>Java
<input name="hobby" type="checkbox" checked="checked" value="cpp"/>C++
<input name="hobby" type="checkbox" value="python" />Python</td>
</tr>
<tr>
<td>国籍:</td>
<td> <select name="country">
<option value="none">-请选择国国籍-</option>
<option selected="selected" value="cn">中国</option>
<option value="usa">美国</option>
<option value="jp">小日本</option>
</select></td>
</tr>
<tr>
<td>自我评价:</td>
<td><textarea rows="10" cols="10" name="self">我是默认值</textarea></td>
</tr>
<tr>
<td><input type="reset" value="恢复默认设置" /></td>
<td align="center"> <input type="submit" value="确认并提交"></td>
</tr>
<tr>
<td><input type="button" value="这是一个按钮"/></td>
</tr>
<tr>
<td><input type="hidden" name = "abc" value="jlg"/></td>
</tr>
</table>

其他标签

演示 div span p标签的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!--需求 1:div、span、p 标签的演示
div 标签 默认独占一行
span 标签 它的长度是封装数据的长度
这里span标签1和span标签2直接有个小空格是因为这俩标签之间有很多空格,浏览器自动优化成一个空格

p 段落标签 默认会在段落的上方或下方各空出一行来(如果已有就不再空)
-->

<div>div标签1</div>
<div>div标签2</div>
<span>span标签1</span>
<span>span标签2</span>
<p>p标签1</p>
<p>p标签2</p>

CSS技术

什么是CSS

CSS 是「层叠样式表单」。是用于(增强)控制网页样式并允许将样式信息与网页内容分离的一种标记性语言。

CSS语法规则

image-20211112210748108

选择器:浏览器根据“选择器”决定受 CSS 样式影响的 HTML 元素(标签)。

属性 (property) 是你要改变的样式名,并且每个属性都有一个值。属性和值被冒号分开,并 由花括号包围,这样就组成了一个完整的样式声明(declaration),例如:p {color: blue} 多个声明:如果要定义不止一个声明,则需要用分号将每个声明分开。虽然最后一条声明的 最后可以不加分号(但尽量在每条声明的末尾都加上分号)

例如: p{

color:red;

font-size:30px;

}

注:一般每行只描述一个属性 CSS 注释:/*注释内容*/

CSS和html的结合方式

第一种:

在标签的 style 属性上设置”key:value value;”,修改标签样式。

1
2
3
4
5
6
7
8
9
<!--需求 1:分别定义两个 div、span 标签,分别修改每个 div 标签的样式为:边框 1 个像素,实线,红色。-->
<div style="border: 1px solid red;">div标签1</div>
<div style="border: 1px solid red;">div标签2</div>
<span style="border: 1px solid red;">span标签1</span>
<span style="border: 1px solid red;">span标签2</span>
问题:这种方式的缺点?
1.如果标签多了。样式多了。代码量非常庞大。
2.可读性非常差。
3.Css 代码没什么复用性可言

快捷键:ctrl+r快捷替换

第二种:

**在 head 标签中,使用 style 标签来定义各种自己需要的 css 样式。

格式如下: xxx {

​ Key : value value;

​ }**

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- style标签专门定义 css样式代码-->
<style type="text/css">
/*这是css的注释*/
div{
border: red solid 1px;
}
span{
border: red solid 1px;
}
</style>
</head>
<body>
<div >div标签1</div>
<div >div标签2</div>
<span >span标签1</span>
<span >span标签2</span>
</body>
</html>

问题:这种方式的缺点。

1.只能在同一页面内复用代码,不能在多个页面中复用 css 代码。

2.维护起来不方便,实际的项目中会有成千上万的页面,要到每个页面中去修改。工作量太大了。

第三种:

把 css 样式写成一个单独的 css 文件,再通过 link 标签引入即可复用。 使用 html 的

<link rel="stylesheet" type="text/css" href="css文件路径" />

标签 导入 css 样 式文件。

1
2
3
4
5
6
7
/*这是css的注释*/
div {
border: red solid 1px;
}
span {
border: red solid 1px;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" type="text/css" href="cssfor03.css"/>
</head>
<body>
<div >div标签1</div>
<div >div标签2</div>
<span >span标签1</span>
<span >span标签2</span>
</body>
</html>

CSS选择器

标签名选择器

标签名选择器的格式是:

标签名{

​ 属性:值;

​ }

标签名选择器,可以决定哪些标签被动的使用这个样式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style type="text/css">
div{
border: yellow solid 1px;
color: blue;/*字体颜色*/
font-size: 30px;
}span{
border: blue dashed 5px;
color: yellow;/*字体颜色*/
font-size: 20px;
}
</style>
</head>
<body>
<!-- 需求 1:在所有 div 标签上修改字体颜色为蓝色,字体大小 30 个像素。边框为 1 像素黄色实线。
并且修改所有 span 标签的字体颜色为黄色,字体大小 20 个像素。边框为 5 像素蓝色虚线。
-->
<div>div标签1</div>
<div>div标签2</div>
<div>div标签3</div>
<span>span标签1</span>
<span>span标签2</span>
<span>span标签3</span>
</body>
</html>

id选择器

1
2
3
4
5
6
id 选择器的格式是:
#id 属性值{
属性:值;
}

id 选择器,可以让我们通过 id 属性选择性的去使用这个样
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>id选择器</title>
<style type="text/css">
#id001{
color: blue;
font-size: 30px;
border: yellow 1px dotted;
}
#id002{
color: red;
font-size: 20px;
border: blue 5px dotted;
}
</style>
</head>
<body>
<!--需求 1:分别定义两个 div 标签,
第一个 div 标签定义 id 为 id001 ,然后根据 id 属性定义 css 样式修改字体颜色为蓝色,
字体大小 30 个像素。边框为 1 像素黄色实线。
第二个 div 标签定义 id 为 id002 ,然后根据 id 属性定义 css 样式 修改的字体颜色为红色,字体大小 20 个像
素。边框为 5-->
<div id="id001">div标签1</div>
<div id="id002">div标签1</div>
</body>
</html>

class选择器(类选择器)

1
2
3
4
5
class 类型选择器的格式是:
.class 属性值{
属性:值;
}
class 类型选择器,可以通过 class 属性有效的选择性地去使用这个样式。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>class类型选择器</title>
<style type="text/css">
.class01{
color: blue;
font-size: 30px;
border: 1px solid yellow;
}

.class02{
color: grey;
font-size: 26px;
border: 1px solid red;
}
</style>
</head>
<body>
<!--
需求1:修改 class 属性值为 class01的 span 或 div 标签,字体颜色为蓝色,字体大小30个像素。边框为1像素黄色实线。
需求2:修改 class 属性值为 class02的 div 标签,字体颜色为灰色,字体大小26个像素。边框为1像素红色实线。
-->
<div class="class02">div标签class01</div>
<div class="class02">div标签</div>
<span class="class02">span标签class01</span>
<span>span标签2</span>
</body>
</html>


组合选择器

1
2
3
4
5
组合选择器的格式是:
选择器 1,选择器 2,选择器 n{
属性:值;
}
组合选择器可以让多个选择器共用同一个 css 样式代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>class类型选择器</title>
<style type="text/css">
.class01 , #id01{
color: blue;
font-size: 20px;
border: yellow 1px solid;
}
</style>
</head>
<body>
<!--
需求1:修改 class="class01" 的div 标签 和 id="id01" 所有的span标签,
字体颜色为蓝色,字体大小20个像素。边框为1像素黄色实线。
-->
<div id="id01">div标签class01</div> <br />
<span>span 标签</span> <br />
<div>div标签</div> <br />
<div>div标签id01</div> <br />
</body>
</html>


常用样式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
1、字体颜色
color:red;
颜色可以写颜色名如:black, blue, red, green 等
颜色也可以写 rgb 值和十六进制表示值:如 rgb(255,0,0),#00F6DE,如果写十六进制值必
须加#

2、宽度
width:19px;
宽度可以写像素值:19px;
也可以写百分比值:20%;

3、高度
height:20px;
高度可以写像素值:19px;
也可以写百分比值:20%;

4、背景颜色
background-color:#0F2D4C

4、字体样式:
color:#FF0000;字体颜色红色
font-size:20px; 字体大小

5、红色 1 像素实线边框
border:1px solid red;

7、DIV 居中
margin-left: auto;
margin-right: auto;

8、文本居中:
text-align: center;

9、超连接去下划线
text-decoration: none;

10、表格细线
table {
border: 1px solid black; /*设置边框*/
border-collapse: collapse; /*将边框合并*/
}
td,th {
border: 1px solid black; /*设置边框*/
}

11、列表去除修饰
ul {
list-style: none;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>06-css常用样式.html</title>
<style type="text/css">

div{
color: red;
border: 1px yellow solid;
width: 300px;
height: 300px;
background-color: green;
font-size: 30px;
margin-left: auto;
margin-right: auto;
text-align: center;
}
table{
border: 1px red solid;
border-collapse: collapse;
}

td{
border: 1px red solid;
}

a{
text-decoration: none;

}

ul{
list-style: none;
}

</style>
</head>
<body>
<ul>
<li>11111111111</li>
<li>11111111111</li>
<li>11111111111</li>
<li>11111111111</li>
<li>11111111111</li>
</ul>
<table>
<tr>
<td>1.1</td>
<td>1.2</td>
</tr>
</table>
<a href="http://www.baidu.com">百度</a>
<div>我是div标签</div>
</body>
</html>

JavaScript

JavaScript介绍

Javascript 语言诞生主要是完成页面的数据验证。

因此它运行在客户端,需要运行浏览器来解析执行 JavaScript 代码。

JS 是 Netscape 网景公司的产品,最早取名为 LiveScript;为了吸引更多 java 程序员。更名为 JavaScript。

JS 是弱类型,Java 是强类型。

1
2
3
4
5
6
7
8
弱类型就是类型可变。
强类型,就是定义变量的时候。类型已确定。而且不可变。
int i = 12;
var i; 类型不确定,可变
i=12; 数值型
i= "abc" 字符串类型


特点:

  1. 交互性(它可以做的就是信息的动态交互)

  2. 安全性(不允许直接访问本地硬盘)

  3. 跨平台性(只要是可以解释 JS 的浏览器都可以执行,和平台无关)

JavaScript和html结合

第一种

只需要在 head 标签中,或者在 body 标签中, 使用 script 标签 来书写 JavaScript 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript">
// alert是JavaScript提供的一个警告框函数
// 它可以接收任意类型的参数,这个参数就是警告框的提示信息
alert("hello,JavaScript!");
</script>
</head>
<body>

</body>
</html>

第二种

使用 script 标签引入 单独的 JavaScript 代码文件

在外部定义js文件 ,再通过标签属性 src引入<script type="text/javascript" src="For02.js">

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- 这里用到从外部引入js文件的方法,注意这里不能再嵌套使用第一种方法-->
<script type="text/javascript" src="For02.js">
//alert();不能再在这里写,而是要另开标签再使用
</script>
<script type="text/javascript">
alert("这是在html文件定义的js文件");
</script>
</head>
<body>

</body>
</html>

变量

  • 什么是变量?变量是可以存放某些值的内存的命名。

  • JavaScript 的变量类型:
    数值类型: number
    字符串类型: string
    对象类型: object
    布尔类型: boolean
    函数类型: function

  • JavaScript 里特殊的值:
    undefined 未定义,所有 js 变量未赋于初始值的时候,默认值都是 undefined.
    null 空值
    NaN 全称是:Not a Number。非数字。非数值。

  • JS 中的定义变量格式:
    var 变量名;
    var 变量名 = 值

1
2
3
4
5
6
7
8
9
10
11
12
13
<script type="text/javascript">
var i;
//typeof是JavaScript提供的一个函数,可以返回参数的类型
//alert(typeof (i)); //undefined
i = 13;
//alert(typeof (i));//number
i = "aba";
//alert(typeof (i));//string

var a =12;
var b = "abc";
alert(a*b); //NaN 全称是 not a number 非数字,非数值。
</script>

关系运算(比较运算)

等于: == 等于是简单的做字面值的比较

全等于: === 除了做字面值的比较之外,还会比较两个变量的数据类型

1
2
3
4
5
6
<script type="text/javascript">
var a = 12;
var b = "12";
//alert(a==b); //true ==简单的字面上的比较 ,中间会自动做类型转换
alert(a===b); //false
</script>

逻辑运算

且运算: &&

或运算: ||

取反运算: !

在 JavaScript 语言中,所有的变量,都可以做为一个 boolean 类型的变量去使用。

0 、null、 undefined、””(空串) 都认为是 false;**

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*
&& 且运算。
有两种情况:
第一种:当表达式全为真的时候。返回最后一个表达式的值。
第二种:当表达式中,有一个为假的时候。返回第一个为假的表达式的值

|| 或运算
第一种情况:当表达式全为假时,返回最后一个表达式的值
第二种情况:只要有一个表达式为真。就会把回第一个为真的表达式的值

并且 && 与运算 和 ||或运算 有短路。
短路就是说,当这个&&或||运算有结果了之后 。后面的表达式不再执行

*/
var a = "abc";
var b = true;
var d = false;
var c = null;
alert(b && c && d); //只要有一个为假,就返回第一个为假的值 //null

数组(!!!)

JS 中 数组的定义:

格式: var 数组名 = []; // 空数组

var 数组名 = [1 , ’abc’ , true]; // 定义数组同时赋值元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var arr=[]; //空数组
var brr = [true ,1,null,"aaa",""];//数组元素不要求同类型

arr[0] = 12
//alert(arr[0]);//12
arr[2]="java";
//alert(arr[2])//java
//alert(arr.length)//3 这里定义arr[2]的时候自动扩容成了长度为3的数组,
//至于arr[1]没有赋值,所以是undefined

//alert(arr[9]); //读操作不会给数组扩容
for (var i = 0; i < arr.length; i++) {//javaScript数组也可以进行遍历操作
alert(arr[i]);
}

函数

函数的两种定义方式(!!!)

第一种,可以使用 function

使用的格式如下:

1
2
3
function 函数名(形参列表){ 
函数体
}

在 JavaScript 语言中,如何定义带有返回值的函数?

只需要在函数体内直接使用 return 语句返回值即可

1
2
3
4
5
6
7
8
9
10
11
12
//function定义函数
function f() {
alert("这是一个无参函数")
}

//f();//和java一样,需要调用函数

function f1(s,m) { //定义有参函数,参数不需要写类型。
return s+m; //返回值直接使用return
}

alert(f1(100,50));//调用有参函数
第二种

使用格式如下:

var 函数名 = function(形参列表){}

注意:JavaScript不允许函数重载,若使用重载,则会覆盖上一次的定义

函数的 arguments 隐形参数(只在 function 函数内)

基本介绍
  • 就是在 function 函数中不需要定义,但却可以直接用来获取所有参数的变量。

    我们管它叫隐形参数。 隐形参数特别像 java 基础的可变长参数一样。

    public void fun( Object … args );

    可变长参数其实就是一个数组。

    那么 js 中的隐形参数也跟 java的可变长参数一样。操作类似数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 function f(a) {//这里的参数列表不会影响可变参数 arguments
alert(arguments.length);
for (var i = 0; i < arguments.length; i++) {
alert(arguments[i]);
}
}
//f(1,3,6,"abc");

//arguments的使用案例演示
//要求:求函数参数列表的和
function f1(num1,num2) {//这里传递两个参数是为了可读性,实际调用的时候可以传递多个参数
var result = 0;

for (var i = 0; i < arguments.length; i++) {
if (typeof(arguments[i])=="number") //这里做一个类型判断
result += arguments[i];
}
return result;
}
alert( f1(1,2,3,4,5,6,7,8,9,10,"abcv"));//55

js中自定义对象

object形式的自定义对象

对象的定义: var 变量名 = new Object(); // 对象实例(空对象)

变量名.属性名 = 值; // 定义一个属性

变量名.函数名 = function(){} // 定义一个函数

对象的访问: 变量名.属性 / 函数名()

1
2
3
4
5
6
7
8
9
10
//自定义 object 类型的对象
var obj = new Object();
obj.name = "周星驰";
obj.age="18";
obj.fun = function () {
alert("姓名:"+this.name+",年龄:"+this.age);
}
// alert(obj.name);
// alert(obj.age);
obj.fun();

大括号形式的自定义对象

对象的定义:

var 变量名 = { // 空对象

​ 属性名:值, // 定义一个属性 注意:不是最后一个就要加,

​ 属性名:值, // 定义一个属性

​ 函数名:function(){} / / 定义一个函数

};

对象的访问: 变量名.属性 / 函数名();**

1
2
3
4
5
6
7
8
9
10
11
12
//大括号形式的自定义对象
var obj = { //定义对象
name: "周星驰",
age: 18,
fun: function () {
alert("姓名:" + this.name + ",年龄:" + this.age);
}
};
//对象调用
alert(obj.name);
alert(obj.age);
obj.fun();

js中的事件

什么是事件?事件是电脑输入设备与页面进行交互的响应。我们称之为事件。

常用的事件

1
2
3
4
5
onload 						加载完成事件: 页面加载完成之后,常用于做页面 js 代码初始化操作
onclick 单击事件: 常用于按钮的点击响应操作。
onblur 失去焦点事件: 常用用于输入框失去焦点后验证其输入内容是否合法。
onchange 内容发生改变事件: 常用于下拉列表和输入框内容发生改变后操作
onsubmit 表单提交事件: 常用于表单提交前,验证所有表单项是否合法。

事件的注册又分为静态注册和动态注册两种

什么是事件的注册(绑定)?
  • 其实就是告诉浏览器,当事件响应后要执行哪些操作代码,叫事件注册或事件绑定。
静态注册事件:
  • 通过 html 标签的事件属性直接赋于事件响应后的代码,这种方式我们叫静态注册。
动态注册事件:
  • 是指先通过 js 代码得到标签的 dom 对象,然后再通过 dom 对象.事件名 = function(){} 这种形式赋于事件响应后的代码,叫动态注册。
动态注册基本步骤:

​ 固定写法

  • 1、获取标签对象
    2、标签对象.事件名 = fucntion()

onload 加载完成事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript">
//onload事件
var onloadFun = function () {
alert("这是静态注册Onload事件...很多代码")
}
//onload事件动态注册 是固定写法
window.onload = function () {
alert("这是动态注册 Onload事件")
}
</script>
</head>
<!--<body onload="alert('这是静态注册onload事件')"> 可以直接写在 body标签里面,但不推荐这种写法 -->
<!--
静态注册onload事件:
onload事件是浏览器解析完成页面之后自动触发的事件
<body onload="onloadFun()">
-->
<body >

</body>
</html>

onclick 单击事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript">
var onclickFun = function () {
alert("这是静态注册 onclick事件")
}
//动态注册 onclick事件
window.onload=function () {
//动态注册的步骤
//1.获取标签对象
var btnObj = document.getElementById("btn02");
/* document 是 JavaScript语言提供的一个对象(文档) 相当于整个页面
getElementById 通过 id属性获取标签对象
*/

//2.标签对象.事件名 = function(){};
btnObj.onclick = function () {
alert("这是动态注册 onclick事件");
}

}
</script>
</head>
<body>
<!-- 演示onclick事件-->
<button onclick="onclickFun()">按钮1</button>
<!--通过按钮2 演示 动态注册onclick事件-->
<button id="btn02">按钮2</button>

</body>
</html>

onblur 失去焦点事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript">
var onBlurFun = function () {
//演示静态注册onblur事件
//console 是 浏览器的控制台,是JavaScript提供的一个对象,用于在浏览器控制台输出
//log 就是打印的方法
console.log("静态注册onblur事件");
}
//动态注册onblur事件
//仍然是固定写法
window.onload = function () {
var pwdObj = document.getElementById("pwd");
pwdObj.onblur = function () {
console.log("这是动态注册onblur事件");
}
}
</script>
</head>
<body>
用户名:<input type="text" onblur="onBlurFun()"><br/>
密码:<input type="password" id="pwd"><br/>

</body>
</html>

onchange 内容发生改变事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript">
//静态注册onchange事件
var onChangeFun = function () {
alert("档次发生变化");
}
//动态注册
window.onload = function () {
//固定步骤
//获取标签对象
var goodsObj = document.getElementById("selGoods");
//标签对象.事件名 = function(){};
goodsObj.onchange = function () {
alert("商品发生变化");
}
}
</script>
</head>
<body>
请选择购买档次<select onchange="onChangeFun()">
<option>-档次-</option>
<option>低级</option>
<option>普通</option>
<option>高级</option>
</select>

请选择商品<select id="selGoods">
<option>-商品-</option>
<option>13</option>
<option>13pro</option>
<option>13proMax</option>
</select>


</body>
</html>

onsubmit 表单提交事

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript">
function onSubmitFun() {
alert("静态注册 onsubmit事件...不合法,阻止提交");
return false;
}
//演示动态注册onsubmit事件
window.onload = function () {
//固定写法
var formObj = document.getElementById("form02");
formObj.onsubmit = function () {
alert("动态注册 onsubmit事件...不合法,阻止提交");
return false;
}
}

</script>
</head>
<body>
<form action="http://localhost:8080" method="get" onsubmit="return onSubmitFun()">
<input type="submit" value="静态注册"/>
</form>
<form action="http://localhost:8080" id="form02">
<input type="submit" value="动态注册"/>
</form>
</body>
</html>

js的正则表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 表示要求字符串中,是否包含字母e
// var patt = new RegExp("e");
// var patt = /e/; // 也是正则表达式对象
// 表示要求字符串中,是否包含字母a或b或c的任意一个
// var patt = /[abc]/;
// 表示要求字符串,是否包含小写字母
// var patt = /[a-z]/;
// 表示要求字符串,是否包含任意大写字母
// var patt = /[A-Z]/;
// 表示要求字符串,是否包含任意数字
// var patt = /[0-9]/;
// 表示要求字符串,是否包含字母,数字,下划线
// var patt = /\w/;
// 表示要求 字符串中是否包含至少一个a(1到多)
// var patt = /a+/;
// 表示要求 字符串中是否 *包含* 零个 或 多个a
// var patt = /a*/;
// 表示要求 字符串是否包含一个或零个a
// var patt = /a?/;
// 表示要求 字符串是否包含连续三个a
// var patt = /a{3}/;
// 表示要求 字符串是否包 至少3个连续的a,最多5个连续的a
// var patt = /a{3,5}/;
// 表示要求 字符串是否包 至少3个连续的a,
// var patt = /a{3,}/;
// 表示要求 字符串必须以a结尾
// var patt = /a$/;
// 表示要求 字符串必须以a打头
// var patt = /^a/;

// 要求字符串中是否*包含* 至少3个连续的a
// var patt = /a{3,5}/;
// 要求字符串,从头到尾都必须完全匹配
// var patt = /^a{3,5}$/;

var patt = /^\w{5,12}$/;

var str = "wzg168[[[";

alert( patt.test(str) );

DOM模型

DOM 全称是 Document Object Model 文档对象模型

​ 大白话,就是把文档中的标签,属性,文本,转换成为对象来管理。

​ 那么 它们是如何实现把标签,属性,文本转换成为对象来管理呢。这就是我们马上要学习的重点。

Document对象(重点!!!)

image-20211113224646603

  • Document 对象的理解:

  • 第一点:Document 它管理了所有的 HTML 文档内容。

  • 第二点:document 它是一种树结构的文档。有层级关系。

  • 第三点:它让我们把所有的标签 都 对象化

  • 第四点:我们可以通过 document 访问所有的标签对象。

  • 什么是对象化??

    • 我们已经学过面向对象。请问什么是对象化?

      举例: 有一个人有年龄:18 岁,性别:女,名字:张某某

      我们要把这个人的信息对象化怎么办!

           Class Person { 
      

      ​ private int age;

      ​ private String sex;

      ​ private String name;

      ​ }

  • 那么 html 标签 要 对象化 怎么办?

    • 模拟对象化,相当于:

    • ```

    class Dom{

       private String id; // id 属性
    
        private String tagName; //表示标签名
    
        private Dom parentNode; //父亲
    
        private List<Dom> children; // 孩子结点
        
        private String innerHTML; // 起始标签和结束标签中间的内容 
    

    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82





    #### Document 对象中的方法介绍(重点!!!)

    ****

    - **`document.getElementById(elementId)`
    通过标签的id属性查找标签dom对象, elementId是标签的id属性值

    - `document.getElementsByName (elementName)`
    通过标签的name属性查找标签dom对象, elementName标签的name属性值

    - `document.getElementsByTagName (tagname)`
    通过标签名查找标签dom对象。tagname是标签名

    - `document.createElement( tagName)`
    方法,通过给定的标签名,创建一个标签对象。tagName是要创建的标签名

    注:
    document对象的三个查询方法,如果有id属性,优先使用`getElementById`方法来进行查询
    如果没有id属性,则优先使用`getElementsByName`方法来进行查询
    如果id属性和name属性都没有最后再按标签名查`getElementsByTagName`

    以上三个方法,一定要在页面加载完成之后执行,才能查询到标签对象。



    #### getElementById

    ##### 完成用户输入校验

    ```html
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript">
    //设计:通过校验按钮校验 用户名输入框的合法性

    function onClickFun() {/*
    思路:首先要获取 用户名输入框的内容
    通过document 的 getElementById方法获取标签对象
    得到的对象实际就是 dom,该对象包含了标签的属性
    */
    var userNameObj = document.getElementById("userName");
    var value = userNameObj.value;//获取该标签的value,即输入框的内容
    //通过正则表达式校验
    var patt = /^\w{5,12}$/;
    /*
    * test()方法用于测试某个字符串,是不是匹配我的规则 ,
    * 匹配就返回 true。不匹配就返回 false.
    * */
    //通过document对象操作span标签
    var spanObj = document.getElementById("userNameSpan");


    if (patt.test(value)) {
    // alert("合法")
    //spanObj.innerHTML = "用户名合法";

    //注意这里的img标签里的“需要转义
    spanObj.innerHTML = "<img src=\"right.png\" width=\"18px\" height=\"18\"px>";
    } else
    spanObj.innerHTML = "<img src=\"wrong.png\" width=\"18px\" height=\"18\"px>";

    }
    </script>
    </head>
    <body>

    <!-- 完成用户名输入校验,要求 5-12位 数字,字母,下划线 -->
    用户名:<input type="text" value="wag" id="userName">
    <span id="userNameSpan" style="color: red">

    </span>
    <button onclick="onClickFun()">校验</button>
    </body>
    </html>

getElementsByName

完成全选,全不选和反选功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript">
var hobbys = document.getElementsByName("hobby");

function checkAll() {
//通过js操作实现复选框的全选,全不选,反选功能
//通过 getElementsByName方法返回 多个标签对象集合
// 这个集合的操作跟数组 一样
// 集合中每个元素都是 dom 对象
// 这个集合中的元素顺序是他们在 html 页面中从上到下的顺序
//集合里的元素的checked属性可读可写,当checked = true时表示选中,反之未选中
for (var i = 0; i < hobbys.length; i++) {
hobbys[i].checked = true;
}
}
function checkNone() {
//集合里的元素的checked属性可读可写,当checked = true时表示选中,反之未选中
for (var i = 0; i < hobbys.length; i++) {
hobbys[i].checked = false;
}
}
function checkReberse() {
for (var i = 0; i < hobbys.length; i++) {
hobbys[i].checked =! hobbys[i].checked;
}
}

</script>
</head>
<body>

兴趣爱好:<input type="checkbox" value="java" name="hobby" />java
兴趣爱好:<input type="checkbox" value="cpp" name="hobby" />cpp
兴趣爱好:<input type="checkbox" value="python" name="hobby" />python<br/>

<button onclick="checkAll()">全选</button>
<button onclick="checkNone()">全不选</button>
<button onclick="checkReberse()">反选</button>
</body>
</html>

getElementsByTagName

用过标签名获取元素集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript">
function checkAll() {
//getElementsByTagName是通过 标签名来获取标签对象集合,其他操作和getElementsByName类似,
var inputObjs = document.getElementsByTagName("input");
for (var i = 0; i < inputObjs.length; i++) {
inputObjs[i].checked = true;
}
}
</script>
</head>
<body>
兴趣爱好:<input type="checkbox" value="java" name="hobby" />java
兴趣爱好:<input type="checkbox" value="cpp" name="hobby" />cpp
兴趣爱好:<input type="checkbox" value="python" name="hobby" />python<br/>

<button onclick="checkAll()">全选</button>
</body>
</html>

createElement & appendChild

document.createElement(tagName)

方法,通过给定的标签名,创建一个标签对象。tagName是要创建的标签名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript">
window.onload = function () {
// 现在需要我们使用js代码来创建html标签,并显示在页面上
// 标签的内容就是:<div>你也能变成光</div>
var divObj = document.createElement("div"); // 在内存中 <div></div>

var textNodeObj = document.createTextNode("你也能变成光");//有一个文本节点对象 #你也能变成光

divObj.appendChild(textNodeObj); // <div>你也能变成光</div>

// divObj.innerHTML = "你也能变成光"; // <div>你也能变成光</div>,但,还只是在内存中
// 添加子元素
document.body.appendChild(divObj);
}
</script>
</head>
<body>

</body>
</html>

节点的常用属性和方法

节点就是标签对象

方法:
  • 通过具体的元素节点调用

    getElementsByTagName()

    方法,获取当前节点的指定标签名孩子节点

  • appendChild( oChildNode )

    方法,可以添加一个子节点,oChildNode 是要添加的孩子节点

属性:
  • childNodes
    属性,获取当前节点的所有子节点

  • firstChild
    属性,获取当前节点的第一个子节点

  • lastChild
    属性,获取当前节点的最后一个子节点

  • parentNode
    属性,获取当前节点的父节点

  • nextSibling
    属性,获取当前节点的下一个节点

  • previousSibling
    属性,获取当前节点的上一个节点

  • className
    用于获取或设置标签的class属性值

  • innerHTML
    属性,表示获取/设置起始标签和结束标签中的内容(包含里面的标签)

  • innerText
    属性,表示获取/设置起始标签和结束标签中的文本

练习,DOM查询练习

jQury

jQury介绍

什么是 jQuery ?
jQuery,顾名思义,也就是 JavaScript 和查询(Query),它就是辅助 JavaScript 开发的 js 类库。

jQuery 核心思想!!!
它的核心思想是 write less,do more(写得更少,做得更多),所以它实现了很多浏览器的兼容问题。

jQuery 流行程度
jQuery 现在已经成为最流行的 JavaScript 库,在世界前 10000 个访问最多的网站中,有超过 55%在使用
jQuery。

jQuery 好处!!!
jQuery 是免费、开源的,jQuery 的语法设计可以使开发更加便捷,例如操作文档对象、选择 DOM 元素、
制作动画效果、事件处理、使用 Ajax 以及其他功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<!-- 引入jq jar包-->
<script type="text/javascript" src="../script/jquery-1.7.2.js"></script>
<script type="text/javascript">
/*window.onload = function () {
var buttonObj = document.getElementById("btnId");
buttonObj.onclick = function () {
alert("这是通过js实现的按钮点击响应效果")
}
}*/
$(function () { //页面加载完成之后, 相当于 window.onload = function () {}
var $btnObj= $("#btnId");
$btnObj.click(function () {
alert("jq实现响应效果");
});
});
</script>
</head>
<body>

<button id="btnId">SayHello</button>

</body>
</html>

常见问题?
1、使用jQuery一定要引入jQuery库吗? 答案:是,必须

2、jQuery中的$到底是什么? 答案: 它是一个函数

3、怎么为按钮添加点击响应函数的?

答案:

1、使用jQuery查询到标签对象
2、使用标签对象.click( function(){} );**

jQuery 核心函数

$ 是 jQuery 的核心函数,能完成 jQuery 的很多功能。$()就是调用$这个函数

1、传入参数为 [ 函数 ] 时:

$(function(){});

表示页面加载完成之后。相当于 window.onload = function(){}

2、传入参数为 [ HTML 字符串 ] 时:
会对我们创建这个 html 标签对象

3、传入参数为 [ 选择器字符串 ] 时:
$(“#id 属性值”); id 选择器,根据 id 查询标签对象
$(“标签名”); 标签名选择器,根据指定的标签名查询标签对象
$(“.class 属性值”); 类型选择器,可以根据 class 属性查询标签对象

4、传入参数为 [ DOM 对象 ] 时:
会把这个 dom 对象转换为 jQuery 对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<script type="text/javascript" src="../script/jquery-1.7.2.js"></script>
<script type="text/javascript">

//核心函数的4个作用
//传入参数为[函数]时:在文档加载完成后执行这个函数

// $(function () {
// alert("1111"); //相当于 window.onload = function(){}
// })
//传入参数为[HTML字符串]时:根据这个字符串创建元素节点对象
$(function () {

$("<div>" +
" <span>div-span标签1</span>" +
" <span>div-span标签2</span>" +
" </div>").appendTo("body");

//传入参数为[选择器字符串]时:根据这个字符串查找元素节点对象
//alert($("button").length);
//传入参数为[DOM对象]时:将DOM对象包装为jQuery对象返回
var btnObj = document.getElementById("btn01");
alert($(btnObj))

});

jQuery对象和DOM对象区分

Dom 对象

1.通过 getElementById()查询出来的标签对象是 Dom 对象

2.通过 getElementsByName()查询出来的标签对象是 Dom 对象

3.通过 getElementsByTagName()查询出来的标签对象是 Dom 对象

4.通过 createElement() 方法创建的对象,是 Dom 对象

DOM 对象 Alert 出来的效果是:[object HTML 标签名 Element]

jQuery 对象

5.通过 JQuery 提供的 API 创建的对象,是 JQuery 对象

6.通过 JQuery 包装的 Dom 对象,也是 JQuery 对象

7.通过 JQuery 提供的 API 查询到的对象,是 JQuery 对象

jQuery 对象 Alert 出来的效果是:[object Object]

问题:jQuery 对象的本质是什么

jQuery 对象是 dom 对象的数组 + jQuery 提供的一系列功能

说明:

1
2
3
4
$(function(){
var $btns =$("button"); //有 4 个button
alert($btns);
});

$btns 对象

image-20211114195606842

DOM对象和jQuery对象使用区别

jQuery 对象不能使用 DOM 对象的属性和方法

DOM 对象也不能使用 jQuery 对象的属性和方法

DOM对象和jQuery对象互转(!!!)

1、dom 对象转化为 jQuery 对象(重点)
1、先有 DOM 对象
2、$( DOM 对象 ) 就可以转换成为 jQuery 对象

2、jQuery 对象转为 dom 对象(重点)
1、先有 jQuery 对象
2、jQuery 对象[下标]取出相应的 DOM 对象

jQuery选择器(重点!!!)

基本选择器(!!!)

#ID 选择器:根据 id 查找标签对象

.class 选择器:根据 class 查找标签对象

element 选择器:根据标签名查找标签对象

*选择器:表示任意的,所有的元素

selector1,selector2 组合选择器:合并选择器 1,选择器 2 的结果并返回

p.myClass : 表示标签名必须是 p 标签,而且 class 类型还要是 myClass

演示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<script type="text/javascript" src="../script/jquery-1.7.2.js"></script>
<script type="text/javascript">
$(function () {
//1.选择 id 为 one 的元素 "background-color","#bbffaa"
$("#btn1").click(function () {
//jQuery对象的css方法可以设置和获取样式
$("#one").css("background-color","#bbffaa");
})


//2.选择 class 为 mini 的所有元素
$("#btn2").click(function () {
$(".mini").css("background-color","#bbffaa");
});

//3.选择 元素名是 div 的所有元素
$("#btn3").click(function () {
$("div").css("background-color","#bbffaa");
});

//4.选择所有的元素
$("#btn4").click(function () {
$("*").css("background-color","#bbffaa");
});

//5.选择所有的 span 元素和id为two的元素
$("#btn5").click(function () {
$("span,#two").css("background-color","#bbffaa");
});

});

层级选择器(!!!)

ancestor descendant 后代选择器 :在给定的祖先元素下匹配所有的后代元素

parent > child 子元素选择器:在给定的父元素下匹配所有的子元素

prev + next 相邻元素选择器:匹配所有紧接在 prev 元素后的 next 元素

prev ~ sibings 之后的兄弟元素选择器:匹配 prev 元素之后的所有 siblings 兄弟元素

演示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$(document).ready(function(){		//这个是 $( function(){} )的全写
//1.选择 body 内的所有 div 元素
$("#btn1").click(function(){
$("body div").css("background", "#bbffaa");
});

//2.在 body 内, 选择div子元素
$("#btn2").click(function(){
$("body > div").css("background", "#bbffaa");
});

//3.选择 id 为 one 的下一个 div 元素
$("#btn3").click(function(){
$("#one+div").css("background", "#bbffaa");
});

//4.选择 id 为 two 的元素后面的所有 div 兄弟元素
$("#btn4").click(function(){
$("#two~div").css("background", "#bbffaa");
});
});

过滤选择器

基本过滤器

:first 获取第一个元素

:last 获取最后个元素

:not(selector) 去除所有与给定选择器匹配的元素

:even 匹配所有索引值为偶数的元素,从 0 开始计数

:odd 匹配所有索引值为奇数的元素,从 0 开始计数

:eq(index) 匹配一个给定索引值的元素

:gt(index) 匹配所有大于给定索引值的元素

:lt(index) 匹配所有小于给定索引值的元素

:header 匹配如 h1, h2, h3 之类的标题元素

:animated 匹配所有正在执行动画效果的元素

内容过滤器

:contains(text) 匹配包含给定文本的元素

:empty 匹配所有不包含子元素或者文本的空元素

:parent 匹配含有子元素或者文本的元素

:has(selector) 匹配含有选择器所匹配的元素的元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$(document).ready(function(){
//1.选择 含有文本 'di' 的 div 元素
$("#btn1").click(function(){
$("div:contains(di)").css("background", "#bbffaa");
});

//2.选择不包含子元素(或者文本元素) 的 div 空元素
$("#btn2").click(function(){
$("div:empty").css("background", "#bbffaa");
});

//3.选择含有 class 为 mini 元素的 div 元素
$("#btn3").click(function(){
$("div:has(.mini)").css("background", "#bbffaa");
});

//4.选择含有子元素(或者文本元素)的div元素
$("#btn4").click(function(){
$("div:parent").css("background", "#bbffaa");
});
});
属性过滤器:

[attribute] 匹配包含给定属性的元素。

[attribute=value] 匹配给定的属性是某个特定值的元素

[attribute!=value] 匹配所有不含有指定的属性,或者属性不等于特定值的元素。

[attribute^=value] 匹配给定的属性是以某些值开始的元素

[attribute$=value] 匹配给定的属性是以某些值结尾的元素

[attribute*=value] 匹配给定的属性是以包含某些值的元素

[attrSel1][attrSel2][attrSelN] 复合属性选择器,需要同时满足多个条件时使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
$(function() {
//1.选取含有 属性title 的div元素
$("#btn1").click(function() {
$("div[title]").css("background", "#bbffaa");
});
//2.选取 属性title值等于'test'的div元素
$("#btn2").click(function() {
$("div[title = 'test']").css("background", "#bbffaa");
});
//3.选取 属性title值不等于'test'的div元素(*没有属性title的也将被选中)
$("#btn3").click(function() {
$("div[title !='test']").css("background", "#bbffaa");
});
//4.选取 属性title值 以'te'开始 的div元素
$("#btn4").click(function() {
$("div[title^='te']").css("background", "#bbffaa");
});
//5.选取 属性title值 以'est'结束 的div元素
$("#btn5").click(function() {
$("div[title $= 'est']").css("background", "#bbffaa");
});
//6.选取 属性title值 含有'es'的div元素
$("#btn6").click(function() {
$("div[title *='es']").css("background", "#bbffaa");
});

//7.首先选取有属性id的div元素,然后在结果中 选取属性title值 含有'es'的 div 元素
$("#btn7").click(function() {
$("div[id][title *='es']").css("background", "#bbffaa");
});
//8.选取 含有 title 属性值, 且title 属性值不等于 test 的 div 元素
$("#btn8").click(function() {
$("div[title][title != 'test']").css("background", "#bbffaa");
});
表单过滤器

:input 匹配所有 input, textarea, select 和 button 元素

:text 匹配所有 文本输入框

:password 匹配所有的密码输入框

:radio 匹配所有的单选框

:checkbox 匹配所有的复选框

:submit 匹配所有提交按钮

:image 匹配所有 img 标签

:reset 匹配所有重置按钮

:button 匹配所有 input type=button 按钮

:file 匹配所有 input type=file 文件上传

:hidden 匹配所有不可见元素 display:none 或 input type=hidden

表单对象属性过滤器

:enabled 匹配所有可用元素

:disabled 匹配所有不可用元素

:checked 匹配所有选中的单选,复选,和下拉列表中选中的 option 标签对象

:selected 匹配所有选中的 option

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
	//1.对表单内 可用input 赋值操作
$("#btn1").click(function(){
// val()可以操作表单项的value属性值
// 它可以设置和获取
$(":text").val("你也能变成光");
});
//2.对表单内 不可用input 赋值操作
$("#btn2").click(function(){
$(":text:disabled").val("Monster");
});
//3.获取多选框选中的个数 使用size()方法获取选取到的元素集合的元素个数
$("#btn3").click(function(){
alert($(":checkbox:checked").length);
});
//4.获取多选框,每个选中的value值
$("#btn4").click(function(){
//先获取多选框
var $checkboxes = $(":checkbox:checked");
//遍历多选框 ,使用 jq的each方法遍历
$checkboxes.each(function () {
alert(this.value);
})
});
//5.获取下拉框选中的内容
$("#btn5").click(function(){

var $selecteds = $(":selected:selected");
$selecteds.each(function () {
alert(this.innerText);
})

});
})

jQuery 元素筛选

eq() 获取给定索引的元素 功能跟 :eq() 一样

first() 获取第一个元素 功能跟 :first 一样

last() 获取最后一个元素 功能跟 :last 一样

filter(exp) 留下匹配的元素

is(exp) 判断是否匹配给定的选择器,只要有一个匹配就返回,true

has(exp) 返回包含有匹配选择器的元素的元素 功能跟 :has 一样

not(exp) 删除匹配选择器的元素 功能跟 :not 一样

children(exp) 返回匹配给定选择器的子元素 功能跟 parent>child 一样

find(exp) 返回匹配给定选择器的后代元素 功能跟 ancestor descendant 一样

next() 返回当前元素的下一个兄弟元素 功能跟 prev + next 功能一样

nextAll() 返回当前元素后面所有的兄弟元素 功能跟 prev ~ siblings 功能一样

nextUntil() 返回当前元素到指定匹配的元素为止的后面元素

parent() 返回父元素

prev(exp) 返回当前元素的上一个兄弟元素

prevAll() 返回当前元素前面所有的兄弟元素

prevUnit(exp) 返回当前元素到指定匹配的元素为止的前面元素

siblings(exp) 返回所有兄弟元素

add() 把 add 匹配的选择器的元素添加到当前 jquery 对象中

:visible 匹配所有可见元素

:hidden 匹配所有不可见元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
    //(1)eq()  选择索引值为等于 3 的 div 元素
$("#btn1").click(function () {
$("div").eq(3).css("background-color", "#bfa");
});
//(2)first()选择第一个 div 元素
$("#btn2").click(function () {
//first() 选取第一个元素
$("div").first().css("background-color", "#bfa");
});
//(3)last()选择最后一个 div 元素
$("#btn3").click(function () {
//last() 选取最后一个元素
$("div").last().css("background-color", "#bfa");
});
//(4)filter()在div中选择索引为偶数的
$("#btn4").click(function () {
//filter() 过滤 传入的是选择器字符串
$("div").filter(":even").css("background-color", "#bfa");
});
//(5)is()判断#one是否为:empty或:parent
//is用来检测jq对象是否符合指定的选择器
$("#btn5").click(function () {
alert($("#one").is(":parent"));
});

//(6)has()选择div中包含.mini的
$("#btn6").click(function () {
//has(selector) 选择器字符串 是否包含selector
$("div").has(".mini").css("background-color", "#bfa");
});
//(7)not()选择div中class不为one的
$("#btn7").click(function () {
//not(selector) 选择不是selector的元素
$("div").not(".one").css("background-color", "#bfa");
});
//(8)children()在body中选择所有class为one的div子元素
$("#btn8").click(function () {
//children() 选出所有的子元素
$("body").children("div.one").css("background-color", "#bfa");
});


//(9)find()在body中选择所有class为mini的div元素
$("#btn9").click(function () {
//find() 选出所有的后代元素
$("body").find("div.mini").css("background-color", "#bfa");
});
//(10)next() #one的下一个div
$("#btn10").click(function () {
//next() 选择下一个兄弟元素
$("#one").next("div").css("background-color", "#bfa");
});
//(11)nextAll() #one后面所有的span元素
$("#btn11").click(function () {
//nextAll() 选出后面所有的元素
$("#one").nextAll("span").css("background-color", "#bfa");
});
//(12)nextUntil() #one和span之间的元素
$("#btn12").click(function () {
//
$("#one").nextUntil("span").css("background-color", "#bfa")
});
//(13)parent() .mini的父元素
$("#btn13").click(function () {
$(".mini").parent().css("background-color", "#bfa");
});
//(14)prev() #two的上一个div
$("#btn14").click(function () {
//prev()
$("#two").prev("div").css("background-color", "#bfa")
});
//(15)prevAll() span前面所有的div
$("#btn15").click(function () {
//prevAll() 选出前面所有的元素
$("span").prevAll("div").css("background-color", "#bfa")
});
//(16)prevUntil() span向前直到#one的元素
$("#btn16").click(function () {
//prevUntil(exp) 找到之前所有的兄弟元素直到找到exp停止
$("span").prevUntil("#one").css("background-color", "#bfa")
});
//(17)siblings() #two的所有兄弟元素
$("#btn17").click(function () {
//siblings() 找到所有的兄弟元素,包括前面的和后面的
$("#two").siblings().css("background-color", "#bfa")
});


//(18)add()选择所有的 span 元素和id为two的元素
$("#btn18").click(function () {

// $("span,#two,.mini,#one")
$("span").add("#two")
.css("background-color", "#bfa");
});

});

jQuery属性操作

html() 它可以设置和获取起始标签和结束标签中的内容。 跟 dom 属性 innerHTML 一样。

text() 它可以设置和获取起始标签和结束标签中的文本。 跟 dom 属性 innerText 一样。

val() 它可以设置和获取表单项的 value 属性值。 跟 dom 属性 value 一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$(function () {
// 不传参数,是获取,传递参数是设置
// alert( $("div").html() );// 获取
// $("div").html("<h1>我是div中的标题 1</h1>");// 设置

// 不传参数,是获取,传递参数是设置
// alert( $("div").text() ); // 获取
// $("div").text("<h1>我是div中的标题 1</h1>"); // 设置

// 不传参数,是获取,传递参数是设置
$("button").click(function () {
alert($("#username").val());//获取
$("#username").val("超级程序猿");// 设置
});

});

val 方法同时设置多个表单项的选中状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
    $(function () {
/*
// 批量操作单选
$(":radio").val(["radio2"]);
// 批量操作筛选框的选中状态
$(":checkbox").val(["checkbox3","checkbox2"]);
// 批量操作多选的下拉框选中状态
$("#multiple").val(["mul2","mul3","mul4"]);
// 操作单选的下拉框选中状态
$("#single").val(["sin2"]);
*/
$("#multiple,#single,:radio,:checkbox").val(["radio2","checkbox1","checkbox3","mul1","mul4","sin3"]);
});

attr() 可以设置和获取属性的值,不推荐操作 checked、readOnly、selected、disabled 等等

​ attr 方法还可以操作非标准的属性。比如自定义属性:abc,bbj

prop() 可以设置和获取属性的值,只推荐操作 checked、readOnly、selected、disabled 等

jQuery练习

2.全选,全不选,反选

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="../../script/jquery-1.7.2.js"></script>
<script type="text/javascript">

$(function () {
//全选按钮单击事件
$("#checkedAllBtn").click(function () {
$(":checkbox").prop("checked", true);
})

//全不选按钮绑定单击事件
$("#checkedNoBtn").click(function () {
$(":checkbox").prop("checked", false);
});

//反选按钮绑定单击事件
$("#checkedRevBtn").click(function () {

$(":checkbox[name='items']").each(function () {
this.checked = !this.checked;
})
//判定是否满选,操作 全选/全不选 复选框
var $all = $(":checkbox[name ='items']").length;
var $selected = $(":checkbox[name = 'items']:checked").length;
$("#checkedAllBox").prop("checked", $all === $selected);
})

//提交按钮绑定单击事件
$("#sendBtn").click(function () {
//获取选中的复选框的文本信息
$(":checkbox[name = 'items']:checked").each(function () {
alert(this.value)
});

});

// 全选/全不选复选框绑定单击事件(妙!)
$("#checkedAllBox").click(function () {
$(":checkbox[name = 'items']").prop("checked",this.checked)
})

//给全部球类绑定单击事件
$(":checkbox[name = 'items']").click(function () {

//判定是否满选,操作 全选/全不选 复选框
var $all = $(":checkbox[name ='items']").length;
var $selected = $(":checkbox[name = 'items']:checked").length;
$("#checkedAllBox").prop("checked", $all === $selected);
})


});

</script>
</head>
<body>

<form method="post" action="">

你爱好的运动是?<input type="checkbox" id="checkedAllBox"/>全选/全不选

<br/>
<input type="checkbox" name="items" value="足球"/>足球
<input type="checkbox" name="items" value="篮球"/>篮球
<input type="checkbox" name="items" value="羽毛球"/>羽毛球
<input type="checkbox" name="items" value="乒乓球"/>乒乓球
<br/>
<input type="button" id="checkedAllBtn" value="全 选"/>
<input type="button" id="checkedNoBtn" value="全不选"/>
<input type="button" id="checkedRevBtn" value="反 选"/>
<input type="button" id="sendBtn" value="提 交"/>
</form>

</body>
</html>

Dom的增删改

内部插入:

appendTo() a.appendTo(b) 把 a 插入到 b 子元素末尾,成为最后一个子元素
prependTo() a.prependTo(b) 把 a 插到 b 所有子元素前面,成为第一个子元素**

外部插入:

insertAfter() a.insertAfter(b) 得到 ba
insertBefore() a.insertBefore(b) 得到 ab**

替换:

replaceWith() a.replaceWith(b) 用 b 替换掉 a ,并且多个a只被替换成1个b
replaceAll() a.replaceAll(b) 用 a 替换掉所有 b,多个b可以被多个a替换掉**

删除:

remove() a.remove(); 删除 a 标签
empty() a.empty(); 清空 a 标签里的内容**

jQuery练习二

2.从左到右,从右到左

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
$(function () {
// 给选中添加到右边按钮绑定单击事件
$("button:eq(0)").click(function () {
//获取选中的选项
//把选中的选项appendTo到 右边
$("select:eq(0) option:selected").appendTo($("select:eq(1)"));
})


//给 全部添加到右边(第二个按钮) 按钮绑定单击事件
$("button:eq(1)").click(function () {
$("select:eq(0) option").appendTo($("select:eq(1)"));
})

//给 选中添加到左边(第三个按钮) 按钮绑定单击事件
$("button:eq(2)").click(function () {
$("select:eq(1) option:selected").appendTo($("select:eq(0)"));
})

//给 全部添加到左边(第四个按钮) 按钮绑定单击事件
$("button:eq(3)").click(function () {
$("select:eq(1) option").appendTo($("select:eq(0)"));
})

})

动态添加、删除表格记录(!!!)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
$(function () {
//定义一个删除函数
var deleteFun = function () {
// 在事件响应的function函数中,有一个this对象。这个this对象是当前正在响应事件的dom对象。
var $trObj = $(this).parent().parent();
var $name = $trObj.find("td:first").text();

/**
* confirm 是JavaScript语言提供的一个确认提示框函数。你给它传什么,它就提示什么<br/>
* 当用户点击了确定,就返回true。当用户点击了取消,就返回false
*/
if (confirm("你确定要删除[" + $name + "]这个数据吗")) {
$trObj.remove();
}
// return false 可以阻止默认行为
return false;
}

//添加记录
$("#addEmpButton").click(function () {
//获取添加表格的输入框信息
var name = $("#empName").val();
var email = $("#email").val();
var salary = $("#salary").val();
//创建表格添加到table表格最后
var $trObj = $("<tr>" +
"<td>" + name + "</td>" +
"<td>" + email + "</td>" +
"<td>" + salary + "</td>" +
"<td><a href=\"deleteEmp?id=001\">Delete</a></td>" +
"</tr>").appendTo($("#employeeTable"))

//添加记录后要给delete绑定单击事件
// 给添加的行的a标签绑上事件
$trObj.find("a").click(deleteFun);
})

//删除记录
$("a").click(deleteFun);

})


CSS样式操作

addClass() 添加样式

removeClass() 删除样式

toggleClass() 有就删除,没有就添加样式。

offset() 获取和设置元素的坐标。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
<style type="text/css">//css

div{
width:100px;
height:260px;
}

div.border{
border: 2px white solid;
}

div.redDiv{
background-color: red;
}

div.blackDiv{
border: 5px blue solid;
}

</style>



<script type="text/javascript" src="script/jquery-1.7.2.js"></script>
<script type="text/javascript">


$(function(){
/*
CSS
css(name|pro|[,val|fn]) 读写匹配元素的样式属性。
a.css('color')取出a元素的color
a.css('color',"red")设置a元素的color为red

CSS 类

addClass(class|fn) 为元素添加一个class值;<div class="mini big">
removeClass([class|fn]) 删除元素的class值;传递一个具体的class值,就会删除具体的某个class
a.removeClass():移除所有的class值

**/

var $divEle = $('div:first');

$('#btn01').click(function(){
//addClass() - 向被选元素添加一个或多个类
$divEle.addClass("redDiv blackDiv");
});

$('#btn02').click(function(){
//removeClass() - 从被选元素删除一个或多个类
$divEle.removeClass()
});


$('#btn03').click(function(){
//toggleClass() - 对被选元素进行添加/删除类的切换操作
//切换就是如果具有该类那么删除,如果没有那么添加上
$divEle.toggleClass("redDiv");
});

$('#btn04').click(function(){
//offset() - 返回第一个匹配元素相对于文档的位置。
var os = $divEle.offset();
//注意通过offset获取到的是一个对象,这个对象有两个属性top表示顶边距,left表示左边距
alert("顶边距:"+os.top+" 左边距:"+os.left);

//调用offset设置元素位置时,也需要传递一个js对象,对象有两个属性top和left
//offset({ top: 10, left: 30 });
$divEle.offset({
top:50,
left:60
});
});

})
</script>

jQuery动画

基本动画

show() 将隐藏的元素显示
hide() 将可见的元素隐藏。
toggle() 可见就隐藏,不可见就显示。

  • 以上动画方法都可以添加参数。

    • 第一个参数是动画 执行的时长,以毫秒为单位

    • 第二个参数是动画的回调函数 (动画完成后自动调用的函数)

淡入淡出动画

fadeIn() 淡入(慢慢可见)

fadeOut() 淡出(慢慢消失)

fadeTo() 在指定时长内慢慢的将透明度修改到指定的值。0 透明,1 完成可见,0.5 半透明

fadeToggle() 淡入/淡出 切换

  • 以上动画方法都可以添加参数。

    • 第一个参数是动画 执行的时长,以毫秒为单位

    • 第二个参数是动画的回调函数 (动画完成后自动调用的函数)

练习 06、CSS_动画 品牌展

1
详见idea模块

jQuery事件操作

$( function(){} ); 和 window.onload = function(){} 的区别?

他们分别是在什么时候触发?
1、jQuery 的页面加载完成之后是浏览器的内核解析完页面的标签创建好 DOM 对象之后就会马上执行。

2、原生 js 的页面加载完成之后,除了要等浏览器内核解析完标签创建好 DOM 对象,

​ 还要等标签显示时需要的内容加载完成。

他们触发的顺序?
1、jQuery 页面加载完成之后先执行

2、原生 js 的页面加载完成之后

他们执行的次数?
1、原生 js 的页面加载完成之后,只会执行最后一次的赋值函数(前面的都被覆盖了js没有重载)。

2、jQuery 的页面加载完成之后是全部把注册的 function 函数,依次顺序全部执行。

jQuery中其他事件的处理方法

click() 它可以绑定单击事件,以及触发单击事件

mouseover() 鼠标移入事件

mouseout() 鼠标移出事件

bind() 可以给元素一次性绑定一个或多个事件。

例:

1
2
3
$("h5").bind("click mouseover mouseout",function () { //function之间可以传一个或多个绑定事件
console.log("这是bind绑定的事件")
})

one() 使用上跟 bind 一样。但是 one 方法绑定的事件只会响应一次。

unbind() 跟 bind 方法相反的操作,解除事件的绑定

live() 也是用来绑定事件。它可以用来绑定选择器匹配的所有元素的事件。

​ 哪怕这个元素是后面动态创建出来的也有效

事件的冒泡

什么是事件的冒泡?

事件的冒泡是指,父子元素同时监听同一个事件。当触发子元素的事件的时候,

同一个事件也被传递到了父元素的事件里去 响应。

那么如何阻止事件冒泡呢?

在子元素事件函数体内,return false; 可以阻止事件的冒泡传递。**

JavaScript事件对象

事件对象,是封装有触发的事件信息的一个 javascript 对象。
我们重点关心的是怎么拿到这个 javascript 的事件对象。以及使用。

如何获取呢 javascript 事件对象呢?
在给元素绑定事件的时候,在事件的 function( event ) 参数列表中添加一个参数,

这个参数名,我们习惯取名为 event。
这个 event 就是 javascript 传递参事件处理函数的事件对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//1.原生javascript获取 事件对象
/*window.onload = function () {
document.getElementById("areaDiv").onclick = function (event) {
console.log(event);
}
}*/
//2.JQuery代码获取 事件对象
/*$(function () {
$("#areaDiv").click(function (event) {
console.log(event);
})
})*/
//3.使用bind同时对多个事件绑定同一个函数。怎么获取当前操作是什么事件。
$(function () {
$("#areaDiv").bind("mouseover mouseout",function (event) {
if (event.type=="mouseover"){
console.log("bind绑定鼠标移入事件")
}else {
console.log("bind绑定鼠标移出事件")
}
})
})

练习 事件 图片跟随

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$(function(){
$("#small").bind("mouseover mouseout mousemove",function (event) {
if (event.type=="mouseover"){
$("#showBig").show()
}else if (event.type=="mouseout"){
$("#showBig").hide()
}else if (event.type=="mousemove") {
$("#showBig").offset({//pageX和 pageY都是 event的属性
left: event.pageX+10,
top:event.pageY+10
});
}
})
});

xml

(其他详见笔记)

xml解析技术介绍

第三方的解析:

jdom 在 dom 基础上进行了封装 、

dom4j 又对 jdom 进行了封装。

pull 主要用在 Android 手机开发,是在跟 sax 非常类似都是事件机制解析 xml 文件

这个 Dom4j 它是第三方的解析技术

我们需要使用第三方给我们提供好的类库才可以解析 xml 文件。

dom4j解析技术(!!!)

需要分四步操作:
第一步,通过创建 SAXReader 对象。来读取 xml 文件,获取 Document 对象
第二步,通过 Document 对象。拿到 XML 的根元素对象
第三步,通过根元素对象。获取所有的 book 标签对象
第四步,遍历每个 book 标签对象。然后获取到 book 标签对象内的每一个元素,再通过 getText() 方法拿到起始标签和结
束标签之间的文本内容/elementText()方法可以直接获得指定标签文本内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.izumi.pojo;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.junit.Test;

import java.util.List;

public class Dom4jTest {
@Test
public void test1() throws DocumentException {
//创建一个SaxReader输入流,去读取xml配置文件,获得document对象
SAXReader saxReader = new SAXReader();
Document document = saxReader.read("src/books.xml");
System.out.println(document);
}
@Test
public void test2() throws DocumentException {
//创建一个SaxReader输入流,去读取xml配置文件,获得document对象
SAXReader saxReader = new SAXReader();
//再JUnit测试中,相对路径是从模块名开始算
//获得 document对象
Document document = saxReader.read("src/books.xml");
//通过 document对象获取根元素
Element rootElement = document.getRootElement();
//element()和 elements()都是通过标签名查找子元素
List<Element> books = rootElement.elements("book");
//遍历,处理每个book标签转化为Book类
for (Element book : books) {
//asXml()方法把标签对象转化为标签字符串
// System.out.println(book.asXML());
Element nameElement = book.element("name");
//System.out.println(nameElement.asXML());
//getTest()可以得到标签文本内容
String name = nameElement.getText();
//System.out.println(text);
//elementText()方法可以直接获得指定标签文本内容
String author = book.elementText("author");
String price = book.elementText("price");
//attributeValue()可以获得标签属性的值
String id = book.attributeValue("id");
Book book1 = new Book(id, name, author, Double.parseDouble(price));
System.out.println(book1);
}
}

}

JavaWeb

JavaWeb基本介绍

什么是JavaWeb

JavaWeb 是指,所有通过 Java 语言编写可以通过浏览器访问的程序的总称,叫 JavaWeb。

JavaWeb 是基于请求和响应来开发的。**

什么是请求

请求是指客户端给服务端发送数据,叫请求request

什么是响应

响应是指服务端给客户端回传数据,叫响应response

请求和响应的关系

请求和响应是成对出现的,有请求就有响应。

image-20220130110032735

Web资源的分类

web 资源按实现的技术和呈现的效果的不同,又分为静态资源和动态资源两种。

静态资源: html、css、js、txt、mp4 视频 , jpg 图片

动态资源: jsp 页面、Servlet 程序

常见的Web服务器

  • Tomcat:由 Apache 组织提供的一种 Web 服务器,提供对 jsp 和 Servlet 的支持。它是一种轻量级的 javaWeb 容器(服务器),也是当前应用最广的 JavaWeb 服务器(免费)。

  • Jboss:是一个遵从 JavaEE 规范的、开放源代码的、纯 Java 的 EJB 服务器,它支持所有的 JavaEE 规范(免费)。

  • GlassFish: 由 Oracle 公司开发的一款 JavaWeb 服务器,是一款强健的商业服务器,达到产品级质量(应用很少)。

  • Resin:是 CAUCHO 公司的产品,是一个非常流行的服务器,对 servlet 和 JSP 提供了良好的支持,
    性能也比较优良,resin 自身采用 JAVA 语言开发(收费,应用比较多)。

  • WebLogic:是 Oracle 公司的产品,是目前应用最广泛的 Web 服务器,支持 JavaEE 规范,
    而且不断的完善以适应新的开发要求,适合大型项目(收费,用的不多,适合大公司)。

TomCat服务器和Servlet版本的对应关系

当前企业常用的版本 7.*8.*

Servlet 程序从 2.5 版本是现在世面使用最多的版本(xml 配置)

到了 Servlet3.0 之后。就是注解版本的 Servlet 使用。 以 2.5 版本为主线讲解 Servlet 程序

TomCat的使用

目录介绍

bin 专门用来存放 Tomcat 服务器的可执行程序
conf 专门用来存放 Tocmat 服务器的配置文件
lib 专门用来存放 Tomcat 服务器的 jar 包
logs 专门用来存放 Tomcat 服务器运行时输出的日记信息
temp 专门用来存放 Tomcdat 运行时产生的临时数据
webapps 专门用来存放部署的 Web 工程。
work 是 Tomcat 工作时的目录,用来存放 Tomcat 运行时 jsp 翻译为 Servlet 的源码,和 Session 钝化的目录。

如何启动TomCat

找到 Tomcat 目录下的 bin 目录下的 startup.bat 文件,双击,就可以启动 Tomcat 服务器。

如何测试 Tomcat 服务器启动成功???

打开浏览器,在浏览器地址栏中输入以下地址测试:

1、http://localhost:8080

2、http://127.0.0.1:8080

3、http://真实 ip:8080

当出现如下界面,说明 Tomcat 服务器启动

常见的启动失败原因分析

常见的启动失败的情况有,双击 startup.bat 文件,就会出现一个小黑窗口一闪而来。

这个时候,失败的原因基本上都是因为没有配置好 JAVA_HOME 环境变量

另一种启动tomcat服务器的方法

1、打开命令行

2、cd 到 你的 Tomcat 的 bin 目录下

3、敲入启动命令: catalina run

并且如果启动失败,控制台会提示错误原因

Tomcat的停止

1、点击 tomcat 服务器窗口的 x 关闭按钮

2、把 Tomcat 服务器窗口置为当前窗口,然后按快捷键 Ctrl+C

3、找到 Tomcat 的 bin 目录下的 shutdown.bat 双击,就可以停止 Tomcat 服务器

如何修改tomcat的服务器端口号

Mysql 默认的端口号是:3306

Tomcat 默认的端口号是:8080

找到 Tomcat 目录下的 conf 目录,找到 server.xml 配置

image-20211117101922161-

平时上百度:http://www.baidu.com:80

HTTP 协议默认的端口号是:80端口会默认隐藏

如何部署web工程到Tomcat中

第一种部署方法:

只需要把 web 工程的目录拷贝到 Tomcat 的 webapps 目录下 即可。

第二种部署方法

找到 Tomcat 下的 conf 目录\Catalina\localhost\ 下,创建如下的配置文件:

1
2
3
4
5
<!-- Context 表示一个工程上下文
path 表示工程的访问路径:/abc 不要用中文
docBase 表示你的工程目录在哪里
-->
<Context path="/abc" docBase="E:\book" />

访问这个工程的路径如下:http://ip:port/abc/ 就表示访问 E:\book 目录

手托 html 页面到浏览器和在浏览器中输入 http://ip:端口号/工程名/访问的区别

  • 手托html页面的原理:

    • image-20211117103856370-
  • 输入访问地址访问的原理:

    • image-20211117104008444-

ROOT 的工程的访问,以及 默认 index.html 页面的访 问

  • 当我们在浏览器地址栏中输入访问地址如下

    http://ip:port/ ====>>>> 没有工程名的时候,默认访问的是 ROOT 工程。

  • 当我们在浏览器地址栏中输入的访问地址如下:

    http://ip:port/工程名/ ====>>>> 没有资源名,默认访问 index.html

Idea整合Tomcat服务器

操作的菜单如下:File | Settings | Build, Execution, Deployment | Application Servers

image-20211117105045539-

IDEA 中动态 web 工程的操作

image-20211117113525185-

web工程目录介绍

image-20211117114146158

如何给动态 web 工程添加额外 jar 包

除了常见的那种 右键 add as libraries方法,还有以下方法:

1、可以打开项目结构菜单操作界面,添加一个自己的类库:

image-20211117124113444

2、添加你你类库需要的 jar 包文件。

image-20211117124440089-

3、选择你添加的类库,给哪个模块使用:

4、选择 Artifacts 选项,将类库,添加到打包部署中:

image-20211117124729994-

如何在 IDEA 中部署工程到 Tomcat 上运行

1、建议修改 web 工程对应的 Tomcat 运行实例名称:

​ 你还可以修改你的 Tomcat 实例启动后默认的访问地址:

image-20211117130306228-

2、确认你的 Tomcat 实例中有你要部署运行的 web 工程模块

image-20211117130435481-

3.重启tomcat实例

image-20211117130653089-

修改工程访问路径

image-20211117131230250-

修改运行的端口号

image-20211117131419956-

修改运行使用的浏览器

image-20211117131452730-

配置资源热部署

image-20211117131520450-

Servlet

Servlet技术

什么是Servlet

1、Servlet 是 JavaEE 规范之一。规范就是接口

2、Servlet 是 JavaWeb 三大组件之一。三大组件分别是:

​ Servlet 程序、Filter 过滤器、Listener 监听器。

3、Servlet 是运行在服务器上的一个 java 小程序,

​ 它可以接收客户端发送过来的请求,并响应数据给客户端。

手动实现 Servlet 程序

1、编写一个类去实现 Servlet 接口

2、实现 service 方法,处理请求,并响应数据

3、到 web.xml 中去配置 servlet 程序的访问地址

  • 编写一个类实现Servlet接口并实现service方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class HelloServlet  implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {

}

@Override
public ServletConfig getServletConfig() {
return null;
}

//service方法是专门用来处理请求和响应的
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("HelloServlet被执行...");
}

@Override
public String getServletInfo() {
return null;
}

@Override
public void destroy() {

}
}
  • 到 web.xml 中去配置 servlet 程序的访问地址
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">

<!-- servlet标签给Tomcat配置Servlet程序-->
<servlet>
<!-- servlet-name标签 给Servlet程序起一个别名(一般用类名)-->
<servlet-name>HelloServlet</servlet-name>
<!-- servlet-class是Servlet的全类名 -->
<servlet-class>com.izumi.servlet.HelloServlet</servlet-class>
</servlet>
<!-- servlet-mapping标签 给servlet程序配置访问地址 -->
<servlet-mapping>
<!-- servlet-name 标签是告诉 服务器,我当前配置的地址给哪个servlet程序使用-->
<servlet-name>HelloServlet</servlet-name>
<!-- url-pattern 标签配置访问地址
/ 表示在服务器解析的时候,表示地址为: http://ip:port//工程路径
/hello表示地址为 http://ip:port/工程路径/hello-->
<url-pattern>/hello</url-pattern>
</servlet-mapping>

</web-app>
常见错误
  • 常见的错误 1:url-pattern 中配置的路径没有以斜杠打头。

image-20211117185353788-

  • 常见错误 2:servlet-name 配置的值不存在:(这种错误在idea中,编译器会发现)

image-20211117185422173-

  • 常见错误 3:servlet-class 标签的全类名配置错误:(这种错误在idea中,编译器会发现)

image-20211117185549494-

url 地址到 Servlet 程序的访问(原理图)

image-20211117190049749-

Servlet的生命周期

1、执行 Servlet 构造器方法

2、执行 init 初始化方法

​ 第一、二步,是在第一次访问,的时候创建 Servlet 程序会调用。

3、执行 service 方法

​ 第三步,每次访问都会调用。

4、执行 destroy 销毁方法

​ 第四步,在 web 工程停止的时候调用

GET和POST请求的分发处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
//将ServletRequest接口类型向下转型为 HttpRequest接口类型调用 getMethod方法
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String method = httpServletRequest.getMethod();
System.out.println("3.HelloServlet被执行...");
if ("POST".equals(method)){
doPost();
}else if ("GET".equals(method)) {
doGet();
}
}
public void doPost(){
System.out.println("post请求");
}
public void doGet(){
System.out.println("get请求");

}

通过继承 HttpServlet 实现 Servlet 程序

一般在实际项目开发中,都是使用继承 HttpServlet 类的方式去实现 Servlet 程序。

1、编写一个类去继承 HttpServlet 类

2、根据业务需要重写 doGet 或 doPost 方法

3、到 web.xml 中的配置 Servlet 程序的访问地址

  • 新创建一个 HelloServlet02,继承 HttpServlet类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.izumi.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class HelloServlet02 extends HttpServlet {

//在get请求的时候调用
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("com.izumi.servlet.HelloServlet02 doGet方法被调用");
}

//在post请求的时候调用
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("com.izumi.servlet.HelloServlet02 doPost方法被调用");

}
}

  • 到 web.xml 中的配置 Servlet 程序的访问地址
1
2
3
4
5
6
7
8
<servlet>
<servlet-name>HelloServlet02</servlet-name>
<servlet-class>com.izumi.servlet.HelloServlet02</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet02</servlet-name>
<url-pattern>/hello02</url-pattern>
</servlet-mapping>

使用Idea创建servlet程序(方便快捷)

菜单:new ->Servlet 程序

image-20211117203747471-

配置servlet信息

image-20211117204204218-

如果new没有 servle的解决方案

2.1.将src标记成Sources文件

image-20211117203928687

2.2.在facets配置中的sources root的,将src勾选上

image-20211117204033796-

Servlet 类的继承体系

简单的源码分析(129p)

image-20211117205718599

ServletConfig类

ServletConfig 类从类名上来看,就知道是 Servlet 程序的配置信息类。

Servlet 程序和 ServletConfig 对象都是由 Tomcat 负责创建,我们负责使用。

Servlet 程序默认是第一次访问的时候创建,ServletConfig 是每个 Servlet 程序创建时,

就创建一个对应的 ServletConfig 对 象。

ServletConfig的三大作用

1、可以获取 Servlet 程序的别名 servlet-name 的值

2、获取初始化参数 init-param (需要到web.xml里面增添相关配置信息)

3、获取 ServletContext 对象

  • web.xml配置

    在实现servlet程序的类中的 <servlet> 标签加入以下标签

1
2
3
4
5
6
7
8
9
10
11
<!--        ini-param 是初始化参数 可以配置多个-->
<init-param>
<!-- 是参数名-->
<param-name>userName</param-name>
<!-- 是参数值-->
<param-value>root</param-value>
</init-param>
<init-param>
<param-name>url</param-name>
<param-value>jdbc:mysql//localhost:3306/test</param-value>
</init-param>
  • servlet中的代码
1
2
3
4
5
6
7
8
9
10
11
12
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("2. init方法被调用");
// 1、可以获取 Servlet 程序的别名 servlet-name 的值
System.out.println("HelloServlet的别名是:"+servletConfig.getServletName());
// 2、获取初始化参数 init-param
System.out.println("HelloServlet的初始化参数 userName:"+servletConfig.getInitParameter("userName"));
System.out.println("HelloServlet的初始化参数url:"+servletConfig.getInitParameter("url"));
// 3、获取 ServletContext 对象
System.out.println("获取 ServletContext对象:"+servletConfig.getServletContext());

}

ServletConfig的一些细节:

  • 继承 HttpServlet 的 servlet类:

    可以在其他位置使用 getServletConfig()方法获得 ServletConfig对象

    (实际上调用的是 GenericServlet getServletConfig()方法)

  • 重写 init方法里面一定要调用父类的init操作

image-20211117223746491-

​ 因为 该方法在 HttpServlet 的父类 GennericServlet中有定义,

​ 这里重写不调用父类的init方法就会丢失 config造成空指针异常

1
2
3
4
5
//GennericServlet类的 init方法
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}

ServletContext 类

什么是ServletContext?

1、ServletContext 是一个接口,它表示 Servlet 上下文对象

2、一个 web 工程,只有一个 ServletContext 对象实例。

3、ServletContext 对象是一个域对象*。

4、ServletContext 是在 web 工程部署启动的时候创建。在 web 工程停止的时候销毁

什么是域对象?

域对象,是可以像 Map 一样存取数据的对象,叫域对象。

这里的域指的是存取数据的操作范围,整个 web 工程。

存数据取数据删除数据
mapput()get()remove()
域对象putAttribute()getAttribute()removeAttribute()

ServletContext的四大作用

​ ServletContext对象 (可以通过getServletContext方法直接获取到)

1、获取 web.xml 中配置的上下文参数 context-param

​ 调用方法 getInitParameter()

1
2
3
4
5
6
7
8
9
<!--    context-param是上下文参数(它属于整个web工程)-->
<context-param>
<param-name>username</param-name>
<param-value>root</param-value>
</context-param>
<context-param>
<param-name>url</param-name>
<param-value>www.baidu.com</param-value>
</context-param>

2、获取当前的工程路径 格式: /工程路径

​ 调用方法getContextPath()

3、获取工程部署后在服务器硬盘上的绝对路径 ServletContext对象调用方法getRealtPath()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    1、获取 web.xml 中配置的上下文参数 context-param
ServletContext servletContext = getServletConfig().getServletContext();
System.out.println("获取context-param: "+servletContext.getInitParameter("username"));
System.out.println("获取context-param: "+servletContext.getInitParameter("url"));
// 2、获取当前的工程路径,格式: /工程路径
System.out.println("获取当前工程的路径:"+servletContext.getContextPath()); // 得到 /servlet

// 3、获取工程部署后在服务器硬盘上的绝对路径
//得到F:\IdeaProjects\JavaWeb\out\artifacts\servelet_war_exploded\
// 重启tomcat服务可以看到idea 拷贝的tomcat的数据:
// C:\Users\Pixar\AppData\Local\JetBrains\IntelliJIdea2020.1\tomcat\Tomcat_8_0_50_JavaWeb_2
/*
/ / 斜杠被服务器解析地址为:http://ip:port/工程名/ 映射到 IDEA 代码的web目录
*/
System.out.println("获取工程部署后在硬盘的绝对路径:"+servletContext.getRealPath("/"));
System.out.println("获取工程部署后css在硬盘的绝对路径:"+servletContext.getRealPath("/css"));
System.out.println("获取工程部署后imgs在硬盘的绝对路径:"+servletContext.getRealPath("/imgs"));

4、像 Map 一样存取数据

1
2
3
4
5
6
//4、像 Map 一样存取数据
servletContext.setAttribute("id","value1");
System.out.println("key为 id的 value= "+ servletContext.getAttribute("id"));
System.out.println("key为 id的 value= "+ servletContext.getAttribute("id"));
System.out.println("key为 id的 value= "+ servletContext.getAttribute("id"));
System.out.println("key为 id的 value= "+ servletContext.getAttribute("id"));
1
2
3
4
5
6
7
8
9
public class ServletContext02 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext servletContext = getServletContext();
System.out.println(servletContext.getAttribute("id"));
//因为attribute数据是域对象,是web工程下所有servlet程序共同拥有,而存数据的操作在 ServletContext中,所以必须先运行 ServletContext,才能获取到数据

}
}

关于idea部署Servlet的一些细节

通过 ContextServlet对象的getRealPath得到的真实路径地址:

1
2
3
  // idea 拷贝的tomcat的数据:(重启tomcat服务可以看到)
/* C:\Users\Pixar\AppData\Local\JetBrains\IntelliJIdea2020.1\tomcat\Tomcat_8_0_50_JavaWeb_2
*/

再进入到 conf\Catalina\localhost下可以看到xml配置文件,里面记录的信息如下:

可以看到,idea的tomcat服务配置是上面介绍的 如何将web工程部署到tomcat服务器的第二种方法:

<Context path="/servlet" docBase="F:\IdeaProjects\JavaWeb\out\artifacts\servelet_war_exploded" />

HTTP协议

什么是HTTTP协议

什么是协议?

协议就是指双方,或多方,相互约定,大家都要遵守的规则,就叫协议

HTTP协议:

就是指 服务端和客户端之间通行时,发送的数据,需要遵守的规则,就叫HTTP协议。

HTTP协议中的数据又叫报文

请求的HTTP协议

客户端给服务器发送数据叫请求。

服务器给客户端回传数据叫响应。

请求又分为 GET 请求,和 POST 请求两种

GET请求

1、请求行

​ (1) 请求的方式 GET

​ (2) 请求的资源路径[+?+请求参数]

​ (3) 请求的协议的版本号 HTTP/1.1

2、请求头

​ key : value 组成 不同的键值对,表示不同的含义

图解:

image-20211118124329257
Post请求

1、请求行

​ (1) 请求的方式 POST

​ (2) 请求的资源路径[+?+请求参数]

​ (3) 请求的协议的版本号 HTTP/1.1

2、请求头

​ key : value 组成 不同的键值对,表示不同的含义

3、空行

​ 请求体 ===>>> 就是发送给服务器的数据

image-20211118125147592

常见的请求头说明

​ Accept: 表示客户端可以接收的数据类型

​ Accpet-Languege: 表示客户端可以接收的语言类型

​ User-Agent: 表示客户端浏览器的信息

​ Host: 表示请求时的服务器 ip 和端口号

哪些是 GET 请求,哪些是 POST 请求

​ GET 请求有哪些:

​ 1、form 标签 method=get

​ 2、a 标签

​ 3、link 标签引入 css

                 4、Script 标签引入 js 文件 

​ 5、img 标签引入图片

​ 6、iframe 引入 html 页面

​ 7、在浏览器地址栏中输入地址后敲回车

POST 请求有哪些:

​ 8、form 标签 method=post

响应的HTTP协议格式

1、响应行

(1) 响应的协议和版本号

(2) 响应状态码

(3) 响应状态描述符

2、响应头

(1) key : value 不同的响应头,有其不同含义

—空行 –

7

3、响应体 —->>> 就是回传给客户端的数据

image-20211118130408184
常用的响应码说明

​ 200 表示请求成功

​ 302 表示请求重定向

​ 404 表示请求服务器已经收到了,但是你要的数据不存在(请求地址错误)

​ 500 表示服务器已经收到请求,但是服务器内部错误(代码错误)

MIME类型说明

MIME 是 HTTP 协议中数据类型。

MIME 的英文全称是”Multipurpose Internet Mail Extensions” 多功能 Internet 邮件扩充服务。

MIME 类型的格式是“大类型/小 类型”,并与某一种文件的扩展名相对应。

  • 常见的MIME类型

image-20211118131334873

  • 谷歌浏览器查看http协议

image-20211118131715384-

HttpServletRequest类

HttpServletRequest有什么用

每次只要有请求进入 Tomcat 服务器,Tomcat 服务器就会把请求过来的 HTTP 协议信息

解析好封装到 Request 对象中。 然后传递到 service 方法(doGet 和 doPost)

中给我们使用。我们可以通过 HttpServletRequest 对象,获取到所有请求的 信息

HttpServletRequest类常用方法

1
2
3
4
5
6
7
8
9
10
i. getRequestURI() 				获取请求的资源路径
ii. getRequestURL() 获取请求的统一资源定位符(绝对路径)
iii. getRemoteHost() 获取客户端的 ip 地址
iv. getHeader() 获取请求头
v. getParameter() 获取请求的参数
vi. getParameterValues() 获取请求的参数(多个值的时候使用)
vii. getMethod() 获取请求的方式 GET 或 POST
viii. setAttribute(key, value); 设置域数据
ix. getAttribute(key); 获取域数据
x. getRequestDispatcher() 获取请求转发对象

常用 API 示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// i. getRequestURI() 获取请求的资源路径
System.out.println("请求的资源路径是:"+request.getRequestURI());
// ii. getRequestURL() 获取请求的统一资源定位符(绝对路径)
System.out.println("获取请求的统一资源定位符(绝对路径:"+request.getRequestURL());
// iii. getRemoteHost() 获取客户端的 ip 地址
// //使用 localhost 去访问,得到的客户端IP地址是: 127.0.0.1
// //使用 127.0.0.1 去访问,得到的客户端IP地址是: 127.0.0.1
// //使用 ip地址(10.184.7.190) 去访问,得到的客户端IP地址是: 10.184.7.190
System.out.println("获取客户端的ip地址:"+request.getRemoteHost());
// iv. getHeader() 获取请求头
System.out.println("获取请求头:User-Agent"+request.getHeader("User-Agent"));
// getMethod() 获取请求的方式 GET 或 POST
System.out.println("获取请求参数:"+request.getMethod());

}

/*输出结果
请求的资源路径是:/servlet/request
获取请求的统一资源定位符(绝对路径:http://localhost:8080/servlet/request
获取客户端的ip地址:127.0.0.1
获取请求头:User-AgentMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36
获取请求参数:GET
*/

如何获取请求参数

1
2
3
4
5
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("获得表单用户名:"+request.getParameter("username"));
System.out.println("获得表单密码:"+request.getParameter("pwd"));
System.out.println("获得表单爱好:"+ Arrays.toString(request.getParameterValues("hobby")) );
}

Post请求只需要注意:

1
2
3
// 若请求内容有中文 会出现乱码
// 所以需要设置字符集编码 注意:该方法需要在获取参数请求之前调用才有效
request.setCharacterEncoding("UTF-8");

请求的转发

什么是请求的转发?

请求转发是指,服务器收到请求后,

从一次资源跳转到另一个资源的操作叫请求转发。

不可以访问工程以外的资源

image-20211118203011852

代码演示:

servlet1代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取请求的参数(办事的材料)查看
String username = request.getParameter("username");
System.out.println("在 servlet1查看参数(材料):"+username);

//(给材料盖章),传到servlet2
request.setAttribute("key1","servlet1的盖章");

//请求转发 给 servlet2
// 问路:Servlet2(柜台 2)怎么走
/**
* 请求转发必须要以斜杠打头,/ 斜杠表示地址为:http://ip:port/工程名/ ,
* 映射到 IDEA 代码的 web 目录
*
*/

// RequestDispatcher requestDispatcher = request.getRequestDispatcher("/servlet2");
//可以转发到 WEB-INF目录下
RequestDispatcher requestDispatcher = request.getRequestDispatcher("/WEB-INF/form.html");
//// 走向 Sevlet2(柜台 2)
requestDispatcher.forward(request,response);
//不能跳转到外部地址 原因:采用该方法跳转的地址是: //http://localhost:8080/servlet/www.baidu.com
// request.getRequestDispatcher("www.baidu.com").forward(request,response);
}

servlet2代码

1
2
3
4
5
6
7
8
9
10
11
12
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//来到 servlet2
// 获取请求的参数(办事的材料)查看
System.out.println("在 servlet2中查看参数:(username)"+request.getParameter("username"));

//查看 servlet 1 是否有盖章 attribute
System.out.println("查看 servlet 1 是否有盖章 :"+request.getAttribute("key1"));

//处理自己的业务
System.out.println("servlet2处理自己的业务");

}

base标签的作用

base标签可以设置当前页面中所有相对路径工作时,参照哪个路径来进行跳转

ForwardC代码

1
2
3
4
5
 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("经过 ForwardC~~~~");
request.getRequestDispatcher("/a/b/c.html").forward(request,response);
}
}

image-20211118222513160

image-20211118222308452-

Web中的相对路径和绝对路径

在javaweb中,路径分为相对路径和绝对路径

  • 相对路径:

    • . 表示当前目录
    • .. 表示上一级目录
    • 资源名: 表示当前目录/资源名
  • 绝对路径

    • http//localhost:8080/工程路径/资源路径
  • 在实际开发中,路径都使用绝对路径,而不简单的使用相对路径。

    1、绝对路径 2、base+相对

Web中斜杠(/)的不同意义

在 web 中 / 斜杠 是一种绝对路径。

/ 斜杠 如果被浏览器解析,得到的地址是:http://ip:port/

<a href="/">斜杠</a>

/ 斜杠 如果被服务器解析,得到的地址是:http://ip:port/工程路径

<url-pattern>/forwardC</url-pattern>

servletRequest.getRealPath("/")

request.getRequestDispatcher("/")

特殊情况:response.sendRediect(“/”); 把斜杠发送给浏览器解析。得到 http://ip:port

HttpServletResponse类

HttpServletResponse的作用

HttpServletResponse 类和 HttpServletRequest 类一样。每次请求进来,

Tomcat 服务器都会创建一个 Response 对象传 递给 Servlet 程序去使用。
HttpServletRequest 表示请求过来的信息,HttpServletResponse

表示所有响应的信息, 我们如果需要设置返回给客户端的信息,

都可以通过HttpServletResponse 对象来进行设置

两个输出流的说明

  • 字节流
    • 通过 getOutputStream() 获取 常用于下载(传递二进制数据)
  • 字符流
    • 通过 getWriter() 获取 常用于回传字符串(常用)

两个流同时只能使用一个。 使用了字节流,就不能再使用字符流,反之亦然,否则就会报错。

image-20211120145954071-

如何往客户端回传数据

1
2
3
4
5
6
7
8
public class ResponseIOServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException {
// 要求 : 往客户端回传 字符串 数据。
PrintWriter writer = resp.getWriter();
writer.write("response's content!!!");
}
}

响应乱码解决

  • 方案一(不推荐使用)

    • ```java

    // 设置服务器字符集为 UTF-8
    resp.setCharacterEncoding(“UTF-8”);
    // 通过响应头,设置浏览器也使用 UTF-8 字符集
    resp.setHeader(“Content-Type”, “text/html;

    1
    2
    3
    4
    5
    6
    7

    - **方案二(推荐使用)**

    - ```java
    // 它会同时设置服务器和客户端都使用 UTF-8 字符集,还设置了响应头
    // 此方法一定要在获取流对象之前调用才有效
    resp.setContentType("text/html; charset=UTF-8");

请求重定向

请求重定向,是指客户端给服务器发请求,然后服务器告诉客户端说。我给你一些地址。

你去新地址访问。叫请求 重定向(因为之前的地址可能已经被废弃)。

image-20211120151105871

请求重定向的特点:

1、浏览器地址栏会发生变化
2、两次请求
3、不共享Request域中数据
4、不能访问WEB-INF下的资源
5、可以访问工程外的资源**

  • 请求重定向方式一:

    • ```java
      //设置请求重定向
      //1.设置响应状态码 为302
      response.setStatus(302);
      //2.设置响应头,说明新的地址在哪里
      response.setHeader(“Location”,”http://localhost:8080/servlet02/response2“);
      1
      2
      3
      4
      5
      6

      - **请求重定向方式二:**

      - ```java
      //请求重定向方式二(推荐使用)
      response.sendRedirect("http://localhost:8080/servlet02/response2");

关于中文乱码的解决总结

request的post请求容易出现乱码:

1
2
3
// 若请求内容有中文 会出现乱码
// 所以需要设置字符集编码 注意:该方法需要在获取参数请求之前调用才有效
request.setCharacterEncoding("UTF-8");

response的响应出现乱码:两种方案:

1、

1
2
3
4
// 设置服务器字符集为 UTF-8
resp.setCharacterEncoding("UTF-8");
// 通过响应头,设置浏览器也使用 UTF-8 字符集
resp.setHeader("Content-Type", "text/html;

2、推荐使用:

1
2
3
// 它会同时设置服务器和客户端都使用 UTF-8 字符集,还设置了响应头
// 此方法一定要在获取流对象之前调用才有效
resp.setContentType("text/html; charset=UTF-8");

书城项目–第二阶段

目标

  • 需求 1:用户注册
    需求如下:
    1)访问注册页面
    2)填写注册信息,提交给服务器
    3)服务器应该保存用户
    4)当用户已经存在—-提示用户注册 失败,用户名已存在
    5)当用户不存在—–注册成功

  • 需求 2:用户登陆
    需求如下:
    1)访问登陆页面
    2)填写用户名密码后提交
    3)服务器判断用户是否存在
    4)如果登陆失败 —>>>> 返回用户名或者密码错误信息
    5)如果登录成功 —>>>> 返回登陆成功 信息

JavaEE的三层架构

image-20211120174608782

分层的目的是为了解耦。解耦就是为了降低代码的耦合度。方便项目后期的维护和升级。

web 层 com.izumi.web/servlet/controller
service 层 com.izumi.service Service接口包
com.izumi.service.impl Service 接口实现类
dao 持久层 com.izumi.dao Dao 接口包
com.izumi.dao.impl Dao 接口实现类
实体 bean 对象 com.izumi.pojo/entity/domain/bean JavaBean 类
测试包 com.izumi.test/junit
工具类 com.izumi.utils

1、创建书城项目数据库所需要的表

1
2
3
4
5
6
7
8
9
10
11
CREATE DATABASE book;

CREATE TABLE t_user(
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(20) NOT NULL UNIQUE,
`password` VARCHAR(32) NOT NULL,
email VARCHAR(200));

INSERT INTO t_user(username,`password`,email) VALUES('admin','admin','admin@163.com');

SELECT * FROM t_user;

2、编写数据库对应的JavaBean

1
2
3
4
5
6
7
public class User {
private Integer id;
private String username;
private String password;
private String email;
...
}

3、编写工具类JdbcUtils

导入需要的jar包

​ druid-1.1.9.jar

​ mysql-connector-java-5.1.7-bin.jar

在 src 源码目录下编写 jdbc.properties 属性配置文件:

1
2
3
4
5
6
username=root
password=zzt
url=jdbc:mysql://localhost:3306/book
driverClassName=com.mysql.jdbc.Driver
initialSize=5
maxActive=10

编写 JdbcUtils类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class JdbcUtils {
private static DataSource dataSource;
static {
Properties properties = new Properties();
try {
//通过反射获取连接properties路径
InputStream loadStream = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
properties.load(loadStream);
//获取连接
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}


//获取数据库连接
//如果返回null,说明获取连接失败
public static Connection getConnection(){
Connection connection = null;
try {
connection = dataSource.getConnection();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return connection;

}
//关闭数据库连接
public static void close(Connection connection){
if (connection != null){
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}

}

JdbcUtils测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.izumi.test;

import com.izumi.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.SQLException;
//测试 JdbcUtils
public class JdbcUtilsTest {
public static void main(String[] args) throws SQLException {
for (int i = 0; i < 100; i++) {
Connection connection = JdbcUtils.getConnection();
System.out.println(connection);
connection.close();
}
}
}

编写BaseDao

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
public abstract class BasicDao {
private QueryRunner queryRunner = new QueryRunner();

/**
* 实现增改删
* @param sql 传入 的 update sql语句
* @param args sql对应的参数值
* @return 返回-1说明失败,返回其他值即表示影响的行数
*/
public int update(String sql,Object...args){
Connection connection = JdbcUtils.getConnection();
try {
return queryRunner.update(connection,sql,args);
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JdbcUtils.close(connection);
}
return -1;
}

/**
* 查询返回一个JavaBean的 sql
* @param type 返回的对象类型
* @param sql 执行的sql语句
* @param args sql对应的参数值
* @param <T> 返回的类型的泛型
* @return
*/
public <T> T queryForOne(Class<T> type,String sql,Object...args){
Connection connection = JdbcUtils.getConnection();
try {
return queryRunner.query(connection,sql,new BeanHandler<T>(type),args);
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JdbcUtils.close(connection);
}
return null;
}

/**
* 查询返回多个JavaBean的 sql
* @param type 返回的对象类型
* @param sql 执行的sql语句
* @param args sql对应的参数值
* @param <T> 返回的类型的泛型
* @return
*/
public <T> List<T> queryForList(Class<T> type, String sql, Object...args){
Connection connection = JdbcUtils.getConnection();
try {
return queryRunner.query(connection,sql,new BeanListHandler<T>(type),args);
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JdbcUtils.close(connection);
}
return null;
}

/**
* 执行返回一行一列的 查询 语句
* @param sql
* @param args
* @return
*/
public Object queryForSingValue(String sql,Object...args){
Connection connection = JdbcUtils.getConnection();
try {
return queryRunner.query(connection,sql,new ScalarHandler(),args);
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JdbcUtils.close(connection);
}
return null;
}


}

编写UserDao和测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class UserDaoImpl extends BasicDao implements UserDao {


@Override
public User queryUserByUsername(String username) {
String sql = "select * from t_user where username = ?";
return queryForOne(User.class,sql,username);
}

@Override
public User queryUserByUsernameAndPwd(String username, String password) {
String sql = "select * from t_user where username = ? and password = ?";
return queryForOne(User.class,sql,username,password);
}

@Override
public int saveUser(User user) {
String sql = "insert into t_user (username,password,email) values(?,?,?);";
return update(sql,user.getUsername(),user.getPassword(),user.getEmail());
}
}

编写UserService和测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class UserServiceImpl implements UserService {
private UserDao userDao = new UserDaoImpl();
@Override
public void registerUser(User user) {
userDao.saveUser(user);
}

@Override
public User login(User user) {
return userDao.queryUserByUsernameAndPwd(user.getUsername(),user.getPassword());
}

@Override
public boolean exitsUsername(String username) {
User user = userDao.queryUserByUsername(username);
if (user==null){
//说明没有查到,用户名不存在,即可以使用
return false;
}
return true;
}
}

编写web层

实现用户注册的功能

  • 图解分析:

image-20211120214837725

修改 regist.html 和 regist_success.html 页面

1、添加 base 标签

1
2
<!--    base标签,永远固定base标签的跳转结果  一般写到 工程路径-->
<base href="http://localhost:8080/book/">

2、修改 base 标签对页面中所有相对路径的影响(浏览器 F12,哪个报红,改哪个)

​ 以下是几个修改示例

1
2
<link type="text/css" rel="stylesheet" href="static/css/style.css">
<script type="text/javascript" src="static/script/jquery-1.7.2.js"></script>

3、修改注册表单的提交地址和请求方式

<form action="registerServlet" method="post">

用户登录功能的实现

  • 图解用户登录

image-20211120223908767

修改 login.html 页面和 login_success.html 页面

和修改 regist.html 和 regist_success.html 页面类似,这里不再赘述

修改 login.html 表单的 提交地址 和 请求方式

<form action="loginServlet" method="post">

LoginServlet程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class LoginServlet extends HttpServlet {
UserService userService = new UserServiceImpl();
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1、获取请求的参数
String username = request.getParameter("username");
String password = request.getParameter("password");
// 2、调用XxxService.xxx0处理业务
// userService.login()登录
// 3、根据login()方法返回结果判断登最是否成功
// 成功
// 跳到成功页面loginsuccess. html
User loginUser = userService.login(new User(null, username, password, null));
if(loginUser==null){
System.out.println("登陆失败");
// 失败
// 跳回登录页面
System.out.println("用户名密码错误!!!");
request.getRequestDispatcher("/pages/user/login.html").forward(request,response);
}else {
//登录成功,跳转到成功页面
request.getRequestDispatcher("/pages/user/login_success.html").forward(request,response);
}


}

}

jsp

什么是jsp,它有什么用?

jsp 的全换是 java server pages。Java 的服务器页面。

jsp 的主要作用是代替 Servlet 程序回传 html 页面的数据。

因为 Servlet 程序回传 html 页面数据是一件非常繁锁的事情。开发成本和维护成本都极高。

2、jsp 如何访问:

jsp 页面和 html 页面一样,都是存放在 web 目录下。访问也跟访问 html 页面一样。

比如: 在 web 目录下有如下的文件:

web 目录

a.html 页面 访问地址是 =======>>>>>> http://ip:port/工程路径/a.html

b.jsp 页面 访问地址是 =======>>>>>> http://ip:port/工程路径/b.jsp

jsp的本质是什么

jsp 页面本质上是一个 Servlet 程序。 当我们第一次访问 jsp 页面的时候。

Tomcat 服务器会帮我们把 jsp 页面翻译成为一个 java 源文件。

并且对它进行编译成 为.class 字节码程序。我们打开 java 源文件不难发现其里面的内容是:

image-20211121114836668 -

我们跟踪原代码发现,HttpJspBase 类。它直接地继承了 HttpServlet 类。

也就是说。jsp 翻译出来的 java 类,它间接了继 承了 HttpServlet 类。

也就是说,翻译出来的是一个 Servlet

image-20211121145858168-

总结: 通过翻译的 java 源代码我们就可以得到结果:jsp 就是 Servlet 程序。

大家也可以去观察翻译出来的 Servlet 程序的源代码,不难发现。

其底层实现,也是通过输出流。把 html 页面数据回传 给客户端

jsp的三种语法

a)jsp 头部的 page 指令

jsp 的 page 指令可以修改 jsp 页面中一些重要的属性,或者行为。

<%@ page contentType="text/html;charset=UTF-8" language="java">

i. language 属性 表示 jsp 翻译后是什么语言文件。暂时只支持 java。

ii. contentType 属性 表示 jsp 返回的数据类型是什么。也是源码中 response.setContentType()参数值

iii. pageEncoding 属性 表示当前 jsp 页面文件本身的字符集。

iv. import 属性 跟 java 源代码中一样。用于导包,导类。

========================两个属性是给 out 输出流使用=============================

v. autoFlush 属性 设置当 out 输出流缓冲区满了之后,是否自动刷新冲级区。默认值是 true。

vi. buffer 属性 设置 out 缓冲区的大小。默认是 8kb

========================两个属性是给 out 输出流使用=============================

vii. errorPage 属性 设置当 jsp 页面运行时出错,自动跳转去的错误页面路径。

viii. isErrorPage 属性 设置当前 jsp 页面是否是错误信息页面。

​ 默认是 false。如果是 true 可以获取异常信息。

ix. session 属性 设置访问当前 jsp 页面,是否会创建 HttpSession 对象。默认是 true。

x. extends 属性 设置 jsp 翻译出来的 java 类默认继承谁。

b)jsp中常用脚本

声明脚本(极少使用

声明脚本的格式是:

<%! 声明 java 代码 %>

作用:可以给 jsp 翻译出来的 java 类定义属性和方法甚至是静态代码块。内部类等。

示例:

1
2
3
4
5
<%!
private Integer id;
private String name;
private static Map<String,Object> map;
%>
表达式脚本(常用

表达式脚本的格式是:<%=表达式%>

表达式脚本的作用是:在jsp 页面上输出数据。

表达式脚本的特点:
1、所有的表达式脚本都会被翻译到_jspService() 方法中_
2、表达式脚本都会被翻译成为 out.print()输出到页面上
3、由于表达式脚本翻译的内容都在_jspService() 方法中,
​ 所以_jspService()方法中的对象都可以直接使用,如request对象。
4、表达式脚本中的表达式不能以分号结束。

代码示例:

1
2
3
4
<%=12%>
<%=12.22%>
<%="我是字符串"%>
<%=request.getParameter("username")%>
代码脚本

代码脚本的格式是:

<% java语句 %>

代码脚本的作用是:可以在 jsp 页面中,编写我们自己需要的功能(写的是 java 语句)。

代码脚本的特点是:

  1. 代码脚本翻译之后都在_jspService 方法中
  2. 代码脚本由于翻译到_jspService()方法中,所以在_jspService()方法中的现有对象都可以直接使用。
  3. 还可以由多个代码脚本块组合完成一个完整的 java 语句。
  4. 代码脚本还可以和表达式脚本一起组合使用,在 jsp 页面上输出数据

练习:

  1. 代码脚本—-if 语句
  2. 代码脚本—-for 循环语句
  3. 翻译后 java 文件中_jspService 方法内的代码都可以写

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<%--1. 代码脚本----if 语句--%>
<%
int i = 13;
if (i>12){
%>
<h1>你赢了!!</h1>
<%
}else {
%>
<h1>你输了!!!</h1>
<%
}
%>
<%--2. 代码脚本----for 循环语句--%>
<table border="1" cellspacing="0">
<%
for (int j = 0; j < 10; j++) {
%>
<tr>
<td>第 <%=j + 1%>行</td>
</tr>
<%
}
%>
</table>
<%--3. 翻译后 java 文件中_jspService 方法内的代码都可以写--%>
<%
String username = request.getParameter("username");
System.out.println("用户名的请求参数值是:" + username);
%>

c)jsp中的三种注释

i. html 注释

html 注释会被翻译到 java 源代码中。在_jspService 方法里,以 out.writer 输出到客户端。

ii. java 注释
1
2
3
4
<%
// 单行 java 注释
/* 多行 java 注释 */
%>

java 注释会被翻译到 java 源代码中。

iii. jsp 注释

<%-- 这是 jsp 注释 --%>
jsp 注释可以注掉,jsp 页面中所有代码

jsp九大内置对象

jsp 中的内置对象,是指 Tomcat 在翻译 jsp 页面成为 Servlet 源代码后,

内部提供的九大对象,叫内置对象。

image-20211121185134388

四大域对象

四个域对象分别是:

  • pageContext(PageContextImpl类) 当前jsp页面范围内有效

  • request (HttpServletRequest类) 一次请求内有效

  • session (HttpSession类) 一次会话范围内有效(打开浏览器访问服务器,直到关闭服务器)

  • application (ServletContext类) 整个web工程范围内有效(只要web工程不停止,数据都在)

域对象可以像Map一样存取数据的对象。四个域对象功能一样。不同的是它们数据的存取范围。

虽然域对象可以存取数据。在使用上它们是有优先顺序的。

四个域对象在使用的时候,优先顺序分别是,它们从小到大的顺序:

pageContext—> request–>session–>application

scope.jsp示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<%
//往四个域中分别保存数据
pageContext.setAttribute("key","pageContext");
request.setAttribute("key","request");
session.setAttribute("key","session");
application.setAttribute("key","application");
%>
pageContext 是否有值:<%=pageContext.getAttribute("key")%><br>
request 是否有值:<%=request.getAttribute("key")%><br>
session 是否有值:<%=session.getAttribute("key")%><br>
application 是否有值:<%=application.getAttribute("key")%><br>
<%
request.getRequestDispatcher("/scope2.jsp").forward(request,response);
//跳转到 scope2取值,pageContext取出的值为null
%>
</body>

scope2.jsp示例:

1
2
3
4
5
6
7
<body>
<h1>scope2.jsp 页面</h1>
pageContext 域是否有值:<%=pageContext.getAttribute("key")%> <br>
request 域是否有值:<%=request.getAttribute("key")%> <br>
session 域是否有值:<%=session.getAttribute("key")%> <br>
application 域是否有值:<%=application.getAttribute("key")%> <br>
</body>

说明:

  1. 当执行到请求跳转到scope2后,pageContext域为null
  2. 当再次请求scope2时,request为null
  3. 当关闭浏览器,在访问scope2,session为null
  4. 当重新部署项目,再访问scope2,application为null

jsp 中的 out 输出和 response.getWriter 输出的区 别

response 中表示响应,我们经常用于设置返回给客户端的内容(输出)

out 也是给用户做输出使用的。

output1示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<body>
<%-- 自行调用 out.flush验证--%>
<%-- 发现 response 输出在最前面
原因是 当jsp页面所有代码执行完成后会执行 out.flush方法,会把out缓冲区的数据追加到
response 缓冲区末尾--%>
<%
JspWriter
out.print("out输出1");
//out.flush();//out输出1 response输出1 response输出2 out输出2
out.print("out输出2");
response.getWriter().write("response输出1");
response.getWriter().write("response输出2");
%>
</body>

image-20211121223520138

output2示例:

1
2
3
4
<%
out.print(12);//底层调用 write方法,并全部转成了字符串形式
out.write(12);//显示不正常,原因是底层采用 (char)转化
%>

jsp常用标签

a)jsp 静态包含

<%@include file="" %>就是静态包含

主题include示例

1
2
3
4
5
6
7
8
9
10
11
12
<body>
<%-- <%@include file="" %>就是静态包含
file 属性指定你要包含的jsp页面的路径
地址中第一个斜杠 / 表示为 http://ip:port/工程路径/ 映射到代码的web目录
--%>
头部信息<br>
主题内容<br>
<%--静态包含的特点:
1,静态包含不会翻译被包含的jsp页面。
2,静态包含其实是把被包含的jsp页面的代码拷贝到包含的位置执行输出。--%>
<%@ include file="/main/footer.jsp" %>
</body>

页脚footer.jsp示例

1
2
3
<body>
页脚信息升级修改版<br>
</body>

b)jsp 动态包含

<jsp:include page=""></jsp:include> 这是动态包含
page 属性是指定你要包含的 jsp 页面的路径
动态包含也可以像静态包含一样。把被包含的内容执行输出到包含位置

动态包含的特点:
1、动态包含会把包含的 jsp 页面也翻译成为 java 代码
2、动态包含底层代码使用如下代码去调用被包含的 jsp 页面执行输出。
JspRuntimeLibrary.include(request, response, “/include/footer.jsp”, out, false);
3、动态包含,还可以传递参数

1
2
3
4
<jsp:include page="/main/footer.jsp">
<jsp:param name="username" value="root"/>
<jsp:param name="password" value="zzt"/>
</jsp:include>

动态包含的底层原理

image-20211122180736702

c)jsp 标签-转发

<jsp:forward:page=""></jsp:forward> 是请求转发标签,它的功能就是请求转发

page 属性设置请求转发的路径

<jsp:forward page="/scope2.jsp"></jsp:forward>

练习二:jsp 输出一个表格,里面有 10 个学生信息。

利用请求重定向,html–》servlet–》jsp–》客户端

SearchStudent.html

<a href="http://localhost:8080/jsp/searchServlet">获取学生数据</a>

Student类

1
2
3
4
5
6
public Student(Integer id, String name, int age, String address) {
this.id = id;
this.name = name;
this.age = age;
this.address = address;
}

SearchServlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取请求的参数
// 发 sql 语句查询学生的信息
// 使用 for 循环生成查询到的数据做模拟
List<Student> students = new ArrayList<>();
for (int i = 0; i < 10; i++) {
int t = i+1;
students.add(new Student(t,"student"+t,t+10,"address"+t));
}
// 保存查询到的结果(学生信息)到 request 域中
request.setAttribute("studentsList",students);
//请求转发给 SearchStudent.jsp
request.getRequestDispatcher("/test/SearchStudent.jsp").forward(request,response);
}

SearchStudent.jsp 页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<%@ page import="java.util.ArrayList" %>
<%@ page import="java.util.List" %>
<%@ page import="com.izumi.test.Student" %>

<%--
Created by IntelliJ IDEA.
User: Pixar
Date: 2021/11/22
Time: 18:41
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<style type="text/css">
table{
border: black 1px solid;
border-collapse: collapse;
width: 650px;
}
tr,td{
border: black 1px solid;

}
</style>
</head>
<body>
<%-- 练习二:jsp 输出一个表格,里面有 10 个学生信息--%>
<%
List<Student> students = (List<Student>) request.getAttribute("studentsList");
%>
<table>
<tr>
<td>学号</td>
<td>姓名</td>
<td>年龄</td>
<td>地址</td>
</tr>
<%for (Student student : students) {%>
<tr>
<td><%=student.getId()%></td>
<td><%=student.getName()%></td>
<td><%=student.getAge()%></td>
<td><%=student.getAddress()%></td>
<td>修改、删除</td>
</tr>

<% }%>

</table>
</body>
</html>

什么是Listenner监听器

1、Listener 监听器它是 JavaWeb 的三大组件之一。

JavaWeb 的三大组件分别是:Servlet 程序、Filter 过滤器、Listener 监 听器。

2、Listener 它是 JavaEE 的规范,就是接口

3、监听器的作用是,监听某种事物的变化。然后通过回调函数,

反馈给客户(程序)去做一些相应的处理。

ServletContextListener监听

ServletContextListener 它可以监听 ServletContext 对象的创建和销毁。

ServletContext 对象在 web 工程启动的时候创建,在 web 工程停止的时候销毁。

监听到创建和销毁之后都会分别调用 ServletContextListener 监听器的方法反馈。

两个方法分别是:

1
2
3
4
5
6
7
8
9
10
public interface ServletContextListener extends EventListener {
/**
* 在 ServletContext 对象创建之后马上调用,做初始化
*/
public void contextInitialized(ServletContextEvent sce);
/**
* 在 ServletContext 对象销毁之后调用
*/
public void contextDestroyed(ServletContextEvent sce);
}

如何使用 ServletContextListener 监听器监听 ServletContext 对象。

使用步骤如下:

  1. 编写一个类去实现 ServletContextListener
  2. 实现其两个回调方法
  3. 到 web.xml 中去配置监听器

实现ServletContextListener 并实现其两个回调方法

1
2
3
4
5
6
7
8
9
10
11
public class MyServletContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("listener被创建");
}

@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("listener被销毁");
}
}

到 web.xml 中去配置监听器

1
2
3
4
<!--    配置监听器-->
<listener>
<listener-class>com.izumi.listener.MyServletContextListener</listener-class>
</listener>

EL表达式

a)什么是 EL 表达式,EL 表达式的作用?

EL 表达式的全称是:Expression Language。是表达式语言。
EL 表达式有什么作用:EL 表达式主要是代替 jsp 页面中的表达式脚本在 jsp 页面中进行数据的输出。
因为 EL 表达式在输出数据的时候,要比 jsp 的表达式脚本要简洁很多

EL表达式的格式${表达式}

EL 表达式在输出 null 值的时候,输出的是空串。jsp 表达式脚本输出 null 值的时候,输出的是 null

1
2
3
4
5
6
<body>
输出值
<% request.setAttribute("key","值");%><br>
jsp输出:<%=request.getAttribute("key")%>
EL表达式输出:${key}
</body>

b)EL 表达式搜索域数据的顺序

EL 表达式主要是在 jsp 页面中输出数据。 主要是输出域对象中的数据。 当四个域中都有相同的 key 的数据的时候,EL 表达式会按照四个域的从小到大的顺序去进行搜索,找到就输出

1
2
3
4
5
6
7
<%
pageContext.setAttribute("key","pageContext");
request.setAttribute("key","request");
session.setAttribute("key","session");
application.setAttribute("key","application");
%>
${key}

c)EL 表达式输出 Bean 的普通属性,数组属性。List 集 合属性,map 集合属性

Person类

1
2
3
4
5
6
7
8
9
10
public class Person {
private String name;
private String[] phone;
private List<String> cities;
private Map<String,Object> map;
...
public int getAge() {
return 18;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<body>
<%--i. 需求——输出 Person 类中普通属性,数组属性。list 集合属性和 map 集合属性。--%>
<%
Person person = new Person();
person.setName("Goose House");
ArrayList<String> list = new ArrayList<>();
list.add("北京");
list.add("上海");
list.add("深圳");
person.setCities(list);
String [] strings ={"163","185","123"};
person.setPhone(strings);
Map<String, Object> map = new HashMap<>();
map.put("key1","value1");
map.put("key2","value2");
map.put("key3","value3");
person.setMap(map);

pageContext.setAttribute("p",person);
%>
<%--输出内容--%>
输出person:${p}<br>
输出person的名字:${p.name}<br>
输出person的所有城市(List集合):${p.cities}<br>
输出person的个别城市(List集合):${p.cities[0]}<br>
输出person的map:${p.map}<br>
输出person的个别map:${p.map.key2}<br>
<%--这里可以看出只要 bean类里面有 get方法,就可以取到值--%>
输出person的age:${p.age}<br>
</body>

d)EL 表达式——运算

1)关系运算

image-20211123110119533

2)逻辑运算

image-20211123110158336

3)算术运算

image-20211123110240007

i. empty 运算

empty 运算可以判断一个数据是否为空,如果为空,则输出 true,不为空输出 false。

以下几种情况为空:

1、值为 null 值的时候,为空

2、值为空串的时候,为空

3、值是 Object 类型数组,长度为零的时候

4、list 集合,元素个数为零

5、map 集合,元素个数为零

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<body>
<%
// 1、值为 null 值的时候,为空
request.setAttribute("emptyNull", null);
// 2、值为空串的时候,为空
request.setAttribute("emptyStr", "");
// 3、值是 Object 类型数组,长度为零的时候
request.setAttribute("emptyArr", new Object[]{});
// 4、list 集合,元素个数为零
List<String> list = new ArrayList<>();
// list.add("abc");
request.setAttribute("emptyList", list);
// 5、map 集合,元素个数为零
Map<String,Object> map = new HashMap<String, Object>();
// map.put("key1", "value1");
request.setAttribute("emptyMap", map);
%>
${ empty emptyNull } <br/>
${ empty emptyStr } <br/>
${ empty emptyArr } <br/>
${ empty emptyList } <br/>
${ empty emptyMap } <br/>
</body>

ii. 三元运算

表达式 1?表达式 2:表达式 3

如果表达式 1 的值为真,返回表达式 2 的值,如果表达式 1 的值为假,返回表达式 3 的值

${12==12?"你赢了!":"你输了"}

iii. “.”点运算 和 [] 中括号运算符

.点运算,可以输出 Bean 对象中某个属性的值。

[]中括号运算,可以输出有序集合中某个元素的值。

并且[]中括号运算,还可以输出 map 集合中 key 里含有特殊字符的 key,

1
2
3
4
5
6
7
8
9
10
11
12
13
<body>
<%
Map<String,Object> map = new HashMap<String, Object>();
map.put("a.a.a", "aaaValue");
map.put("b+b+b", "bbbValue");
map.put("c-c-c", "cccValue");
request.setAttribute("map", map);
%>

${ map['a.a.a'] } <br>
${ map["b+b+b"] } <br>
${ map['c-c-c'] } <br>
</body>

e)EL 表达式的 11 个隐含对象

EL 个达式中 11 个隐含对象,是 EL表达式自己定义的,可以直接使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
变量                       类型                 作用
pageContext PageContextImpl 它可以获取 jsp 中的九大内置对象

pageScope Map<String,Object> 它可以获取 pageContext 域中的数据
requestScope Map<String,Object> 它可以获取 Request 域中的数据
sessionScope Map<String,Object> 它可以获取 Session 域中的数据
applicationScope Map<String,Object> 它可以获取 ServletContext 域中的数据

param Map<String,String> 它可以获取请求参数的值
paramValues Map<String,String[]> 它也可以获取请求参数的值,获取多个值的时候使用。

header Map<String,String> 它可以获取请求头的信息
headerValues Map<String,String[]> 它可以获取请求头的信息,它可以获取多个值的情况

cookie Map<String,Cookie> 它可以获取当前请求的 Cookie 信息

initParam Map<String,String> 它可以获取在 web.xml 中配置的<context-param>上下文参数

EL获取四个特定域中的属性

pageScope====== pageContext 域

requestScope ====== Request 域

sessionScope ====== Session 域

applicationScope ====== ServletContext 域

1
2
3
4
5
6
7
8
9
10
<body>
<%
pageContext.setAttribute("key1","pageContext");
request.setAttribute("key1","request");
session.setAttribute("key1","session");
application.setAttribute("key1","application");
%>
<%--使用XXXScope获取四种域的属性可以解决 当域对象的key相同时,通过域对象获取值时默认取最小域范围的属性的值的问题--%>
${applicationScope.key1}
</body>

ii. pageContext 对象的使用

  1. 协议:
  2. 服务器 ip:
  3. 服务器端口:
  4. 获取工程路径:
  5. 获取请求方法:
  6. 获取客户端 ip 地址:
  7. 获取会话的 id 编号
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<body>
<%-- request.getScheme();获取请求的协议
request.getServerName()获取服务器的ip或域名
request.getServerPort()获取服务器
request.getContextPath() 获取工程路径
request.getMethod() 获取请求方法
request.getMethod() 获取请求方法
request.getRemoteHost() 获取客户端ip地址
session.getId() 获取会话id编号
--%>
<%=request.getRemoteHost()%><br>
<%=session.getId()%><br>
1. 协议:${pageContext.request.scheme}<br>
2. 服务器 ip:${pageContext.request.serverName}<br>
3. 服务器端口:${pageContext.request.serverPort}<br>
4. 获取工程路径:${pageContext.request.contextPath}<br>
5. 获取请求方法:${pageContext.request.method}<br>
6. 获取客户端 ip 地址:${pageContext.request.remoteHost}<br>
7. 获取会话的 id 编号${pageContext.session.id}<br>
</body>

EL其他隐含对象的使用

param Map<String,String> 它可以获取请求参数的值

paramValues Map<String,String[]> 它也可以获取请求参数的值,获取多个值的时候使用。

请求地址:

http://localhost:8080/EL_JSTL/EL_other_obj.jsp?username=root&password=1234&hobby=java&hobby=cpp

示例代码:

1
2
3
4
5
输出请求参数username的值:${param.username}<br>
输出请求参数password的值:${param.password}<br>
输出请求参数password的值:${paramValues.password[0]}<br>
输出请求参数hobby的值:${paramValues.hobby[0]}<br>
输出请求参数hobby的值:${paramValues.hobby[1]}<br>

​ header Map<String,String> 它可以获取请求头的信息

headerValues Map<String,String[]> 它可以获取请求头的信息,它可以获取多个值的情况

示例代码:

1
2
3
获取请求头【User-Agent】${header['User-Agent']}<br>
获取请求头【Accept-Language】${header['Accept-Language']}<br>
获取请求头【Accept-Language】${headerValues['Accept-Language'][0]}

cookie Map<String,Cookie> 它可以获取当前请求的 Cookie

示例代码:

1
2
获取 cookie 的 name:${cookie.JSESSIONID.name} <br>
获取 cookie 的 value:${cookie.JSESSIONID.value}<br>

initParam Map<String,String> 它可以获取在 web.xml 中配置的上下文参数 web.xml 中的配置:

配置web.xml

1
2
3
4
5
6
7
8
<context-param>
<param-name>username</param-name>
<param-value>root</param-value>
</context-param>
<context-param>
<param-name>ip</param-name>
<param-value>127.0.0.1</param-value>
</context-param>

示例代码:

1
2
输出&lt;Context-param&gt;的 username的值:${initParam.username}<br>
输出&lt;Context-param&gt;的 ip的值:${initParam.ip}

JSTL标签库(次重点!!)

JSTL 标签库 全称是指 JSP Standard Tag Library JSP 标准标签库。是一个不断完善的开放源代码的 JSP 标 签库。 EL 表达式主要是为了替换 jsp 中的表达式脚本,而标签库则是为了替换代码脚本。这样使得整个 jsp 页面 变得更佳简洁

JSTL 由五个不同功能的标签库组成。

功能范围URI前缀
核心标签库–重点http://java.sun.com/jsp/jstl/corec
格式化http://java.sun.com/jsp/jstl/fmtfmt
函数http://java.sun.com/jsp/jstl/functionsfn
数据库(不使用)http://java.sun.com/jsp/jstl/sqlsql
XML(不使用)http://java.sun.com/jsp/jstl/xmlx

在 jsp 标签库中使用 taglib 指令引入标签库 (idea会自动导入)

CORE 标签库
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
XML 标签库
<%@ taglib prefix="x" uri="http://java.sun.com/jsp/jstl/xml" %>
FMT 标签库
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
SQL 标签库
<%@ taglib prefix="sql" uri="http://java.sun.com/jsp/jstl/sql" %>
FUNCTIONS 标签库
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %

f) JSTL 标签库的使用步骤

1、先导入 jstl 标签库的 jar 包。

taglibs-standard-impl-1.2.1.jar

taglibs-standard-spec-1.2.1.jar

2、第二步,使用 taglib 指令引入标签库(idea会自动导入)。

core 核心库使用

i. <c:set />(使用很少)

作用:set 标签可以往域中保存数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<%--
i.<c:set />
作用:set 标签可以往域中保存数据
域对象.setAttribute(key,value);
scope 属性设置保存到哪个域
page 表示 PageContext 域(默认值)
request 表示 Request 域
session 表示 Session 域
application 表示 ServletContext 域
var 属性设置 key 是多少
value 属性设置值
--%>
保存之前:${ sessionScope.abc } <br>
保存之前:${ requestScope.key} <br>
<c:set scope="session" var="abc" value="abcValue"/>
<c:set scope="request" var="key" value="keyValue"/>
保存之后:${ sessionScope.abc } <br>
保存之后:${ requestScope.key} <br>

ii. <c:if /> if 标签

用来做 if 判断

1
2
3
4
5
6
7
8
9
10
11
<%--
ii.<c:if />
if 标签用来做 if 判断。
test 属性表示判断的条件(使用 EL 表达式输出)
--%>
<c:if test="${ 12 == 12 }">
<h1>12 等于 12</h1>
</c:if>
<c:if test="${ 12 != 12 }">
<h1>12 不等于 12</h1>
</c:if>

iii. <c:choose> <c:when> <c:otherwise>标签

作用:多路判断。跟 switch … case …. default 非常接近

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<%--
iii.<c:choose> <c:when> <c:otherwise>标签
作用:多路判断。跟 switch ... case .... default 非常接近
choose 标签开始选择判断
when 标签表示每一种判断情况
test 属性表示当前这种判断情况的值
otherwise 标签表示剩下的情况
<c:choose> <c:when> <c:otherwise>标签使用时需要注意的点:
1、标签里不能使用 html 注释,要使用 jsp 注释
2、when 标签的父标签一定要是 choose 标签
--%>
<%
request.setAttribute("height", 180);
%>
<c:choose>
<%-- 不能使用 html 注释,只能使用jsp注释 --%>
<c:when test="${ requestScope.height > 190 }">
<h2>小巨人</h2>
</c:when>
<c:when test="${ requestScope.height > 180 }">
<h2>很高</h2>
</c:when>
<c:when test="${ requestScope.height > 170 }">
<h2>还可以</h2>
</c:when>
<c:otherwise>
<c:choose>
<c:when test="${requestScope.height > 160}">
<h3>大于 160</h3>
</c:when>
<c:when test="${requestScope.height > 150}">
<h3>大于 150</h3>
</c:when>
<c:when test="${requestScope.height > 140}">
<h3>大于 140</h3>
</c:when>
<c:otherwise>
其他小于 140
</c:otherwise>
</c:choose>
</c:otherwise>
</c:choose>

iv. <c:forEach>

作用:遍历输出使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
<%--1.遍历 110,输出
begin 属性设置开始的索引
end 属性设置结束的索引
var 属性表示循环的变量(也是当前正在遍历到的数据)
for (int i = 1; i < 10; i++)
--%>
<table border="1">
<c:forEach begin="1" end="10" var="i">
<tr>
<td>第${i}行</td>
</tr>
</c:forEach>
</table>

2、 遍历 Object 数组

1
2
3
4
5
6
7
8
9
10
11
%-- 2.遍历 Object 数组
for (Object item: arr)
items 表示遍历的数据源(遍历的集合)
var 表示当前遍历到的数据
--%>
<%
request.setAttribute("arr", new String[]{"18610541354","18688886666","18699998888"});
%>
<c:forEach items="${ requestScope.arr }" var="item">
${ item } <br>
</c:forEach>

3、 遍历 Map 集合

1
2
3
4
5
6
7
8
9
10
11
12
<%
Map<String,Object> map = new HashMap<String, Object>();
map.put("key1", "value1");
map.put("key2", "value2");
map.put("key3", "value3");
// for ( Map.Entry<String,Object> entry : map.entrySet()) {
// }
request.setAttribute("map", map);
%>
<c:forEach items="${ requestScope.map }" var="entry">
<h1>${entry.key} = ${entry.value}</h1>
</c:forEach>

4、遍历 List 集合—list 中存放 Student 类,有属性:编号,用户名,密码,年龄, 电话信息

css表格样式

1
2
3
4
5
6
7
8
9
10
<style type="text/css">
table{
width: 500px;
border: solid 1px;
border-collapse: collapse;
}
th,td{
border: solid 1px;
}
</style>

遍历list代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<%--遍历list--%>
<%
ArrayList<Student> students = new ArrayList<Student>();
for (int i = 1; i <= 10; i++) {
students.add(new Student(i,"student"+i,"pwd"+i,i+10,"phone"+i));
}
request.setAttribute("list",students);
%>
<table>
<tr>
<th>编号</th>
<th>姓名</th>
<th>密码</th>
<th>年龄</th>
<th>电话</th>
<th>操作</th>
</tr>
<c:forEach items="${requestScope.list}" var="list">
<tr>
<td>${list.id}</td>
<td>${list.username}</td>
<td>${list.password}</td>
<td>${list.age}</td>
<td>${list.phone}</td>
<td>修改、删除</td>
</tr>

</c:forEach>
</table>

补充说明:

1
2
3
4
5
6
7
8
9
10
<%--
items 表示遍历的集合
var 表示遍历到的数据
begin 表示遍历的开始索引值
end 表示结束的索引值
step 属性表示遍历的步长值 2就表示 i+=2; ,默认是1
varStatus 属性表示当前遍历到的数据的状态
forint i = 1; i < 10; i+=2
--%>
<c:forEach begin="2" end="7" step="2" varStatus="status" items="${requestScope.stus}" var="stu"

varStatus = “status”说明:

javax.servlet.jsp.jstl.core.LoopTagSupport$1Status

class Status implements LoopTagStatus

image-20211123202009762

文件的上传和下载(!!!重点)

文件的上传介绍

1、要有一个 form 标签,method=post 请求
2、form 标签的 encType 属性值必须为 multipart/form-data 值
3、在 form 标签中使用 input type=file 添加上传的文件
4、编写服务器代码(Servlet 程序)接收,处理上传的数据。

encType=multipart/form-data 表示提交的数据,以多段(每一个表单项一个数据段)的形式进行拼
接,然后以二进制流的形式发送给服务器

文件上传 HTTP协议的说明

image-20211123204925008

commons-fileupload.jar 常用 API 介绍说明

commons-fileupload.jar 需要依赖 commons-io.jar 这个包,

所以两个包我们都要引入。

第一步,就是需要导入两个 jar 包:

commons-fileupload-1.2.1.jar

commons-io-1.4.jar

ServletFileUpload 类,用于解析上传的数据。

FileItem 类,表示每一个表单项。

判断当前上传的数据格式是否是多段的格式。

boolean ServletFileUpload.isMultipartContent(HttpServletRequest request);

解析上传的数据
public ListparseRequest(HttpServletRequest request)

判断当前这个表单项,是否是普通的表单项。还是上传的文件类型

boolean FileItem.isFormField()
true 表示普通类型的表单项
false 表示上传的文件类型

获取表单项的 name 属性值
String FileItem.getFieldName()

获取当前表单项的值。
String FileItem.getString()

获取上传的文件名
String FileItem.getName();

将上传的文件写到 参数 file 所指向抽硬盘位置 。
void FileItem.write( file );

fileupload 类库的使用:

上传文件的表单

1
2
3
4
5
<form action="http://localhost:8080/EL_JSTL/uploadServlet" method="post" encType="multipart/form-data">
用户名<input type="text" name="username" /><br>
头像:<input type="file" name="photo" ><br>
<input type="submit" value="上传">
</form>

解析上传的数据的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1 先判断上传的数据是否多段数据(只有是多段的数据,才是文件上传的)
if (ServletFileUpload.isMultipartContent(request)){
// 创建 FileItemFactory 工厂实现类
FileItemFactory fileItemFactory = new DiskFileItemFactory();
// 创建用于解析上传数据的工具类 ServletFileUpload
ServletFileUpload servletFileUpload = new ServletFileUpload(fileItemFactory);
try {
// 解析上传的数据,得到每一个表单项 FileItem
List<FileItem> list = servletFileUpload.parseRequest(request);
// 循环判断,每一个表单项,是普通类型,还是上传的文件
for (FileItem fileItem : list) {
if (fileItem.isFormField()){ //普通表单项
System.out.println("表单项name属性值:"+fileItem.getFieldName());
//解决乱码问题,传入 参数 UTF-8
System.out.println("表单项的value属性值"+fileItem.getString("UTF-8"));
}else {
//上传的文件
System.out.println("表单项的 name 属性值:" + fileItem.getFieldName());
System.out.println("上传的文件名:" + fileItem.getName());
fileItem.write(new File("d:\\" + fileItem.getName()));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}

}

文件下载

文件下载操作

下载的常用 API 说明:

response.getOutputStream();

servletContext.getResourceAsStream();

servletContext.getMimeType();

response.setContentType();

response.setHeader("Content-Disposition", "attachment; fileName=1.jpg");

response.setHeader(“Content-Disposition”, “attachment; fileName=1.jpg”);
这个响应头告诉浏览器。这是需要下载的。而 attachment 表示附件,也就是下载的一个文件。fileName=后面,表示下载的文件名。

完成上面的两个步骤,下载文件是没问题了。但是如果我们要下载的文件是中文名的话。你会发现,下载无法正确显示出正确的中文名。
原因是在响应头中,不能包含有中文字符,只能包含 ASCII 码

文件下载代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Download  extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取文件名
String downloadFileName = "hero.png";
// 2、读取要下载的文件内容 (通过 ServletContext 对象可以读取)
ServletContext servletContext = getServletContext();
// 获取要下载的文件类
String mimeType = servletContext.getMimeType("/file/" + downloadFileName);
System.out.println("下载的文件类型:"+mimeType);
// 4、在回传前,通过响应头告诉客户端返回的数据类型
resp.setContentType(mimeType);

// 5、还要告诉客户端收到的数据是用于下载使用(还是使用响应头)
// Content-Disposition 响应头,表示收到的数据怎么处理
// attachment 表示附件,表示下载使用
// filename= 表示指定下载的文件名
resp.setHeader("Content-Disposition","attachment;filename="+downloadFileName);
/**
* /斜杠被服务器解析表示地址为 http://ip:prot/工程名/ 映射 到代码的 Web 目录
*/
InputStream inputStream = servletContext.getResourceAsStream("/file/" + downloadFileName);
//获取响应输出流
ServletOutputStream outputStream = resp.getOutputStream();
// 3、把下载的文件内容回传给客户端
// 读取输入流中全部的数据,复制给输出流,输出给客户端
IOUtils.copy(inputStream,outputStream);
}
}

附件中文名乱码问题解决方案:

方案一:URLEncoder 解决 IE 和谷歌浏览器的 附件中 文名问题。(掌握)

如果客户端浏览器是 IE 浏览器 或者 是谷歌浏览器。(火狐现在已经支持)
我们需要使用 URLEncoder 类先对中文名进行 UTF-8 的编码 操作。
因为 IE 浏览器和谷歌浏览器收到含有编码后的字符串后会以 UTF-8 字符集进行解码显示。

1
2
3
4
// 把中文名进行 UTF-8 编码操作。
String str = "attachment; fileName=" + URLEncoder.encode("中文.jpg", "UTF-8");
// 然后把编码后的字符串设置到响应头中
response.setHeader("Content-Disposition", str);
方案二:(了解)BASE64 编解码 解决 火狐浏览器的附件中文名问 题(现在火狐浏览器已支持 URLEncode)
basic
1
2
3
4
5
6
7
8
9
10
如果客户端浏览器是火狐浏览器。 那么我们需要对中文名进行 BASE64 的编码操作。
这时候需要把请求头 Content-Disposition: attachment; filename=中文名
编码成为:Content-Disposition: attachment; filename==?charset?B?xxxxx?=

=?charset?B?xxxxx?= 现在我们对这段内容进行一下说明。
=? 表示编码内容的开始
charset 表示字符集
B 表示 BASE64 编码
xxxx 表示文件名 BASE64 编码后的内容
?= 表示编码内容的结束

Base64编解码操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void main(String[] args) throws Exception {
String content = "这是需要 Base64 编码的内容";
// 创建一个 Base64 编码器
BASE64Encoder base64Encoder = new BASE64Encoder();
// 执行 Base64 编码操作
String encodedString = base64Encoder.encode(content.getBytes("UTF-8"));
System.out.println( encodedString );
// 创建 Base64 解码器
BASE64Decoder base64Decoder = new BASE64Decoder();
// 解码操作
byte[] bytes = base64Decoder.decodeBuffer(encodedString);
String str = new String(bytes, "UTF-8");
System.out.println(str);
}

因为火狐使用的是 BASE64 的编解码方式还原响应中的汉字。所以需要使用 BASE64Encoder 类进行编码操作。

1
2
3
4
5
// 使用下面的格式进行 BASE64 编码后
String str = "attachment; fileName=" + "=?utf-8?B?"
+ new BASE64Encoder().encode("中文.jpg".getBytes("utf-8")) + "?=";
// 设置到响应头中
response.setHeader("Content-Disposition", str);

那么我们如何解决上面两种不同编解码方式呢。我们只需要通过判断请求头中 User-Agent 这个请求头携带过来的 浏览器信息即可判断出是什么浏览器。

1
2
3
4
5
6
7
8
9
10
11
12
13
String ua = request.getHeader("User-Agent");
// 判断是否是火狐浏览器
if (ua.contains("Firefox")) {
// 使用下面的格式进行 BASE64 编码后
String str = "attachment; fileName=" + "=?utf-8?B?"
+ new BASE64Encoder().encode("中文.jpg".getBytes("utf-8")) + "?=";
// 设置到响应头中
response.setHeader("Content-Disposition", str);
} else {
// 把中文名进行 UTF-8 编码操作。
String str = "attachment; fileName=" + URLEncoder.encode("中文.jpg", "UTF-8");
// 然后把编码后的字符串设置到响应头中
response.setHeader("Content-Disposition", str);

书城项目-第三阶段

a)页面 jsp 动态化

1、在 html 页面顶行添加 page 指令。

2、修改文件后缀名为:.jsp

3、使用 IDEA 搜索替换.html 为.jsp(快捷键:Ctrl+Shift+R)

image-20211124124720963

b)抽取页面中相同的内容

i. head 中 css、jquery、base 标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
%
String basePath = request.getScheme()
+ "://"
+ request.getServerName()
+ ":"
+ request.getServerPort()
+ request.getContextPath()
+ "/";
%>
<%=basePath%>
<!--写 base 标签,永远固定相对路径跳转的结果-->
<base href="<%=basePath%>">
<link type="text/css" rel="stylesheet" href="static/css/style.css" >
<script type="text/javascript" src="static/script/jquery-1.7.2.js"></script>

ii. 每个页面的页脚

1
2
3
4
5
<div id="bottom">
<span>
尚硅谷书城.Copyright &copy;2015
</span>
</div>

iii. 登录成功后的菜单

1
2
3
4
5
6
<div>
<span>欢迎<span class="um_span">韩总</span>光临尚硅谷书城</span>
<a href="../order/order.jsp">我的订单</a>
<a href="../../index.jsp">注销</a>&nbsp;&nbsp;
<a href="../../index.jsp">返回</a>
</div>

iv. manager 模块的菜单

1
2
3
4
5
<div>
<a href="book_manager.jsp">图书管理</a>
<a href="order_manager.jsp">订单管理</a>
<a href="../../index.jsp">返回商城</a>
</div>

c)登录,注册错误提示,及表单回显

以登录回显为示例: Servlet 程序端需要添加回显信息到 Request

1
2
3
4
5
6
7
8
if(userService.login(new User(null,username,password,null))==null){
System.out.println(request.getRemoteHost()+" 登陆失败");
// 失败
//告诉客户端错误信息,并把用户名回显
request.setAttribute("mes","用户名或密码错误");
request.setAttribute("username",username);
// 跳回登录页面
request.getRequestDispatcher("/pages/user/login.jsp").forward(request,response);

jsp 页面,需要输出回显信息

image-20211124154419501

d)BaseServlet 的抽取

在实际的项目开发中,一个模块,一般只使用一个 Servlet 程序

代码优化一:代码优化:合并 LoginServlet 和 RegistServlet 程序为 UserServlet 程序

image-20211124160631231

优化代码二:使用反射优化大量 else if 代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
String action = req.getParameter("action");
try {
// 获取 action 业务鉴别字符串,获取相应的业务 方法反射对象
Method method = this.getClass().getDeclaredMethod(action, HttpServletRequest.class,
HttpServletResponse.class);
// System.out.println(method);
// 调用目标业务 方法
method.invoke(this, req, resp);
} catch (Exception e) {
e.printStackTrace();
}
}

代码优化三:抽取 BaseServlet 程序。

image-20211124191432897

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public abstract class BaseServlet extends HttpServlet {
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
String action = req.getParameter("action");
try {
// 获取 action 业务鉴别字符串,获取相应的业务 方法反射对象
Method method = this.getClass().getDeclaredMethod(action, HttpServletRequest.class,
HttpServletResponse.class);
// System.out.println(method);
// 调用目标业务 方法
method.invoke(this, req, resp);
} catch (Exception e) {
e.printStackTrace();
}
}
}

修改其他servlet程序继承它

e)数据的封装和抽取 BeanUtils 的使用

BeanUtils 工具类,它可以一次性的把所有请求的参数注入到 JavaBean 中。
BeanUtils 工具类,经常用于把 Map 中的值注入到 JavaBean 中,或者是对象属性值的拷贝操作。
BeanUtils 它不是 Jdk 的类。而是第三方的工具类。所以需要导包。
1、导入需要的 jar 包:
commons-beanutils-1.8.0.jar
commons-logging-1.1.1.jar
2、编写 WebUtils 工具类使用:
WebUtils 工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class WebUtils {
/**
* 把 Map 中的值注入到对应的 JavaBean 属性中。
* @param value
* @param bean
*/
public static <T> T copyParamToBean( Map value , T bean ){
try {
System.out.println("注入之前:" + bean);
/**
* 把所有请求的参数都注入到 user 对象中
*/
BeanUtils.populate(bean, value);
System.out.println("注入之后:" + bean);
} catch (Exception e) {
e.printStackTrace();
}
return bean;
}
}

**书城-**第四阶段。使用 EL表达式修改表单回显

书城-第五阶段-图书模块

1、MVC 概念

MVC 全称:Model 模型、 View 视图、 Controller 控制器。
MVC 最早出现在 JavaEE 三层中的 Web 层,它可以有效的指导 Web 层的代码如何有效分离,单独工作。

View 视图:只负责数据和界面的显示,不接受任何与显示数据无关的代码,便于程序员和美工的分工合作——JSP/HTML。

Controller 控制器:只负责接收请求,调用业务层的代码处理请求,然后派发页面,是一个“调度者”的角色——Servlet。 转到某个页面。或者是重定向到某个页面。

Model 模型:将与业务逻辑相关的数据封装为具体的 JavaBean 类,其中不掺杂任何与数据处理相关的代码——JavaBean/domain/entity/pojo。

MVC 是一种思想

MVC 的理念是将软件代码拆分成为组件,单独开发,组合使用(目的还是为了降低耦合度)。

image-20211124210503267MVC 的作用还是为了降低耦合。让代码合理分层。方便后期升级和维护。

1、图书模块

1.1、编写图书模块的数据库表

设计图书数据库表并插入测试数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
create TABLE t_book(
id int PRIMARY KEY auto_increment,
`name` VARCHAR(100),
price DECIMAL(11,2),
author VARCHAR(100),
sales int,
stock int,
img_path VARCHAR(200)
);

## 插入初始化测试数据
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , 'java 从入门到放弃' , '国哥' , 80 , 9999 , 9 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , '数据结构与算法' , '严敏君' , 78.5 , 6 , 13 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , '怎样拐跑别人的媳妇' , '龙伍' , 68, 99999 , 52 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , '木虚肉盖饭' , '小胖' , 16, 1000 , 50 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , 'C++编程思想' , '刚哥' , 45.5 , 14 , 95 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , '蛋炒饭' , '周星星' , 9.9, 12 , 53 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , '赌神' , '龙伍' , 66.5, 125 , 535 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , 'Java 编程思想' , '阳哥' , 99.5 , 47 , 36 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , 'JavaScript 从入门到精通' , '婷姐' , 9.9 , 85 , 95 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , 'cocos2d-x 游戏编程入门' , '国哥' , 49, 52 , 62 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , 'C 语言程序设计' , '谭浩强' , 28 , 52 , 74 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , 'Lua 语言程序设计' , '雷丰阳' , 51.5 , 48 , 82 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , '西游记' , '罗贯中' , 12, 19 , 9999 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , '水浒传' , '华仔' , 33.05 , 22 , 88 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , '操作系统原理' , '刘优' , 133.05 , 122 , 188 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , '数据结构 java 版' , '封大神' , 173.15 , 21 , 81 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , 'UNIX 高级环境编程' , '乐天' , 99.15 , 210 , 810 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , 'javaScript 高级编程' , '国哥' , 69.15 , 210 , 810 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , '大话设计模式' , '国哥' , 89.15 , 20 , 10 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , '人月神话' , '刚哥' , 88.15 , 20 , 80 , 'static/img/default.jpg');

SELECT * from t_book;

1.2、编写图书模块的 JavaBean

1.3、编写图书模块的 Dao 和测试 Dao

1.4、编写图书模块的 Service 和测试 Service

1.5、编写图书模块的 Web 层,和页面联调测试

1.5.1、图书列表功能的实现

1、图解列表功能流程:

image-20211125120133525

2、BookServlet 程序中添加 list 方法

1
2
3
4
5
6
7
8
9
protected void list(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
//1 通过 BookService 查询全部图书
List<Book> books = bookService.queryBooks();
//2 把全部图书保存到 Request 域中
req.setAttribute("books", books);
//3、请求转发到/pages/manager/book_manager.jsp 页面
req.getRequestDispatcher("/pages/manager/book_manager.jsp").forward(req,resp);
}

3、修改【图书管理】请求地址

image-20211125120223475

4、修改 pages/manager/book_manager.jsp 页面的数据遍历输出(略)

1.5.2、前后台的简单介绍

image-20211125120307410-

1.5.3、添加图书功能的实现

添加图书流程细节:

image-20211125125317106

1.5.3.2、问题说明:

表单重复提交: 当用户提交完请求,浏览器会记录下最后一次请求的全部信息。

当用户按下功能键 F5,就会发起浏览器记录的最后一次 请求。

1.5.3.3、BookServlet 程序中添加 add 方法

1
2
3
4
5
6
7
8
9
10
11
protected void add(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
// 1、获取请求的参数==封装成为 Book 对象
Book book = WebUtils.copyParamToBean(req.getParameterMap(),new Book());
// 2、调用 BookService.addBook()保存图书
bookService.addBook(book);
// 3、跳到图书列表页面
// /manager/bookServlet?action=list
// req.getRequestDispatcher("/manager/bookServlet?action=list").forward(req, resp);
resp.sendRedirect(req.getContextPath() + "/manager/bookServlet?action=list");
}

1.5.3.4、修改 book_edit.jsp 页面

修改表单提交地址

1
2
<form action="manager/bookServlet" method="get">
<input type="hidden" name="action" value="add" />
1.5.4、删除图书功能的实现

1.5.4.1、图解删除流程:

image-20211125143512764

1.5.4.2、BookServlet 程序中的 delete 方法:

1
2
3
4
5
6
7
8
9
10
protected void delete(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
// 1、获取请求的参数 id,图书编程
int id = WebUtils.parseInt(req.getParameter("id"), 0);
// 2、调用 bookService.deleteBookById();删除图书
bookService.deleteBookById(id);
// 3、重定向回图书列表管理页面
// /book/manager/bookServlet?action=list
resp.sendRedirect(req.getContextPath() + "/manager/bookServlet?action=list");
}

1.5.4.3、给 WebUtils 工具类添加转换 int 类型的工具方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 将字符串转换成为 int 类型的数据
* @param strInt
* @param defaultValue
* @return
*/
public static int parseInt(String strInt,int defaultValue) {
try {
return Integer.parseInt(strInt);
} catch (Exception e) {
e.printStackTrace();
}
return defaultValue;
}

1.5.4.4、修改删除的连接地址:

image-20211125143629913

1.5.4.5、给删除添加确认提示操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script type="text/javascript">
$(function () {
// 给删除的 a 标签绑定单击事件,用于删除的确认提示操作
$("a.deleteClass").click(function () {
// 在事件的 function 函数中,有一个 this 对象。这个 this 对象,是当前正在响应事件的 dom 对象。
/**
* confirm 是确认提示框函数
* 参数是它的提示内容
* 它有两个按钮,一个确认,一个是取消。
* 返回 true 表示点击了,确认,返回 false 表示点击取消。
*/
return confirm("你确定要删除【" + $(this).parent().parent().find("td:first").text() + "】?");
// return false// 阻止元素的默认行为===不提交请求
});
});
</script>
1.5.5、修改图书功能的实现

1.5.5.1:图解修改图书细节:

image-20211125152454744

1.5.5.2、更新【修改】的请求地址:

image-20211125152534833

1.5.5.3、BookServlet 程序中添加 getBook 方法:

1
2
3
4
5
6
7
8
9
10
11
protected void getBook(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
//1 获取请求的参数图书编号
int id = WebUtils.parseInt(req.getParameter("id"), 0);
//2 调用 bookService.queryBookById 查询图书
Book book = bookService.queryBookById(id);
//3 保存到图书到 Request 域中
req.setAttribute("book", book) ;
//4 请求转发到。pages/manager/book_edit.jsp 页面
req.getRequestDispatcher("/pages/manager/book_edit.jsp").forward(req,resp);
}

1.5.5.4、在 book_edit.jsp 页面中显示修改的数据

(略)

1.5.5.5、在 BookServlet 程序中添加 update 方法:

1
2
3
4
5
6
7
8
9
10
protected void update(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
// 1、获取请求的参数==封装成为 Book 对象
Book book = WebUtils.copyParamToBean(req.getParameterMap(),new Book());
// 2、调用 BookService.updateBook( book );修改图书
bookService.updateBook(book);
// 3、重定向回图书列表管理页面
// 地址:/工程名/manager/bookServlet?action=list
resp.sendRedirect(req.getContextPath() + "/manager/bookServlet?action=list");
}

1.5.5.6、解决 book_edit.jsp 页面,即要实现添加,又要实现修改操作。

这里使用图解的第一种方法

book_edit.jsp

<input type="hidden" name="action" value="${param.method}">

book_manager.jsp 增加 method=update

<td><a href="manager/bookServlet?action=getBook&method=update&id=${book.id}">修改</a></td>

<td><a href="pages/manager/book_edit.jsp?method=add">添加图书</a></td>

2、图书分页

1)分页模块的分析

image-20211125184753779

2)分页模型 Page 的抽取(当前页数,总页数,总记录数, 当前页数据,每页记录数)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* Page 是分页的模型对象
* @param <T> 是具体的模块的 javaBean 类
*/
public class Page<T> {
public static final Integer PAGE_SIZE = 4;
// 当前页码
private Integer pageNo;
// 总页码
private Integer pageTotal;
// 当前页显示数量
private Integer pageSize = PAGE_SIZE;
// 总记录数
private Integer pageTotalCount;
// 当前页数据
private List<T> items

3)分页的初步实现

bookDao代码

1
2
3
4
5
6
7
8
9
10
11
12
@Override
public Integer queryForPageTotalCount() {
String sql = "select count(*) from t_book";
Number count = (Number) queryForSingleValue(sql);
return count.intValue();
}
@Override
public List<Book> queryForPageItems(int begin, int pageSize) {
String sql = "select `id` , `name` , `author` , `price` , `sales` , `stock` , `img_path` imgPath
from t_book limit ?,?";
return queryForList(Book.class,sql,begin,pageSize);
}

BookService 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Override
public Page<Book> page(int pageNo, int pageSize) {
Page<Book> page = new Page<Book>();
// 设置当前页码
page.setPageNo(pageNo);
// 设置每页显示的数量
page.setPageSize(pageSize);
// 求总记录数
Integer pageTotalCount = bookDao.queryForPageTotalCount();
// 设置总记录数
page.setPageTotalCount(pageTotalCount);
// 求总页码
Integer pageTotal = pageTotalCount / pageSize;
if (pageTotalCount % pageSize > 0) {
pageTotal+=1;
}
// 设置总页码
page.setPageTotal(pageTotal);
// 求当前页数据的开始索引
int begin = (page.getPageNo() - 1) * pageSize;
// 求当前页数据
List<Book> items = bookDao.queryForPageItems(begin,pageSize);
// 设置当前页数据
page.setItems(items);
return page;
}

BookServlet代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 处理分页功能
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void page(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
//1 获取请求的参数 pageNo 和 pageSize
int pageNo = WebUtils.parseInt(req.getParameter("pageNo"), 1);
int pageSize = WebUtils.parseInt(req.getParameter("pageSize"), Page.PAGE_SIZE);
//2 调用 BookService.page(pageNo,pageSize):Page 对象
Page<Book> page = bookService.page(pageNo,pageSize);
//3 保存 Page 对象到 Request 域中
req.setAttribute("page",page);
//4 请求转发到 pages/manager/book_manager.jsp 页面
req.getRequestDispatcher("/pages/manager/book_manager.jsp").forward(req,resp);
}

manager_menu.jsp 中【图书管理】请求地址的修改:

image-20211125220045693

book_manager.jsp 修改

image-20211125220232513-

在 table后面加上 分页div

1
2
3
4
5
6
7
8
9
10
11
12
<div id="page_nav">
<a href="#">首页</a>
<a href="#">上一页</a>
<a href="#">3</a>
【${ requestScope.page.pageNo }】
<a href="#">5</a>
<a href="#">下一页</a>
<a href="#">末页</a>
共${ requestScope.page.pageTotal }页,${ requestScope.page.pageTotalCount }条记录
到第<input value="4" name="pn" id="pn_input"/>页
<input type="button" value="确定">
</div>

4)首页、上一页、下一页、末页实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div id="page_nav">
<%--大于首页,才显示--%>
<c:if test="${requestScope.page.pageNo > 1}">
<a href="manager/bookServlet?action=page&pageNo=1">首页</a>
<a href="manager/bookServlet?action=page&pageNo=${requestScope.page.pageNo-1}">上一页</a>
</c:if>
<a href="#">3</a>
【${ requestScope.page.pageNo }】
<a href="#">5</a>
<%-- 如果已经 是最后一页,则不显示下一页,末页 --%>
<c:if test="${requestScope.page.pageNo < requestScope.page.pageTotal}">
<a href="manager/bookServlet?action=page&pageNo=${requestScope.page.pageNo+1}">下一页</a>
<a href="manager/bookServlet?action=page&pageNo=${requestScope.page.pageTotal}">末页</a>
</c:if>
共${ requestScope.page.pageTotal }页,${ requestScope.page.pageTotalCount }条记录
到第<input value="4" name="pn" id="pn_input"/>页
<input type="button" value="确定">
</div>

5)分页模块中跳转到指定页数功能实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<div id="page_nav">
<%--大于首页,才显示--%>
<c:if test="${requestScope.page.pageNo > 1}">
<a href="manager/bookServlet?action=page&pageNo=1">首页</a>
<a href="manager/bookServlet?action=page&pageNo=${requestScope.page.pageNo-1}">上一页</a>
</c:if>
<a href="#">3</a>
【${ requestScope.page.pageNo }】
<a href="#">5</a>
<%-- 如果已经 是最后一页,则不显示下一页,末页 --%>
<c:if test="${requestScope.page.pageNo < requestScope.page.pageTotal}">
<a href="manager/bookServlet?action=page&pageNo=${requestScope.page.pageNo+1}">下一页</a>
<a href="manager/bookServlet?action=page&pageNo=${requestScope.page.pageTotal}">末页</a>
</c:if>
共${ requestScope.page.pageTotal }页,${ requestScope.page.pageTotalCount }条记录
到第<input value="${param.pageNo}" name="pn" id="pn_input"/>页
<input id="searchPageBtn" type="button" value="确定">
<script type="text/javascript">
$(function () {
// 跳到指定的页码
$("#searchPageBtn").click(function () {
var pageNo = $("#pn_input").val();
<%--var pageTotal = ${requestScope.page.pageTotal};--%>
<%--alert(pageTotal);--%>
// javaScript 语言中提供了一个 location 地址栏对象
// 它有一个属性叫 href.它可以获取浏览器地址栏中的地址
// href 属性可读,可写
location.href = "${pageScope.basePath}manager/bookServlet?action=page&pageNo=" +
pageNo;
});
});
</script>
</div>

Page 对象中的修改

1
2
3
4
5
6
7
8
9
10
public void setPageNo(Integer pageNo) {
/* 数据边界的有效检查 */
if (pageNo < 1) {
pageNo = 1;
}
if (pageNo > pageTotal) {
pageNo = pageTotal;
}
this.pageNo = pageNo;
}

6)分页模块中,页码 1,2,【3】,4,5 的显示,要显示 5 个页 码,并且页码可以点击跳转。

小情况 1:当前页码为前面 3 个:1,2,3 的情况,页码范围是:1-5. 【1】2,3,4,5
1【2】3,4,5
1,2【3】4,5

小情况 2:当前页码为最后 3 个,8,9,10,页码范围是:总页码减 4 - 总页码
6,7【8】9,10
6,7,8【9】10
6,7,8,9【10】
小情况 3:4,5,6,7,页码范围是:当前页码减 2 - 当前页码加 2

2,3,4,5,6
3,4,5,6,7
4,5,6,7,8
5,6,7,8,9

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<%--页码输出的开始--%>
<c:choose>
<%--情况 1:如果总页码小于等于 5 的情况,页码的范围是:1-总页码--%>
<c:when test="${ requestScope.page.pageTotal <= 5 }">
<c:set var="begin" value="1"/>
<c:set var="end" value="${requestScope.page.pageTotal}"/>
</c:when>
<%--情况 2:总页码大于 5 的情况--%>
<c:when test="${requestScope.page.pageTotal > 5}">
<c:choose>
<%--小情况 1:当前页码为前面 3 个:123 的情况,页码范围是:1-5.--%>
<c:when test="${requestScope.page.pageNo <= 3}">
<c:set var="begin" value="1"/>
<c:set var="end" value="5"/>
</c:when>
<%--小情况 2:当前页码为最后 3 个,8910,页码范围是:总页码减 4 - 总页码--%>
<c:when test="${requestScope.page.pageNo > requestScope.page.pageTotal-3}">
<c:set var="begin" value="${requestScope.page.pageTotal-4}"/>
<c:set var="end" value="${requestScope.page.pageTotal}"/>
</c:when>
<%--小情况 34567,页码范围是:当前页码减 2 - 当前页码加 2--%>
<c:otherwise>
<c:set var="begin" value="${requestScope.page.pageNo-2}"/>
<c:set var="end" value="${requestScope.page.pageNo+2}"/>
</c:otherwise>
</c:choose>
</c:when>
</c:choose>
<c:forEach begin="${begin}" end="${end}" var="i">
<c:if test="${i == requestScope.page.pageNo}">
【${i}】
</c:if>
<c:if test="${i != requestScope.page.pageNo}">
<a href="manager/bookServlet?action=page&pageNo=${i}">${i}</a>
</c:if>
</c:forEach>
<%--页码输出的结束--%>

7) 修改分页后,增加,删除,修改图书信息的回显页面

以修改图书为示例: 1、在修改的请求地址上追加当前页码参数:

image-20211127094503621

2、在 book_edit.jsp 页面中使用隐藏域记录下 pageNo 参数

image-20211127094527901

3、在服务器重定向的时候,获取当前页码追加上进行跳转

1
2
3
4
5
6
7
8
9
10
11
protected void update(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
// 1、获取请求的参数==封装成为 Book 对象
Book book = WebUtils.copyParamToBean(req.getParameterMap(),new Book());
// 2、调用 BookService.updateBook( book );修改图书
bookService.updateBook(book);
// 3、重定向回图书列表管理页面
// 地址:/工程名/manager/bookServlet?action=list
resp.sendRedirect(req.getContextPath() + "/manager/bookServlet?action=page&pageNo=" +
req.getParameter("pageNo"));
}

3、首页 index.jsp 的跳转

image-20211127113948909

4、分页条的抽取

4.1、抽取分页条中请求地址为 url 变量

4.1.1.在 Page类中添加属性 url

1
2
// 分页条的请求地址
private String url;

4.1.2 在 Servlet 程序的 page 分页方法中设置 url 的分页请求地址

image-20211127114131459

4.1.3、修改分页条中请求地址为 url 变量输出,并抽取一个单独的 jsp 页面

即将 manager.jsp页面中的

manager/bookServlet?action=page

和 client下的 index.jsp页面中的

clent/bookServlet?action=page

更改为

${requestScope.page.url}

5、首页价格搜索

image-20211127114729156

1、Cookie 翻译过来是饼干的意思。

2、Cookie 是服务器通知客户端保存键值对的一种技术。

3、客户端有了 Cookie 后,每次请求都发送给服务器。

4、每个 Cookie 的大小不能超过 4kb

image-20211127184123674

Servlet 程序中的代码

1
2
3
4
5
6
7
8
9
10
11
12
protected void createCookie(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
//1 创建 Cookie 对象
Cookie cookie = new Cookie("key4", "value4");
//2 通知客户端保存 Cookie
resp.addCookie(cookie);
//1 创建 Cookie 对象
Cookie cookie1 = new Cookie("key5", "value5");
//2 通知客户端保存 Cookie
resp.addCookie(cookie1);
resp.getWriter().write("Cookie 创建成功");
}

服务器获取客户端的 Cookie 只需要一行代码:req.getCookies():Cookie[]

image-20211127211243868

cookie的工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class CookieUtils {
/**
* 查找指定名称的 Cookie 对象
* @param name
* @param cookies
* @return
*/
public static Cookie findCookie(String name , Cookie[] cookies){
if (name == null || cookies == null || cookies.length == 0) {
return null;
}
for (Cookie cookie : cookies) {
if (name.equals(cookie.getName())) {
return cookie;
}
}
return null;
}
}

Servlet 程序中的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
protected void getCookie(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
Cookie[] cookies = req.getCookies();
for (Cookie cookie : cookies) {
// getName 方法返回 Cookie 的 key(名)
// getValue 方法返回 Cookie 的 value 值
resp.getWriter().write("Cookie[" + cookie.getName() + "=" + cookie.getValue() + "] <br/>");
}
Cookie iWantCookie = CookieUtils.findCookie("key1", cookies);
// for (Cookie cookie : cookies) {
// if ("key2".equals(cookie.getName())) {
// iWantCookie = cookie;
// break;
// }
// }
// 如果不等于 null,说明赋过值,也就是找到了需要的 Cookie
if (iWantCookie != null) {
resp.getWriter().write("找到了需要的 Cookie");
}

方案一:

1、先创建一个要修改的同名(指的就是 key)的 Cookie 对象

2、在构造器,同时赋于新的 Cookie 值。

3、调用 response.addCookie( Cookie );

1
2
3
4
5
6
// 方案一:
// 1、先创建一个要修改的同名的 Cookie 对象
// 2、在构造器,同时赋于新的 Cookie 值。
Cookie cookie = new Cookie("key1","newValue1");
// 3、调用 response.addCookie( Cookie ); 通知 客户端 保存修改
resp.addCookie(cookie);

方案二:

1、先查找到需要修改的 Cookie 对象

2、调用 setValue()方法赋于新的 Cookie 值。

3、调用 response.addCookie()通知客户端保存修

1
2
3
4
5
6
7
8
9
// 方案二:
// 1、先查找到需要修改的 Cookie 对象
Cookie cookie = CookieUtils.findCookie("key2", req.getCookies());
if (cookie != null) {
// 2、调用 setValue()方法赋于新的 Cookie 值。
cookie.setValue("newValue2");
// 3、调用 response.addCookie()通知客户端保存修改
resp.addCookie(cookie);
}

谷歌浏览器查看cookie

image-20211127212910873

火狐浏览器查看cookie

image-20211127212927346

Cookie 的生命控制指的是如何管理 Cookie 什么时候被销毁(删除)

setMaxAge()

​ 正数,表示在指定的秒数后过期
​ 负数,表示浏览器一关,Cookie 就会被删除(默认值是-1)
​ 零,表示马上删除 Cooki

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/**
* 设置存活 1 个小时的 Cooie
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void life3600(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
Cookie cookie = new Cookie("life3600", "life3600");
cookie.setMaxAge(60 * 60); // 设置 Cookie 一小时之后被删除。无效
resp.addCookie(cookie);
resp.getWriter().write("已经创建了一个存活一小时的 Cookie");
}
/**
* 马上删除一个 Cookie
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void deleteNow(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
// 先找到你要删除的 Cookie 对象
Cookie cookie = CookieUtils.findCookie("key4", req.getCookies());
if (cookie != null) {
// 调用 setMaxAge(0);
cookie.setMaxAge(0); // 表示马上删除,都不需要等待浏览器关闭
// 调用 response.addCookie(cookie);
resp.addCookie(cookie);
resp.getWriter().write("key4 的 Cookie 已经被删除");
}
}
/**
* 默认的会话级别的 Cookie
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void defaultLife(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
Cookie cookie = new Cookie("defalutLife","defaultLife");
cookie.setMaxAge(-1);//设置存活时间
resp.addCookie(cookie);
}

Cookie 的 path 属性可以有效的过滤哪些 Cookie 可以发送给服务器。哪些不发。
path 属性是通过请求的地址来进行有效的过滤。
CookieA path=/工程路径
CookieB path=/工程路径/abc
请求地址如下:
http://ip:port/工程路径/a.html
CookieA 发送
CookieB 不发送
http://ip:port/工程路径/abc/a.html
CookieA 发送
CookieB 发送

1
2
3
4
5
6
7
8
protected void testPath(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
Cookie cookie = new Cookie("path1", "path1");
// getContextPath() ===>>>> 得到工程路径
cookie.setPath( req.getContextPath() + "/abc" ); // ===>>>> /工程路径/abc
resp.addCookie(cookie);
resp.getWriter().write("创建了一个带有 Path 路径的 Cookie");
}

image-20211127215728186

login.jsp

1
2
3
4
5
<form action="http://localhost:8080/13_cookie_session/loginServlet" method="get">
用户名:<input type="text" name="username" value="${cookie.username.value}"> <br>
密码:<input type="password" name="password"> <br>
<input type="submit" value="登录">
</form>

servlet程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
String username = req.getParameter("username");
String password = req.getParameter("password");
if ("wzg168".equals(username) && "123456".equals(password)) {
//登录 成功
Cookie cookie = new Cookie("username", username);
cookie.setMaxAge(60 * 60 * 24 * 7);//当前 Cookie 一周内有效
resp.addCookie(cookie);
System.out.println("登录 成功");
} else {
// 登录 失败
System.out.println("登录 失败");
}
}

2、Session 会话

i) 什么是 Session 会话

1、Session 就一个接口(HttpSession)。

2、Session 就是会话。它是用来维护一个客户端和服务器之间关联的一种技术。

3、每个客户端都有自己的一个 Session 会话。

4、Session 会话中,我们经常用来保存用户登录之后的信息

j) 如何创建 Session 和获取(id 号,是否为新

如何创建和获取 Session。它们的 API 是一样的。
request.getSession()
第一次调用是:创建 Session 会话
之后调用都是:获取前面创建好的 Session 会话对象。

isNew() ; 判断到底是不是刚创建出来的(新的)
true 表示刚创建
false 表示获取之前创建
每个会话都有一个身份证号。也就是 ID 值。而且这个 ID 是唯一的。
getId() 得到 Session 的会话 id 值。

k)Session 域数据的存取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
往 Session 中保存数据
*/
protected void setAttribute(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
req.getSession().setAttribute("key1", "value1");
resp.getWriter().write("已经往 Session 中保存了数据");
}
/*
获取 Session 域中的数据
*/
protected void getAttribute(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
Object attribute = req.getSession().getAttribute("key1");
resp.getWriter().write("从 Session 中获取出 key1 的数据是:" + attribute);
}

l) Session 生命周期控制

public void setMaxInactiveInterval(int interval) 设置 Session 的超时时间(以秒为单位),超过指定的时长,Session
就会被销毁。
值为正数的时候,设定 Session 的超时时长。
负数表示永不超时(极少使用)
public int getMaxInactiveInterval()获取 Session 的超时时间
public void invalidate() 让当前 Session 会话马上超时无效。
Session 默认的超时时长是多少!
Session 默认的超时时间长为 30 分钟。
因为在 Tomcat 服务器的配置文件 web.xml中默认有以下的配置,它就表示配置了当前 Tomcat 服务器下所有的 Session
超时配置默认时长为:30 分钟

如果说。你希望你的 web 工程,默认的 Session 的超时时长为其他时长。你可以在你自己的 web.xml 配置文件中做 以上相同的配置。就可以修改你的 web 工程所有 Seession 的默认超时时长。

1
2
3
4
<!--表示当前 web 工程。创建出来 的所有 Session 默认是 20 分钟 超时时长-->
<session-config>
<session-timeout>20</session-timeout>
</session-config>

如果你想只修改个别 Session 的超时时长。就可以使用上面的 API。

setMaxInactiveInterval(int interval)来进行单独的设 置。

session.setMaxInactiveInterval(int interval)单独设置超时时长。

Session 超时的概念介绍:

image-20211128184457126-

1
2
3
4
5
6
7
8
protected void life3(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
// 先获取 Session 对象
HttpSession session = req.getSession();
// 设置当前 Session3 秒后超时
session.setMaxInactiveInterval(3);
resp.getWriter().write("当前 Session 已经设置为 3 秒后超时");
}

session马上超时

1
2
3
4
5
6
7
8
protected void deleteNow(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
// 先获取 Session 对象
HttpSession session = req.getSession();
// 让 Session 会话马上超时
session.invalidate();
resp.getWriter().write("Session 已经设置为超时(无效)");
}

m) 浏览器和 Session 之间关联的技术内幕

Session 技术,底层其实是基于 Cookie 技术来实现

image-20211128190149458

项目第六阶段

3.1、登陆—显示用户名

UserServlet 程序中保存用户登录的信息image-20211128195448625

修改 login_succuess_menu.jsp

image-20211128195504375

还要修改首页 index.jsp

image-20211128195520910

3.2、登出—注销用户

1、销毁 Session 中用户登录的信息(或者销毁 Session)

2、重定向到首页(或登录页面)。

UserServlet 程序中添加 logout 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 注销
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void logout(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
// 1、销毁 Session 中用户登录的信息(或者销毁 Session)
req.getSession().invalidate();
// 2、重定向到首页(或登录页面)。
resp.sendRedirect(req.getContextPath());
}

修改【注销】的菜单地址

[注销](userServlet?action=logout)

3.3、表单重复提交之—–验证码

表单重复提交有三种常见的情况:

一:提交完表单。服务器使用请求转来进行页面跳转。这个时候,用户按下功能键 F5,就会发起最后一次的请求。 造成表单重复提交问题。解决方法:使用重定向来进行跳转

二:用户正常提交服务器,但是由于网络延迟等原因,迟迟未收到服务器的响应,这个时候,用户以为提交失败, 就会着急,然后多点了几次提交操作,也会造成表单重复提交。

三:用户正常提交服务器。服务器也没有延迟,但是提交完成后,用户回退浏览器。重新提交。也会造成表单重复 提交。

image-20211128204706741

3.4、谷歌 kaptcha 图片验证码的使用

谷歌验证码 kaptcha 使用步骤如下:

1、导入谷歌验证码的 jar 包 kaptcha-2.3.2.jar

2、在 web.xml 中去配置用于生成验证码的 Servlet 程序

1
2
3
4
5
6
7
8
servlet>
<servlet-name>KaptchaServlet</servlet-name>
<servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>KaptchaServlet</servlet-name>
<url-pattern>/kaptcha.jpg</url-pattern>
</servlet-mapping>

3、在表单中使用 img 标签去显示验证码图片并使用它

4、在服务器获取谷歌生成的验证码和客户端发送过来的验证码比较使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//获取请求的参数
String username = request.getParameter("username");
String password = request.getParameter("password");
String email = request.getParameter("email");
String code = request.getParameter("code");

//获取session中的验证码
String token = (String) request.getSession().getAttribute(KAPTCHA_SESSION_KEY);
//马上删除session域中的验证码
request.getSession().removeAttribute(KAPTCHA_SESSION_KEY);

//查看验证码是否正确
if (token != null && token.equalsIgnoreCase(code)) {
//正确
...

切换验证码:

1
2
3
4
5
6
7
// 给验证码的图片,绑定单击事件
$("#code_img").click(function () {
// 在事件响应的 function 函数中有一个 this 对象。这个 this 对象,是当前正在响应事件的 dom 对象
// src 属性表示验证码 img 标签的 图片路径。它可读,可写
// alert(this.src);
this.src = "${basePath}kaptcha.jpg?d=" + new Date();
});

1、项目第六阶段:购物车

1.1、购物车模块分析

image-20211128222245216

1.2、购物车模型编写

1.2.1、购物车模型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
/**
* 购物车的商品项
*/
public class CartItem {
private Integer id;
private String name;
private Integer count;
private BigDecimal price;
private BigDecimal totalPrice;
/**
* 购物车对象
*/
public class Cart {
// private Integer totalCount;
// private BigDecimal totalPrice;
/**
* key 是商品编号,
* value,是商品信息
*/
private Map<Integer,CartItem> items = new LinkedHashMap<Integer,CartItem>();
/**
* 添加商品项
*
* @param cartItem
*/
public void addItem(CartItem cartItem) {
// 先查看购物车中是否已经添加过此商品,如果已添加,则数量累加,总金额更新,如果没有添加过,直接放到
集合中即可
CartItem item = items.get(cartItem.getId());
if (item == null) {
// 之前没添加过此商品
items.put(cartItem.getId(), cartItem);
} else {
// 已经 添加过的情况
item.setCount( item.getCount() + 1 ); // 数量 累加
item.setTotalPrice( item.getPrice().multiply(new BigDecimal( item.getCount() )) ); // 更
新总金额
}
}
/**
* 删除商品项
*/
public void deleteItem(Integer id) {
items.remove(id);
}
/**
* 清空购物车
*/
public void clear() {
items.clear();
}
/**
* 修改商品数量
*/
public void updateCount(Integer id,Integer count) {
// 先查看购物车中是否有此商品。如果有,修改商品数量,更新总金额
CartItem cartItem = items.get(id);
if (cartItem != null) {
cartItem.setCount(count);// 修改商品数量
cartItem.setTotalPrice( cartItem.getPrice().multiply(new
BigDecimal( cartItem.getCount() )) ); // 更新总金额
}
}
public Integer getTotalCount() {
Integer totalCount = 0;
for (Map.Entry<Integer,CartItem>entry : items.entrySet()) {
totalCount += entry.getValue().getCount();
}
return totalCount;
}
public BigDecimal getTotalPrice() {
BigDecimal totalPrice = new BigDecimal(0);
for (Map.Entry<Integer,CartItem>entry : items.entrySet()) {
totalPrice = totalPrice.add(entry.getValue().getTotalPrice());
}
return totalPrice;
}
public Map<Integer, CartItem> getItems() {
return items;
}
public void setItems(Map<Integer, CartItem> items) {
this.items = items;
}
@Override
public String toString() {
return "Cart{" +
"totalCount=" + getTotalCount() +
", totalPrice=" + getTotalPrice() +
", items=" + items +
'}';
}
}

1.3、加入购物车功能的实现

cartServlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* 加入购物车
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void addItem(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
// 获取请求的参数 商品编号
int id = WebUtils.parseInt(req.getParameter("id"), 0);
// 调用 bookService.queryBookById(id):Book 得到图书的信息
Book book = bookService.queryBookById(id);
// 把图书信息,转换成为 CartItem 商品项
CartItem cartItem = new CartItem(book.getId(),book.getName(),1,book.getPrice(),book.getPrice());
// 调用 Cart.addItem(CartItem);添加商品项
Cart cart = (Cart) req.getSession().getAttribute("cart");
if (cart == null) {
cart = new Cart();
req.getSession().setAttribute("cart",cart);
}
cart.addItem(cartItem);
System.out.println(cart);
System.out.println("请求头 Referer 的值:" + req.getHeader("Referer"));
// 重定向回原来商品所在的地址页面
resp.sendRedirect(req.getHeader("Referer"));
}

index.jsp 页面 js 的代码:

image-20211129161320733

1
2
3
4
5
6
7
8
9
10
11
12
13
<Script type="text/javascript">
$(function () {
// 给加入购物车按钮绑定单击事件
$("button.addToCart").click(function () {
/**
* 在事件响应的 function 函数 中,有一个 this 对象,这个 this 对象,是当前正在响应事件的 dom 对象
* @type {jQuery}
*/
var bookId = $(this).attr("bookId");
location.href = "http://localhost:8080/book/cartServlet?action=addItem&id=" + bookId;
});
});
</Script>

图解说明,如何跳回添加商品的页面:

image-20211129161359500

1.4、购物车的展示

image-20211129161508956

1.5、删除购物车商品项

CartServlet 程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 删除商品项
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void deleteItem(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException{
// 获取商品编号
int id = WebUtils.parseInt(req.getParameter("id"), 0);
// 获取购物车对象
Cart cart = (Cart) req.getSession().getAttribute("cart");
if (cart != null) {
// 删除 了购物车商品项
cart.deleteItem(id);
// 重定向回原来购物车展示页面
resp.sendRedirect(req.getHeader("Referer"));
}
}

购物车/pages/cart/cart.jsp 页面的代码:

删除的请求地址:

image-20211129200354110

删除的确认提示操作:

image-20211129200410500

1.6、清空购物车

1.7、修改购物车商品数量

CartServlet 程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 修改商品数量
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void updateCount(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException{
// 获取请求的参数 商品编号 、商品数量
int id = WebUtils.parseInt(req.getParameter("id"),0);
int count = WebUtils.parseInt(req.getParameter("count"), 1);
// 获取 Cart 购物车对象
Cart cart = (Cart) req.getSession().getAttribute("cart");
if (cart != null) {
// 修改商品数量
cart.updateCount(id,count);
// 重定向回原来购物车展示页面
resp.sendRedirect(req.getHeader("Referer"));
}
}

修改 pages/cart/cart.jsp 购物车页面

image-20211129212121779

修改商品数量 js代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 给输入框绑定 onchange 内容发生改变事件
$(".updateCount").change(function () {
// 获取商品名称
var name = $(this).parent().parent().find("td:first").text();
var id = $(this).attr('bookId');
// 获取商品数量
var count = this.value;
if ( confirm("你确定要将【" + name + "】商品修改数量为:" + count + " 吗?") ) {
//发起请求。给服务器保存修改
location.href =
"http://localhost:8080/book/cartServlet?action=updateCount&count="+count+"&id="+id;
} else {
// defaultValue 属性是表单项 Dom 对象的属性。它表示默认的 value 属性值。
this.value = this.defaultValue;
}
});

1.8、首页,购物车数据回显

在添加商品到购物车的时候,保存最后一个添加的商品名称:

image-20211129213555886

在 pages/client/index.jsp 页面中输出购物车信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<div style="text-align: center">
<c:if test="${empty sessionScope.cart.items}">
<%--购物车为空的输出--%>
<span> </span>
<div>
<span style="color: red">当前购物车为空</span>
</div>
</c:if>
<c:if test="${not empty sessionScope.cart.items}">
<%--购物车非空的输出--%>
<span>您的购物车中有 ${sessionScope.cart.totalCount} 件商品</span>
<div>
您刚刚将<span style="color: red">${sessionScope.lastName}</span>加入到了购物车中
</div>
</c:if>
</div>

2、项目第七阶段:订单

2.1、订单模块的分析:

image-20211129215246972

2.2:订单模块的实现

2.2.1、创建订单模块的数据库表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
create table t_order(
`order_id` varchar(50) primary key,
`create_time` datetime,
`price` decimal(11,2),
`status` int,
`user_id` int,
foreign key(`user_id`) references t_user(`id`)
);

create table t_order_item(
`id` int primary key auto_increment,
`name` varchar(100),
`count` int,
`price` decimal(11,2),
`total_price` decimal(11,2),
`order_id` varchar(50),
foreign key(`order_id`) references t_order(`order_id`)
);

2.2.2、创建订单模块的数据模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 订单
*/
public class Order {
private String orderId;
private Date createTime;
private BigDecimal price;
// 0 未发货,1 已发货,2 表示已签收
private Integer status = 0;
private Integer userId;
/**
* 订单项
*/
public class OrderItem {
private Integer id;
private String name;
private Integer count;
private BigDecimal price;
private BigDecimal totalPrice;
private String orderId;

2.2.3、编写订单模块的 Dao 程序和测试

OrderDao 接口

1
2
3
public interface OrderDao {
public int saveOrder(Order order);
}

OrderDao 实现

1
2
3
4
5
6
7
8
9
10
11
public class OrderDaoImpl extends BaseDao implements OrderDao {
@Override
public int saveOrder(Order order) {
String sql = "insert into t_order(`order_id`,`create_time`,`price`,`status`,`user_id`)
values(?,?,?,?,?)";
return
update(sql,order.getOrderId(),order.getCreateTime(),order.getPrice(),order.getStatus(),order.getUs
erId());
}
}

OrderItemDao 接口和实现类似

2.2.4、编写订单模块的 Service 和测试

OrderService 接口

1
2
3
public interface OrderService {
public String createOrder(Cart cart,Integer userId);
}

OrderService 实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class OrderServiceImpl implements OrderService {
private OrderDao orderDao = new OrderDaoImpl();
private OrderItemDao orderItemDao = new OrderItemDaoImpl();
@Override
public String createOrder(Cart cart, Integer userId) {
// 订单号===唯一性
String orderId = System.currentTimeMillis()+""+userId;
// 创建一个订单对象
Order order = new Order(orderId,new Date(),cart.getTotalPrice(), 0,userId);
// 保存订单
orderDao.saveOrder(order);
// 遍历购物车中每一个商品项转换成为订单项保存到数据库
for (Map.Entry<Integer, CartItem>entry : cart.getItems().entrySet()){
// 获取每一个购物车中的商品项
CartItem cartItem = entry.getValue();
// 转换为每一个订单项
OrderItem orderItem = new
OrderItem(null,cartItem.getName(),cartItem.getCount(),cartItem.getPrice(),cartItem.getTotalPrice(),
orderId);
// 保存订单项到数据库
orderItemDao.saveOrderItem(orderItem);
}
// 清空购物车
cart.clear();
return orderId;
}
}

2.2.5、编写订单模块的 web和联调

修改 OrderService 程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class OrderServiceImpl implements OrderService {
private OrderDao orderDao = new OrderDaoImpl();
private OrderItemDao orderItemDao = new OrderItemDaoImpl();
private BookDao bookDao = new BookDaoImpl();
@Override
public String createOrder(Cart cart, Integer userId) {
// 订单号===唯一性
String orderId = System.currentTimeMillis()+""+userId;
// 创建一个订单对象
Order order = new Order(orderId,new Date(),cart.getTotalPrice(), 0,userId);
// 保存订单
orderDao.saveOrder(order);
// 遍历购物车中每一个商品项转换成为订单项保存到数据库
for (Map.Entry<Integer, CartItem>entry : cart.getItems().entrySet()){
// 获取每一个购物车中的商品项
CartItem cartItem = entry.getValue();
// 转换为每一个订单项
OrderItem orderItem = new
OrderItem(null,cartItem.getName(),cartItem.getCount(),cartItem.getPrice(),cartItem.getTotalPrice(),
orderId);
// 保存订单项到数据库
orderItemDao.saveOrderItem(orderItem);
// 更新库存和销量
Book book = bookDao.queryBookById(cartItem.getId());
book.setSales( book.getSales() + cartItem.getCount() );
book.setStock( book.getStock() - cartItem.getCount() );
bookDao.updateBook(book);
}
// 清空购物车
cart.clear();
return orderId;
}
}

OrderServlet 程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class OrderServlet extends BaseServlet {
private OrderService orderService = new OrderServiceImpl();
/**
* 生成订单
*
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void createOrder(HttpServletRequest req, HttpServletResponse resp) throws
ServletException, IOException {
// 先获取 Cart 购物车对象
Cart cart = (Cart) req.getSession().getAttribute("cart");
// 获取 Userid
User loginUser = (User) req.getSession().getAttribute("user");
if (loginUser == null) {
req.getRequestDispatcher("/pages/user/login.jsp").forward(req,resp);
return;
}
Integer userId = loginUser.getId();
// 调用 orderService.createOrder(Cart,Userid);生成订单
String orderId = orderService.createOrder(cart, userId);
// req.setAttribute("orderId", orderId);
// 请求转发到/pages/cart/checkout.jsp
// req.getRequestDispatcher("/pages/cart/checkout.jsp").forward(req, resp);
req.getSession().setAttribute("orderId",orderId);
resp.sendRedirect(req.getContextPath()+"/pages/cart/checkout.jsp");
}
}

修改 pages/cart/cart.jsp 页面,结账的请求地址

image-20211130131548477

修改 pages/cart/checkout.jsp 页面,输出订单号

image-20211130131605934

Filter过滤器

1、Filter 什么是过滤器

1、Filter 过滤器它是 JavaWeb 的三大组件之一。

三大组件分别是:Servlet 程序、Listener 监听器、Filter 过滤器

2、Filter 过滤器它是 JavaEE 的规范。也就是接口

3、Filter 过滤器它的作用是:拦截请求,过滤响应。

拦截请求常见的应用场景有:

 1、权限检查 

​ 2、日记操作

​ 3、事务管理 ……等等

2、Filter 的初体验

要求:在你的 web 工程下,有一个 admin 目录。这个 admin 目录下的所有资源(html 页面、jpg 图片、jsp 文件、等等)都必 须是用户登录之后才允许访问。

思考:根据之前我们学过内容。我们知道,用户登录之后都会把用户登录的信息保存到 Session 域中。所以要检查用户是否 登录,可以判断 Session 中否包含有用户登录的信息即可!!!

Filter 的工作流程图

image-20211130150125522

Filter 的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class AdminFilter implements Filter {
/**
* doFilter 方法,专门用于拦截请求。可以做权限检查
*/
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain
filterChain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
HttpSession session = httpServletRequest.getSession();
Object user = session.getAttribute("user");
// 如果等于 null,说明还没有登录
if (user == null) {
servletRequest.getRequestDispatcher("/login.jsp").forward(servletRequest,servletResponse);
return;
} else {
// 让程序继续往下访问用户的目标资源
filterChain.doFilter(servletRequest,servletResponse);
}
}
}

web.xml 中的配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!--filter 标签用于配置一个 Filter 过滤器-->
<filter>
<!--给 filter 起一个别名-->
<filter-name>AdminFilter</filter-name>
<!--配置 filter 的全类名-->
<filter-class>com.atguigu.filter.AdminFilter</filter-class>
</filter>
<!--filter-mapping 配置 Filter 过滤器的拦截路径-->
<filter-mapping>
<!--filter-name 表示当前的拦截路径给哪个 filter 使用-->
<filter-name>AdminFilter</filter-name>
<!--url-pattern 配置拦截路径
/ 表示请求地址为:http://ip:port/工程路径/ 映射到 IDEA 的 web 目录
/admin/* 表示请求地址为:http://ip:port/工程路径/admin/*
-->
<url-pattern>/admin/*</url-pattern>
</filter-mapping>

Filter 过滤器的使用步骤:

1、编写一个类去实现 Filter 接口

2、实现过滤方法 doFilter()

3、到 web.xml 中去配置 Filter 的拦截路

3、Filter 的生命周期

Filter 的生命周期包含几个方法

1、构造器方法

2、init 初始化方法

​ 第 1,2 步,在 web 工程启动的时候执行(Filter 已经创建)

3、doFilter 过滤方法

​ 第 3 步,每次拦截到请求,就会执行

4、destroy 销毁

​ 第 4 步,停止 web 工程的时候,就会执行(停止 web 工程,也会销毁 Filter 过滤

4、FilterConfig 类

FilterConfig 类见名知义,它是 Filter 过滤器的配置文件类。

Tomcat 每次创建 Filter 的时候,也会同时创建一个 FilterConfig 类,

这里包含了 Filter 配置文件的配置信息。

FilterConfig 类的作用是获取 filter 过滤器的配置内容

1、获取 Filter 的名称 filter-name 的内容

2、获取在 Filter 中配置的 init-param 初始化参数

3、获取 ServletContext 对象

1
2
3
4
5
6
7
8
9
10
11
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("2.Filter 的 init(FilterConfig filterConfig)初始化");
// 1、获取 Filter 的名称 filter-name 的内容
System.out.println("filter-name 的值是:" + filterConfig.getFilterName());
// 2、获取在 web.xml 中配置的 init-param 初始化参数
System.out.println("初始化参数 username 的值是:" + filterConfig.getInitParameter("username"));
System.out.println("初始化参数 url 的值是:" + filterConfig.getInitParameter("url"));
// 3、获取 ServletContext 对象
System.out.println(filterConfig.getServletContext());
}

web.xml配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
web.xml 配置:
<!--filter 标签用于配置一个 Filter 过滤器-->
<filter>
<!--给 filter 起一个别名-->
<filter-name>AdminFilter</filter-name>
<!--配置 filter 的全类名-->
<filter-class>com.atguigu.filter.AdminFilter</filter-class>
<init-param>
<param-name>username</param-name>
<param-value>root</param-value>
</init-param>
<init-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost3306/test</param-value>
</init-param>
</filter>

5、FilterChain 过滤器链

Filter 过滤器

Chain 链,链条

FilterChain 就是过滤器链(多个过滤器如何一起工作)

image-20211130191218830

6、Filter 的拦截路径

–精确匹配

<url-patter>/target.jsp</url-pattern>

以上配置的路径,表示请求地址必须为:http://ip:port/工程路径/target.jsp

–目录匹配

<url-patter>/admin/*</url-pattern>

以上配置的路径,表示请求地址必须为:http://ip:port/工程路径/admin/*

–后缀名匹配

<url-patter>*.html</url-pattern>

以上配置的路径,表示请求地址必须以.html 结尾才会拦截到

Filter 过滤器它只关心请求的地址是否匹配,不关心请求的资源是否存在!!!

7、书城第八阶段:

1、使用 Filter 过滤器拦截/pages/manager/所有内容,实 现权限检查

Filter代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ManagerFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
Object user = httpServletRequest.getSession().getAttribute("user");
if (user == null) {
httpServletRequest.getRequestDispatcher("/pages/user/login.jsp").forward(servletRequest,servletResponse);

} else {
filterChain.doFilter(servletRequest,servletResponse);
}
}
@Override
public void destroy() {
}
}

web.xml 中的配置:

1
2
3
4
5
6
7
8
9
<filter>
<filter-name>ManagerFilter</filter-name>
<filter-class>com.izumi.filter.ManagerFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ManagerFilter</filter-name>
<url-pattern>/pages/manager/*</url-pattern>
<url-pattern>/manager/bookServlet</url-pattern>
</filter-mapping>

2、ThreadLocal 的使用

ThreadLocal 的作用,它可以解决多线程的数据安全问题。

ThreadLocal 它可以给当前线程关联一个数据(可以是普通变量,可以是对象,也可以是数组,集合)

ThreadLocal 的特点:

1、ThreadLocal 可以为当前线程关联一个数据。(它可以像 Map 一样存取数据,key 为当前线程)

2、每一个 ThreadLocal 对象,只能为当前线程关联一个数据,如果要为当前线程关联多个数据,

就需要使用多个 ThreadLocal 对象实例。

3、每个 ThreadLocal 对象实例定义的时候,一般都是 static 类型 4、ThreadLocal 中保存数据,

在线程销毁后。会由 JVM 虚拟自动释放

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class OrderService {
public void createOrder(){
String name = Thread.currentThread().getName();
System.out.println("OrderService 当前线程[" + name + "]中保存的数据是:" +
ThreadLocalTest.threadLocal.get());
new OrderDao().saveOrder();
}
}
public class OrderDao {
public void saveOrder(){
String name = Thread.currentThread().getName();
System.out.println("OrderDao 当前线程[" + name + "]中保存的数据是:" +
ThreadLocalTest.threadLocal.get());
}
}
public class ThreadLocalTest {
// public static Map<String,Object> data = new Hashtable<String,Object>();
public static ThreadLocal<Object> threadLocal = new ThreadLocal<Object>();
private static Random random = new Random();
public static class Task implements Runnable {
@Override
public void run() {
// 在 Run 方法中,随机生成一个变量(线程要关联的数据),然后以当前线程名为 key 保存到 map 中
Integer i = random.nextInt(1000);
// 获取当前线程名
String name = Thread.currentThread().getName();
System.out.println("线程["+name+"]生成的随机数是:" + i);
// data.put(name,i);
threadLocal.set(i);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
new OrderService().createOrder();
// 在 Run 方法结束之前,以当前线程名获取出数据并打印。查看是否可以取出操作
// Object o = data.get(name);
Object o = threadLocal.get();
System.out.println("在线程["+name+"]快结束时取出关联的数据是:" + o);
}
}
public static void main(String[] args) {
for (int i = 0; i < 3; i++){
new Thread(new Task()).start();
}
}
}

3、使用 Filter 和 ThreadLocal 组合管理事务

3.1、使用 ThreadLocal 来确保所有 dao 操作都在同一个 Connection 连接对象中完 成

原理分析图:

image-20211130221720112

JdbcUtils 工具类的修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
public class JdbcUtils {
private static DruidDataSource dataSource;
private static ThreadLocal<Connection> conns = new ThreadLocal<Connection>();
static {
try {
Properties properties = new Properties();
// 读取 jdbc.properties 属性配置文件
InputStream inputStream =
JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
// 从流中加载数据
properties.load(inputStream);
// 创建 数据库连接 池
dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取数据库连接池中的连接
* @return 如果返回 null,说明获取连接失败<br/>有值就是获取连接成功
*/
public static Connection getConnection(){
Connection conn = conns.get();
if (conn == null) {
try {
conn = dataSource.getConnection();//从数据库连接池中获取连接
conns.set(conn); // 保存到 ThreadLocal 对象中,供后面的 jdbc 操作使用
conn.setAutoCommit(false); // 设置为手动管理事务
} catch (SQLException e) {
e.printStackTrace();
}
}
return conn;
}
/**
* 提交事务,并关闭释放连接
*/
public static void commitAndClose(){
Connection connection = conns.get();
if (connection != null) { // 如果不等于 null,说明 之前使用过连接,操作过数据库
try {
connection.commit(); // 提交 事务
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
connection.close(); // 关闭连接,资源资源
} catch (SQLException e) {
e.printStackTrace();
}
}
}
// 一定要执行 remove 操作,否则就会出错。(因为 Tomcat 服务器底层使用了线程池技术)
conns.remove();
}
/**
* 回滚事务,并关闭释放连接
*/
public static void rollbackAndClose(){
Connection connection = conns.get();
if (connection != null) { // 如果不等于 null,说明 之前使用过连接,操作过数据库
try {
connection.rollback();//回滚事务
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
connection.close(); // 关闭连接,资源资源
} catch (SQLException e) {
e.printStackTrace();
}
}
}
// 一定要执行 remove 操作,否则就会出错。(因为 Tomcat 服务器底层使用了线程池技术)
conns.remove();
}
/**
* 关闭连接,放回数据库连接池
* @param conn
public static void close(Connection conn){
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
} */
}

修改 BaseDao

//将 每个 catch抛出去,

例如:

1
2
3
4
5
6
try {
return queryRunner.update(connection, sql, args);
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}

3.2、使用 Filter 过滤器统一给所有的 Service 方法都加上 try-catch。来进行实现的 管理。

原理分析图

image-20211130224808210

Filter 类代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class TransactionFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain
filterChain) throws IOException, ServletException {
try {
filterChain.doFilter(servletRequest,servletResponse);
JdbcUtils.commitAndClose();// 提交事务
} catch (Exception e) {
JdbcUtils.rollbackAndClose();//回滚事务
e.printStackTrace();
}
}
}
在 web.xml 中的配置:
<filter>
<filter-name>TransactionFilter</filter-name>
<filter-class>com.atguigu.filter.TransactionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>TransactionFilter</filter-name>
<!-- /* 表示当前工程下所有请求 -->
<url-pattern>/*</url-pattern>
</filter-mapping>

一定要记得把 BaseServlet 中的异常往外抛给 Filter 过滤器

3.3、将所有异常都统一交给 Tomcat,让 Tomcat 展示友好的错误

web.xml配置页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!--error-page 标签配置,服务器出错之后,自动跳转的页面-->
<error-page>
<!--error-code 是错误类型-->
<error-code>500</error-code>
<!--location 标签表示。要跳转去的页面路径-->
<location>/pages/error/error500.jsp</location>
</error-page>
<!--error-page 标签配置,服务器出错之后,自动跳转的页面-->
<error-page>
<!--error-code 是错误类型-->
<error-code>404</error-code>
<!--location 标签表示。要跳转去的页面路径-->
<location>/pages/error/error404.jsp</location>
</error-page>

JSON、AJAX、i18n

1、什么是 JSON?

JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式。

易于人阅读和编写。同时也易于机器解析和生成。

JSON 采用完全独立于语言的文本格式,而且很多语言都提供了对 json 的支持(包括 C, C++, C#, Java, JavaScript, Perl, Python 等)。

这样就使得 JSON 成为理想的数据交换格式。

json 是一种轻量级的数据交换格式。

轻量级指的是跟 xml 做比较。 数据交换指的是客户端和服务器之间业务数据的传递格式。

1.1、JSON 在 JavaScript 中的使用。

1.1.1、json 的定义 json 是由键值对组成,并且由花括号(大括号)包围。

每个键由引号引起来,键和值之间使用冒号进行分隔, 多组键值对之间进行逗号进行分隔。

json 定义示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// json的定义
var jsonObj = {
"key1": 12,
"key2": "value2",
"key3": true,
"key4": [11, "fsf", false],
"key5": {
"key551": 551,
"key_value88": "value88"
},
"key6": [
{"key6_1": 889, "key6_1_2": 612},
{"key6_2": 62, "key6_2_2": 622}
]

};

1.1.2、json 的访问

json 本身是一个对象。

json 中的 key 我们可以理解为是对象中的一个属性。

json 中的 key 访问就跟访问对象的属性一样:

json 对象.key json 访问示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// json的访问
// alert(jsonObj.key1)
// alert(jsonObj.key2)
// alert(jsonObj.key3)
// alert(jsonObj.key4)
// for (var i = 0; i<jsonObj.key4.length;i++){
// alert(jsonObj.key4[i])
// }
// alert(jsonObj.key5.key551)
// alert(jsonObj.key5.key_value88)
/* 取出的 每一个jsonItem都是 json对象
var jsonItem = jsonObj.key6[0]
// alert(jsonItem.key6_1)
alert(jsonItem.key6_1_2)*/
var jsonItem2 = jsonObj.key6[1];
alert(jsonItem2.key6_2_2)

1.1.3、json 的两个常用方法

json 的存在有两种形式。

一种是:对象的形式存在,我们叫它 json 对象。

一种是:字符串的形式存在,我们叫它 json 字符串。

一般我们要操作 json 中的数据的时候,需要 json 对象的格式。

一般我们要在客户端和服务器之间进行数据交换的时候,使用 json 字符串。

JSON.stringify() 把 json 对象转换成为 json 字符串

JSON.parse() 把 json 字符串转换成为 json 对象

1
2
3
4
5
6
// json对象转字符串
var jsonObjString = JSON.stringify(jsonObj);
alert(jsonObjString)
// json字符串转json对象
var jsonObj2 = JSON.parse(jsonObjString);
alert(jsonObj2.key1)

1.2、JSON 在 java 中的使用

1.2.1、javaBean 和 json 的互转

1
2
3
4
5
6
7
8
9
10
11
12
public void test1(){
//javaBean 和 Json的转换
Person person = new Person(1, "天干物燥");
//创建 Gson对象示例
Gson gson = new Gson();
//调用 gson 对象的 方法 toJson 将 javaBean对象转换成 json 字符串类型
String jsonString = gson.toJson(person);
System.out.println(jsonString);
//调用 gson对象 的 fromJson方法 可以将 json对象转换回 JavaBean对象
Person person1 = gson.fromJson(jsonString, Person.class);
System.out.println(person1);
}

1.2.2、List 和 json 的互转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class PersonListType extends TypeToken<List<Person>> {
//创建一个 类继承 com.google.gson.reflect.TypeToken类,泛型写上json转回 集合的类型
}
public void test2(){
//json 和 list 对象 的互转
List<Person> personList = new ArrayList<>();
personList.add(new Person(1,"steve"));
personList.add(new Person(2,"niko"));
//创建 Gson对象示例
Gson gson = new Gson();
String toJson = gson.toJson(personList);
System.out.println(toJson);
//-------------------------
//json转 list集合
List<Person> personList1 = gson.fromJson(toJson, new PersonListType().getType());
System.out.println( personList1.get(0));
}

1.2.3、map 和 json 的互转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void test3(){
//json 和 map 的相互转换
Map<Integer,Person> personMap = new HashMap<>();
personMap.put(1,new Person(1,"乔布斯"));
personMap.put(2,new Person(2,"图灵"));
//创建 Gson 对象实例
Gson gson = new Gson();
String toJson = gson.toJson(personMap);
System.out.println(toJson);

//将json 转换回 map集合,这里使用到匿名内部类
Map<Integer,Person> personMap2 = gson.fromJson(toJson, new TypeToken<HashMap<Integer, Person>>(){}.getType());
System.out.println(personMap2.getClass());

}

2、AJAX 请求

2.1、什么是 AJAX 请求

AJAX 即“Asynchronous Javascript And XML”(异步 JavaScript 和 XML),是指一种创建交互式网页应用的网页开发 技术。

ajax 是一种浏览器通过 js 异步发起请求,局部更新页面的技术。 Ajax 请求的局部更新,浏览器地址栏不会发生变化 局部更新不会舍弃原来页面的内容

2.2、原生 AJAX 请求的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function ajaxRequest() {
// 1、我们首先要创建XMLHttpRequest
var xmlHttpRequest = new XMLHttpRequest();
// 2、调用open方法设置请求参数 true表示异步,false表示同步
xmlHttpRequest.open("get","http://localhost:8080/json_ajax_i18n/ajaxServlet?action=javaScriptAjax",true)
//
// 4、在send方法前绑定onreadystatechange事件,处理请求完成后的操作。
xmlHttpRequest.onreadystatechange = function () {
if (xmlHttpRequest.readyState==4 &&xmlHttpRequest.status==200){
var jsonObj =JSON.parse(xmlHttpRequest.responseText);
//把响应的数据显示在页面上
document.getElementById("div01").innerHTML="id:"+jsonObj.id+" 姓名:"+jsonObj.name

}
}
// 3、调用send方法发送请求
xmlHttpRequest.send();
}

2.3、jQuery 中的 AJAX 请求

$.ajax 方法
url 表示请求的地址
type 表示请求的类型 GET 或 POST 请求
data 表示发送给服务器的数据
格式有两种:
一:name=value&name=value
二:{key:value}
success 请求成功,响应的回调函数
dataType 响应的数据类型
常用的数据类型有:
text 表示纯文本
xml 表示 xml 数据
json 表示 json 对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$("#ajaxBtn").click(function(){
$.ajax({
url:"http://localhost:8080/16_json_ajax_i18n/ajaxServlet",
// data:"action=jQueryAjax",
data:{action:"jQueryAjax"},
type:"GET",

success:function (data) {
// alert("服务器返回的数据是:" + data);
// var jsonObj = JSON.parse(data);
$("#msg").html("编号:" + data.id + " , 姓名:" + data.name);},

dataType : "json"
});
});

**$.get 方法和 $.post 方法 **

url 请求的 url 地址

data 发送的数据

callback 成功的回调函数

type 返回的数据类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// ajax--get 请求
$("#getBtn").click(function(){
$.get("http://localhost:8080/16_json_ajax_i18n/ajaxServlet","action=jQueryGet",
function (data) {
$("#msg").html(" get 编号:" + data.id + " , 姓名:" + data.name);
},"json");
});
// ajax--post 请求
$("#postBtn").click(function(){
$.post("http://localhost:8080/16_json_ajax_i18n/ajaxServlet","action=jQueryPost",
function (data){
$("#msg").html(" post 编号:" + data.id + " , 姓名:" + data.name);
},"json");
});

**$.getJSON 方法 **

url 请求的 url 地址

data 发送给服务器的数据

callback 成功的回调函数

1
2
3
4
5
6
7
// ajax--getJson 请求
$("#getJSONBtn").click(function(){
$.getJSON("http://localhost:8080/16_json_ajax_i18n/ajaxServlet","action=jQueryGetJSON",
function(data) {
$("#msg").html(" getJSON 编号:" + data.id + " , 姓名:" + data.name);
});
});

**表单序列化 serialize() **

serialize()可以把表单中所有表单项的内容都获取到,并以 name=value&name=value 的形式进行拼接。

1
2
3
4
5
6
7
// ajax 请求
$("#submit").click(function(){
// 把参数序列化
$.getJSON("http://localhost:8080/16_json_ajax_i18n/ajaxServlet","action=jQuerySerialize&" +$("#form01").serialize(),function (data) {
$("#msg").html(" Serialize 编号:" + data.id + " , 姓名:" + data.name);
});
});

3、书城项目第九阶段

3.1、使用 AJAX 验证用户名是否可用

image-20211201201524384

UserServlet 程序中 ajaxExistsUsername 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
protected void ajaxExistsUsername(HttpServletRequest req, HttpServletResponse resp) throws
ServletException, IOException {
// 获取请求的参数 username
String username = req.getParameter("username");
// 调用 userService.existsUsername();
boolean existsUsername = userService.existsUsername(username);
// 把返回的结果封装成为 map 对象
Map<String,Object> resultMap = new HashMap<>();
resultMap.put("existsUsername",existsUsername);
Gson gson = new Gson();
String json = gson.toJson(resultMap);
resp.getWriter().write(json);
}

regist.jsp 页面中的代码:

1
2
3
4
5
6
7
8
9
10
11
$("#username").blur(function () {
//1 获取用户名
var username = this.value;
$.getJSON("http://localhost:8080/book/userServlet","action=ajaxExistsUsername&username=" +username,function (data) {
if (data.existsUsername) {
$("span.errorMsg").text("用户名已存在!");
} else {
$("span.errorMsg").text("用户名可用!");
}
});
});

3.2、使用 AJAX 修改把商品添加到购物车

image-20211201202055848

CartServlet 程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
protected void ajaxAddItem(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// System.out.println("调用 addItem方法");
// System.out.println("商品编号为:"+req.getParameter("id"));
Cart cart = (Cart) req.getSession().getAttribute("cart");
if (cart == null) {
cart = new Cart();
req.getSession().setAttribute("cart", cart);
}
Integer id = WebUtils.StringtoInteger(req.getParameter("id"), -1);
Book book = bookService.queryBookById(id);
CartItem cartItem = new CartItem(book.getId(), book.getName(), 1, book.getPrice(), book.getPrice());
cart.addItem(cartItem);
//保存最后一个 添加的商品项到 session域中
req.getSession().setAttribute("lastName",cartItem.getName());
//返回购物车总的商品数量和 添加的最后一个商品项
Map<String,Object> resultMap = new HashMap<>();
resultMap.put("totalCount",cart.getTotalCount());
resultMap.put("lastName",cartItem.getName());
Gson gson = new Gson();
String json = gson.toJson(resultMap);
resp.getWriter().write(json);


}

pages/client/index.jsp 页面 html 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<c:if test="${empty sessionScope.cart.items}">
<%-- 购物车为空 不显示--%>
<div style="text-align: center">
<span class="cartTotalCount">&nbsp;</span>
<div class="cartLastName">
<span style="color: red" >&nbsp;</span>
</div>
</div>
</c:if>
<c:if test="${not empty sessionScope.cart.items}">
<%-- 购物车非空--%>
<div style="text-align: center">
<%-- <span class="cartTotalCount">您的购物车中有${sessionScope.cart.totalCount}件商品</span>--%>
<span class="cartTotalCount"></span>
<div class="cartLastName">
<%-- 您刚刚将<span style="color: red" >${sessionScope.lastName}</span>加入到了购物车中--%>
</div>
</div>
</c:if>

js代码

1
2
3
4
5
6
7
8
9
10
11
12
//给加入购物车按钮绑定单击事件
$("button.addToCart").click(function () {
var bookId = $(this).attr("bookId");
// location.href="cartServlet?action=addItem&id=" + bookId;
//发 ajax 请求,添加商品到购物车
$.getJSON("cartServlet","action=ajaxAddItem&id="+bookId,function (data) {
$(".cartTotalCount").text("您的购物车中有"+data.totalCount+"件商品");
// $(".cartLastName").text(data.lastName);
$(".cartLastName").html("您刚刚将<span style='color: red' > "+data.lastName+" </span>加入到了购物车中")

})
})

4、i18n 国际化(了解内容)

什么是 i18n 国际化

 国际化(Internationalization)指的是同一个网站可以支持多种不同的语言,以方便不同国家,不同语种的用户访问。  关于国际化我们想到的最简单的方案就是为不同的国家创建不同的网站,比如苹果公司,他的英文官网是: http://www.apple.com 而中国官网是 http://www.apple.com/cn  苹果公司这种方案并不适合全部公司,而我们希望相同的一个网站,而不同人访问的时候可以根据用户所在的区域显示 不同的语言文字,而网站的布局样式等不发生改变。  于是就有了我们说的国际化,国际化总的来说就是同一个网站不同国家的人来访问可以显示出不同的语言。但实际上这 种需求并不强烈,一般真的有国际化需求的公司,主流采用的依然是苹果公司的那种方案,为不同的国家创建不同的页 面。所以国际化的内容我们了解一下即可。  国际化的英文 Internationalization,但是由于拼写过长,老外想了一个简单的写法叫做 I18N,代表的是 Internationalization 这个单词,以 I 开头,以 N 结尾,而中间是 18 个字母,所以简写为 I18N。以后我们说 I18N 和国际化是一个意思。

4.2、国际化相关要素介绍

image-20211201212824688