JavaWeb

JavaWeb

01-HTML CSS

1、B/S软件的结构

image-20220105221547608

2、前端的开发流程

image-20220105221629796

3、 网页的组成部分

页面由三部分内容组成!

分别是内容(结构)、表现、行为。

内容(结构),是我们在页面中可以看到的数据。我们称之为内容。一般内容 我们使用

html 技术来展示。

表现,指的是这些内容在页面上的展示形式。比如说。布局,颜色,大小等等。一般使用

CSS 技术实现

行为,指的是页面中元素与输入设备交互的响应。一般使用 javascript 技术实现

4 、HTML 简介

Hyper Text Markup Language (超文本标记语言)

简写:HTML

HTML 通过标签来标记要显示的网页中的各个部分。网页文件本身是一种文本文件,

通过在文本文件中添加标记符,可以告诉浏览器如何显示其中的内容(如:文字如何处理,画

面如何安排,图片如何显示等)

5 、创建 HTML

1 、创建一个 web 工程(静态的 web 工程)

image-20220105221909284

image-20220105221922828

image-20220105221928119

2、在工程下创建 html 页面

image-20220105221944367

选择浏览器执行页面

image-20220105221952124

第一个 html 示例:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>标题</title>
</head>
<body>
hello
</body>
</html>

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

6 、HTML 文件的书写规范

image-20220105222059022

Html 的代码注释

7 、HTML 标签介绍

1.标签的格式:
​ <标签名>封装的数据</标签名>
2.标签名大小写不敏感。
3.标签拥有自己的属性。
​ i. 分为基本属性: bgcolor="red" 可以修改简单的样式效果
​ ii. 事件属性: onclick="alert('你好!');" 可以直接设置事件响应后的代码。
4.标签又分为,单标签和双标签。
​ i. 单标签格式: <标签名 /> br 换行 hr 水平线
​ ii. 双标签格式: <标签名> ...封装的数据...</标签名>

image-20220105222250499

标签的语法:

<!-- ①标签不能交叉嵌套 -->
正确:<div><span>早安,尚硅谷</span></div>
错误:<div><span>早安,尚硅谷</div></span>

<!-- ②标签必须正确关闭 -->
<!-- i.有文本内容的标签: -->

正确:<div>早安,尚硅谷</div>
错误:<div>早安,尚硅谷

<!-- ii.没有文本内容的标签: -->
正确:<br />
错误:<br>

<!-- ③属性必须有值,属性值必须加引号 -->
正确:<font color="blue">早安,尚硅谷</font>
错误:<font color=blue>早安,尚硅谷</font>
错误:<font color>早安,尚硅谷</font>
    
 <!-- ④注释不能嵌套 -->
正确:<!-- 注释内容 --> <br/>
错误:<!-- <!-- 这是错误的 html 注释 --> -->

注意事项:
1.html 代码不是很严谨。有时候标签不闭合,也不会报错。

8 、常用标签介绍

8.1 、font 字体标签 字体标签

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

<body>
<!-- 字体标签
需求 1 :在网页上显示 我是字体标签 ,并修改字体为 宋体,颜色为红色。
font 标签是字体标签 , 它可以用来修改文本的字体 , 颜色 , 大小 ( 尺寸 )
color 属性修改颜色
face 属性修改字体
size 属性修改文本大小
-->
<font color="red" face=" 宋体" size="7">我是字体标签</font>
</body>

8.2、特殊字符

常用特殊字符表:

image-20220105222712411

其他特殊字符表:

image-20220105222726127

<body>
<!-- 特殊字符
需求 1 :把 <br> 换行标签 变成文本 转换成字符显示在页面上
常用的特殊字符 :
< ===>>>> &lt;
> ===>>>> &gt;
空格 ===>>>> &nbsp;
-->
我是&lt;br&gt;标签<br/>
国哥好
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;帅啊!
</body>

8.3、标题标签

标题标签是 h1 到 h6
求 需求 1 :演示标题 1 到 到 题 标题 6 的

<body>
<!-- 标题标签
需求 1 :演示标题 1 到 标题 6 的
h1 - h6 都是标题标签
h1 最大
h6 最小
align 属性是对齐属性
left 左对齐 ( 默认 )
center 剧中
right 右对齐
-->
<h1 align="left">标题 1</h1>
<h2 align="center">标题 2</h2>
<h3 align="right">标题 3</h3>
<h4>标题 4</h4>
<h5>标题 5</h5>
<h6>标题 6</h6>
<h7>标题 7</h7>
</body>

8.4、超链接 、超链接 ( **** 重 重 点 点 必 ,必 须 须 掌 掌 握 握 * )

在网页中所有点击之后可以跳转的内容都是超连接

需求 1的 :普通的 超连接。

<body>
<!-- a 标签是 超链接
href 属性设置连接的地址
target 属性设置哪个目标进行跳转
_self 表示当前页面 ( 默认值 )
_blank 表示打开新页面来进行跳转
-->
    <a href="http://localhost:8080">百度</a><br/>
<a href="http://localhost:8080" target="_self">百度_self</a><br/>
<a href="http://localhost:8080" target="_blank">百度_blank</a><br/>
</body>

8.5、列表标签

无序列表 、 有序列表

需求 1 :使用无序,列表方式,把东北 F4 ,赵四,刘能,小沈阳,宋小宝,展示出来

<body>
<!-- 需求 1 :使用无序,列表方式,把东北 F4 ,赵四,刘能,小沈阳,宋小宝,展示出来
ul 是无序列表
type 属性可以修改列表项前面的符号
li 是列表项
-->
<ul type="none">
<li>赵四</li>
<li>刘能</li>
<li>小沈阳</li>
<li>宋小宝</li>
</ul>
</body>

8.6 、img 标签

img 标签可以在 html 页面上显示图片。
求 需求 1 :使用 img

<body>
<!-- 需求 1 :使用 img 标签显示一张美女的照片。并修改宽高,和边框属性
img 标签是图片标签 , 用来显示图片
src 属性可以设置图片的路径
width 属性设置图片的宽度
height 属性设置图片的高度
border 属性设置图片边框大小
alt 属性设置当指定路径找不到图片时 , 用来代替显示的文本内容
在 JavaSE 中路径也分为相对路径和绝对路径 .
相对路径 : 从工程名开始算
绝对路径 : 盘符 :/ 目录 / 文件名
在 web 中路径分为相对路径和绝对路径两种
相对路径 :
. 表示当前文件所在的目录
.. 表示当前文件所在的上一级目录
文件名 表示当前文件所在目录的文件 , 相当于 ./ 文件名 ./ 可以省略
绝对路径 :
正确格式是 : http://ip:port/ 工程名 / 资源路径
错误格式是 : 盘符 :/ 目录 / 文件名
-->
<img src="1.jpg" width="200" height="260" border="1" alt=" 美女找不到"/>
<img src="../2.jpg" width="200" height="260" />
<img src="../imgs/3.jpg" width="200" height="260" />
<img src="../imgs/4.jpg" width="200" height="260" />
<img src="../imgs/5.jpg" width="200" height="260" />
<img src="../imgs/6.jpg" width="200" height="260" />
</body>

8.7、表格标签 、表格标签(重点,必须掌握 )

image-20220105223143187

需求 1个 :做一个 的 带表头的

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

<body>
<!--
需求 1:做一个 带表头的 ,三行,三列的表格,并显示边框
需求 2:修改表格的宽度,高度,表格的对齐方式,单元格间距。
table 标签是表格标签
border 设置表格标签
width 设置表格宽度
height 设置表格高度
align 设置表格相对于页面的对齐方式
cellspacing 设置单元格间距
tr 是行标签
th 是表头标签
td 是单元格标签
align 设置单元格文本对齐方式
b 是加粗标签
-->
<table align="center" border="1" width="300" height="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>
</body>

8.8、跨行跨列表格 、跨行跨列表格 (* 握 次重点,必须掌握 *)

image-20220105225013808

需求 1 :新建一个五行,五列的表格,第一行,第一列的单元格要跨两列,第二行第一列的单元格跨两行,第四行第四
列的单元格跨两行两列

<body>
<!-- 需求 1:
新建一个五行,五列的表格,
第一行,第一列的单元格要跨两列,
第二行第一列的单元格跨两行,
第四行第四列的单元格跨两行两列。
colspan 属性设置跨列
rowspan 属性设置跨行
-->
<table width="500" height="500" cellspacing="0" border="1">
<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 colspan="2" rowspan="2">4.4</td>
</tr>
<tr>
<td>5.1</td>
<td>5.2</td>
<td>5.3</td>
</tr>
</table>
</body>

8.9、 了解 了解 iframe 签 框架标签 ( 内嵌窗口)

ifarme 标签它可以在一个 html 页面上,打开一个小窗口,去加载一个单独的页面.

<body>
我是一个单独的完整的页面<br/><br/>
<!--ifarme 标签可以在页面上开辟一个小区域显示一个单独的页面
ifarme 和 a 标签组合使用的步骤:
1 在 iframe 标签中使用 name 属性定义一个名称
2 在 a 标签的 target 属性上设置 iframe 的 name 的属性值
-->
<iframe src="3. 标题标签.html" width="500" height="400" name="abc"></iframe>
<br/>
<ul>
<li><a href="0- 标签语法.html" target="abc">0-标签语法.html</a></li>
<li><a href="1.font  标签.html" target="abc">1.font 标签.html</a></li>
<li><a href="2. 特殊字符.html" target="abc">2.特殊字符.html</a></li>
    </ul>
</body>

8.10、表单标签 、表单标签 ( ***** 重点 重点 ,必须掌握 ,必须掌握 * )

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

image-20220105225233572

需求 1: 创建一个个人信息注册的表单界面 。 包含用户名 , 密码 , 确认密码 。 性别 ( 单选 ) , 兴趣爱好 ( 多选 ) , 国籍 ( 下拉列表)。
隐藏域,自我评价(多行文本域)。重置,提交。

表单的显示:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>表单的显示</title>
</head>
<body>
<!--需求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>
        <h1 align="center">用户注册</h1>
        <table align="center">
            <tr>
                <td> 用户名称:</td>
                <td>
                    <input type="text" value="默认值"/>
                </td>
            </tr>
            <tr>
                <td> 用户密码:</td>
                <td><input type="password" value="abc"/></td>
            </tr>
            <tr>
                <td>确认密码:</td>
                <td><input type="password" value="abc"/></td>
            </tr>
             <tr>
                <td>性别:</td>
                <td>
                    <input type="radio" name="sex"/>男
                    <input type="radio" name="sex" checked="checked"  />女
                </td>
            </tr>
             <tr>
                <td> 兴趣爱好:</td>
                <td>
                    <input type="checkbox" checked="checked" />Java
                    <input type="checkbox" />JavaScript
                    <input type="checkbox" />C++
                </td>
            </tr>
             <tr>
                <td>国籍:</td>
                <td>
                    <select>
                        <option>--请选择国籍--</option>
                        <option selected="selected">中国</option>
                        <option>美国</option>
                        <option>小日本</option>
                    </select>
                </td>
            </tr>
             <tr>
                <td>自我评价:</td>
                <td><textarea rows="10" cols="20">我才是默认值</textarea></td>
            </tr>
             <tr>
                <td><input type="reset" /></td>
                <td align="center"><input type="submit"/></td>
            </tr>
        </table>
    </form>
</body>
</html>

表单提交细节:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>表单的显示</title>
</head>
<body>
    <!--
        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">
        <input type="hidden" name="action" value="login" />
        <h1 align="center">用户注册</h1>
        <table align="center">
            <tr>
                <td> 用户名称:</td>
                <td>
                    <input type="text" name="username" value="默认值"/>
                </td>
            </tr>
            <tr>
                <td> 用户密码:</td>
                <td><input type="password" name="password" value="abc"/></td>
            </tr>
             <tr>
                <td>性别:</td>
                <td>
                    <input type="radio" name="sex" value="boy"/>男
                    <input type="radio" name="sex" checked="checked" value="girl" />女
                </td>
            </tr>
             <tr>
                <td> 兴趣爱好:</td>
                <td>
                    <input name="hobby" type="checkbox" checked="checked" value="java"/>Java
                    <input name="hobby" type="checkbox" value="js"/>JavaScript
                    <input name="hobby" type="checkbox" value="cpp"/>C++
                </td>
            </tr>
             <tr>
                <td>国籍:</td>
                <td>
                    <select name="country">
                        <option value="none">--请选择国籍--</option>
                        <option value="cn" selected="selected">中国</option>
                        <option value="usa">美国</option>
                        <option value="jp">小日本</option>
                    </select>
                </td>
            </tr>
             <tr>
                <td>自我评价:</td>
                <td><textarea name="desc" rows="10" cols="20">我才是默认值</textarea></td>
            </tr>
             <tr>
                <td><input type="reset" /></td>
                <td align="center"><input type="submit"/></td>
            </tr>
        </table>
    </form>
</body>
</html>

8.11 其他标签

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>其他标签</title>
</head>
<body>
        <!--需求1:div、span、p标签的演示
            div标签       默认独占一行
            span标签      它的长度是封装数据的长度
            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>
</body>
</html>

9 、CSS 技术

9.1 、CSS 技术介绍

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

9.2 、CSS 语法规则:

image-20220105230030443

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

例如:

p{
	color:red;
    font-size:30px;
}
/*注:一般每行只描述一个属性*/

CSS 注释:/注释内容/

9.3 、CSS 和 和 HTML 的结合方式

9.3.1 、第一种

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

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--需求 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>
</body>
</html>

问题:这种方式的缺点?
1.如果标签多了。样式多了。代码量非常庞大。
2.可读性非常差。
3.Css 代码没什么复用性可言。

9.3.2 、第二种:

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

<!--
xxx {
	Key : value value;
}
-->

<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title>CSS选择器</title>
	<style type="text/css">
		div{
			border: 1px solid yellow;
			color: blue;
			font-size: 30px;
		}
		span{
			border: 5px dashed  blue;
			color: yellow;
			font-size: 20px;
		}
	</style>
</head>
<body>
	<!-- 
	需求1:在所有div标签上修改字体颜色为蓝色,字体大小30个像素。边框为1像素黄色实线。
	并且修改所有span 标签的字体颜色为黄色,字体大小20个像素。边框为5像素蓝色虚线。
	 -->
	<div>div标签1</div>
	<div>div标签2</div>
	<span>span标签1</span>
	<span>span标签2</span>
</body>
</html>

问题:这种方式的缺点。
1.只能在同一页面内复用代码,不能在多个页面中复用 css 代码。
2.维护起来不方便,实际的项目中会有成千上万的页面,要到每个页面中去修改。工作量太大了

9.3.3 、第三种:

把 css 样式写成一个单独的 css 文件,再通过 link 标签引入即可复用。
使用 html 的 <link rel="stylesheet" type="text/css" href="./styles.css" /> 标签 导入 css 样式文件。

9.4 、CSS 选择器

9.4.1 标签名选择器

标签名选择器的格式是:
标签名{
​ 属性:值;
}
标签名选择器,可以决定哪些标签被动的使用这个样式。

<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title>CSS选择器</title>
	<style type="text/css">
		div{
			border: 1px solid yellow;
			color: blue;
			font-size: 30px;
		}
		span{
			border: 5px dashed  blue;
			color: yellow;
			font-size: 20px;
		}
	</style>
</head>
<body>
	<!-- 
	需求1:在所有div标签上修改字体颜色为蓝色,字体大小30个像素。边框为1像素黄色实线。
	并且修改所有span 标签的字体颜色为黄色,字体大小20个像素。边框为5像素蓝色虚线。
	 -->
	<div>div标签1</div>
	<div>div标签2</div>
	<span>span标签1</span>
	<span>span标签2</span>
</body>
</html>


9.4.2 、id 选择器

id 选择器的格式是:

#id 属性值{
​	属性:值;
}

id 选择器,可以让我们通过 id 属性选择性的去使用这个样式。

<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title>ID选择器</title>
	<style type="text/css">

		#id001{
			color: blue;
			font-size: 30px;
			border: 1px yellow solid;
		}

		#id002{
			color: red;
			font-size: 20px;
			border: 5px blue dotted ;
		}

	</style>
</head>
<body>		
	<!--
	需求1:分别定义两个 div 标签,
	第一个div 标签定义 id 为 id001 ,然后根据id 属性定义css样式修改字体颜色为蓝色,
	字体大小30个像素。边框为1像素黄色实线。
	
	第二个div 标签定义 id 为 id002 ,然后根据id 属性定义css样式 修改的字体颜色为红色,字体大小20个像素。
	边框为5像素蓝色点线。
	 -->
	
	<div id="id002">div标签1</div>
	<div id="id001">div标签2</div>
</body>
</html>
9.4.3 、class 选择器(类选择器)

class 类型选择器的格式是:
. class 属性值{
​ 属性:值;
}
class 类型选择器,可以通过 class 属性有效的选择性地去使用这个样式

<!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>
9.4.4 、组合选择器

组合选择器的格式是:
选择器 1,选择器 2,选择器 n{
​ 属性:值;
}
组合选择器可以让多个选择器共用同一个 css 样式代码。

<!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>

9.5、常用样式:

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

5、字体样式:

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

6、红色 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;
}

02-JavaScript

1 、JavaScript 介绍

avascript 语言诞生主要是完成页面的数据验证。因此它运行在客户端,需要运行浏览器来解析执行 JavaScript 代码。
JS 是 Netscape 网景公司的产品,最早取名为 LiveScript;为了吸引更多 java 程序员。更名为 JavaScript。
JS 是弱类型,Java 是强类型

特点:

  1. 交互性(它可以做的就是信息的动态交互)
  2. 安全性(不允许直接访问本地硬盘)
  3. 跨平台性(只要是可以解释 JS 的浏览器都可以执行,和平台无关)

2 、JavaScript 和 和 html 代码的结合方式 代码的结合方式

2.1 、第一种方式

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

示例代码:

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!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">
	// alert 是 JavaScript 语言提供的一个警告框函数。
	// 它可以接收任意类型的参数,这个参数就是警告框的提示信息
	alert("hello javaScript!");
</script>
</head>
<body>

</body>

</html>

2.2、第二种方式

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

<script type="text/javascript">
	// alert 是 JavaScript 语言提供的一个警告框函数。
	// 它可以接收任意类型的参数,这个参数就是警告框的提示信息
	alert("hello javaScript!");
</script>

<!--
	现在需要使用 script 引入外部的 js 文件来执行
	src 属性专门用来引入 js 文件路径(可以是相对路径,也可以是绝对路径)
	script 标签可以用来定义 js 代码,也可以用来引入 js 文件
	但是,两个功能二选一使用。不能同时使用两个功能
-->
<script type="text/javascript" src="1.js"></script>

3、变量

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

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

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

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

5、关系(比较)运算

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

示例代码:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Title</title>
	<script type="text/javascript">
		var a = "12";
		var b = 12;
		alert( a == b ); // true
		alert( a === b ); // false
	</script>
</head>
<body>
</body>
</html>

6 、逻辑运算

且运算: &&
或运算: ||
取反运算: !

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

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

/*
&& 且运算。
有两种情况:
第一种:当表达式全为真的时候。返回最后一个表达式的值。
第二种:当表达式中,有一个为假的时候。返回第一个为假的表达式的值
|| 或运算
第一种情况:当表达式全为假时,返回最后一个表达式的值
第二种情况:只要有一个表达式为真。就会把回第一个为真的表达式的值
并且 && 与运算 和 ||或运算 有短路。
短路就是说,当这个&&或||运算有结果了之后 。后面的表达式不再执行
*/
var a = "abc";
var b = true;
var d = false;
var c = null;

示例代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript">
   /*     在JavaScript语言中,所有的变量,都可以做为一个boolean类型的变量去使用。
        0 、null、 undefined、””(空串) 都认为是 false;*/

/*         && 且运算。
		有两种情况:
		第一种:当表达式全为真的时候。返回最后一个表达式的值。
		第二种:当表达式中,有一个为假的时候。返回第一个为假的表达式的值*/

        var a = "abc";
        var b = true;
        var d = false;
        var c = null;

        // alert( a && b );//true
        // alert( b && a );//abc
        // alert( a && d ); // false
        // alert( a && c ); // null

 /*      || 或运算
       第一种情况:当表达式全为假时,返回最后一个表达式的值
       第二种情况:只要有一个表达式为真。就会把回第一个为真的表达式的值*/
        // alert( d || c ); // null
        // alert( c|| d ); //false

        // alert( a || c ); //abc
        // alert( b || c ); //true

    </script>
</head>
<body>

</body>
</html>

7 、数组(*****重点) 重点)

JS 中 数组的定义:
​ 格式:
​ var 数组名 = []; // 空数组
​ var 数组名 = [1 , ’abc’ , true]; // 定义数组同时赋值元素

示例代码:

   <script type="text/javascript">

        var arr = [true,1]; // 定义一个空数组


        arr[0] = 12;
        // alert( arr[0] );//12

        // javaScript语言中的数组,只要我们通过数组下标赋值,那么最大的下标值,就会自动的给数组做扩容操作。
        arr[2] = "abc";
        alert(arr.length); //3

        // 数组的遍历
        for (var i = 0; i < arr.length; i++){
            alert(arr[i]);
        }

    </script>

8 、函数(***** 重点)

8.1 、函数的二种定义方式

第一种,可以使用 function

使用的格式如下:

function 函数名(形参列表){
	函数体
}
例:
function sum(num1,num2) {
	var result = num1 + num2;
	return result;
}

在 JavaScript 语言中,如何定义带有返回值的函数?
只需要在函数体内直接使用 return 语句返回值即可!

函数的第二种定义方式,格式如下:

使用格式如下:
var 函数名 = function(形参列表) { 函数体 }

示例代码:

 <script type="text/javascript">
        var fun = function () {
            alert("无参函数");
        }
        // fun();

        var fun2 = function (a,b) {
            alert("有参函数a=" + a + ",b=" + b);
        }
        // fun2(1,2);

        var fun3 = function (num1,num2) {
            return num1 + num2;
        }

        alert( fun3(100,200) );
    </script>

注:在 Java 中函数允许重载。但是在 JS 中函数的重载会直接覆盖掉上一次的定义

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

就是在 function 函数中不需要定义,但却可以直接用来获取所有参数的变量。我们管它叫隐形参数
隐形参数特别像 java 基础的可变长参数一样。
public void fun( Object ... args );
可变长参数其他是一个数组。
那么 js 中的隐形参数也跟 java 的可变长参数一样。操作类似数组。

示例代码:

    <script type="text/javascript">
        function fun(a) {
            alert(arguments.length);//可看参数个数

            alert(arguments[0]);
            alert(arguments[1]);
            alert(arguments[2]);

            alert("a = " + a);

            for (var i = 0; i < arguments.length; i++) {
                alert(arguments[i]);
            }

            alert("无参函数fun()");
        }

        // fun(1,"ad",true);

        // 需求:要求 编写 一个函数。用于计算所有参数相加的和并返回
        function sum(num1, num2) {
            var result = 0;
            for (var i = 0; i < arguments.length; i++) {
                if (typeof (arguments[i]) == "number") {
                    result += arguments[i];
                }
            }
            return result;
        }

        alert(sum(1, 2, 3, 4, "abc", 5, 6, 7, 8, 9));


    </script>

9 、JS 中的自定义对象 中的自定义对象(扩展内容)

Object 形式的自定义对象

对象的定义:
​ var 变量名 = new Object(); // 对象实例(空对象)
​ 变量名.属性名 = 值; // 定义一个属性
​ 变量名.函数名 = function(){} // 定义一个函数

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

示例代码:

    <script type="text/javascript">

        // 对象的定义:
        //     var 变量名 = new Object();   // 对象实例(空对象)
        //     变量名.属性名 = 值;		  // 定义一个属性
        //     变量名.函数名 = function(){}  // 定义一个函数
        var obj = new Object();
        obj.name = "华仔";
        obj.age = 18;
        obj.fun = function () {
            alert("姓名:" + this.name + " , 年龄:" + this.age);
        }
        // 对象的访问:
        //     变量名.属性 / 函数名();
        // alert( obj.age );
        obj.fun();

    </script>

{}花括号形式的自定义对象

对象的定义:

var 变量名 = { // 空对象
		属性名:值, // 定义一个属性
		属性名:值, // 定义一个属性
		函数名:function(){} // 定义一个函数
	};

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

示例代码:

    <script type="text/javascript">
        // 对象的定义:
        // var 变量名 = {			// 空对象
        //     属性名:值,			// 定义一个属性
        //     属性名:值,			// 定义一个属性
        //     函数名:function(){}	// 定义一个函数
        // };
        var obj = {
            name: "国哥",
            age: 18,
            fun: function () {
                alert("姓名:" + this.name + " , 年龄:" + this.age);
            }
        };

        // 对象的访问:
        //     变量名.属性 / 函数名();
        alert(obj.name);
        obj.fun();
    </script>

10 、js 中的事件

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

1641738791577

事件的注册又分为静态注册动态注册两种:
什么是事件的注册(绑定)?
其实就是告诉浏览器,当事件响应后要执行哪些操作代码,叫事件注册或事件绑定

静态注册事件 :通过 html 标签的事件属性直接赋于事件响应后的代码,这种方式我们叫静态注册。

动态注册事件 :是指先通过 js 代码得到标签的 dom 对象,然后再通过 dom 对象.事件名 = function(){} 这种形式赋于事件响应后的代码,叫动态注册。

onload 加载完成事件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript">
        // onload事件的方法
        function onloadFun() {
            alert('静态注册onload事件,所有代码');
        }

        // onload事件动态注册。是固定写法
        window.onload = function () {
            alert("动态注册的onload事件");
        }

    </script>
</head>
<!--静态注册onload事件
        onload事件是浏览器解析完页面之后就会自动触发的事件
       <body onload="onloadFun();">
-->
<body>

</body>
</html>

onclick单击事件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript">
        function onclickFun() {
            alert("静态注册onclick事件");
        }

        // 动态注册onclick事件
        window.onload = function () {
            // 1 获取标签对象
            /*
            * document 是JavaScript语言提供的一个对象(文档)<br/>
            * get           获取
            * Element       元素(就是标签)
            * By            通过。。   由。。经。。。
            * Id            id属性
            *
            * getElementById通过id属性获取标签对象
            **/
            var btnObj = document.getElementById("btn01");
            // alert( btnObj );
            // 2 通过标签对象.事件名 = function(){}
            btnObj.onclick = function () {
                alert("动态注册的onclick事件");
            }
        }

    </script>
</head>
<body>
<!--静态注册onClick事件-->
<button onclick="onclickFun();">按钮1</button>
<button id="btn01">按钮2</button>
</body>
</html>

onblur 失去焦点事件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript">
        // 静态注册失去焦点事件
        function onblurFun() {
            // console是控制台对象,是由JavaScript语言提供,专门用来向浏览器的控制器打印输出, 用于测试使用
            // log() 是打印的方法
            console.log("静态注册失去焦点事件");
        }

        // 动态注册 onblur事件
        window.onload = function () {
            //1 获取标签对象
           var passwordObj = document.getElementById("password");
           // alert(passwordObj);
            //2 通过标签对象.事件名 = function(){};
            passwordObj.onblur = function () {
                console.log("动态注册失去焦点事件");
            }
        }

    </script>
</head>
<body>
    用户名:<input type="text" onblur="onblurFun();"><br/>
    密码:<input id="password" type="text" ><br/>
</body>
</html>

onchange 内容发生改变事件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

    <script type="text/javascript">
        function onchangeFun() {
            alert("女神已经改变了");
        }

        window.onload = function () {
            //1 获取标签对象
            var selObj = document.getElementById("sel01");
            // alert( selObj );
            //2 通过标签对象.事件名 = function(){}
            selObj.onchange = function () {
                alert("男神已经改变了");
            }
        }


    </script>
</head>
<body>
    请选择你心中的女神:
    <!--静态注册onchange事件-->
    <select onchange="onchangeFun();">
        <option>--女神--</option>
        <option>芳芳</option>
        <option>佳佳</option>
        <option>娘娘</option>
    </select>

    请选择你心中的男神:
    <select id="sel01">
        <option>--男神--</option>
        <option>国哥</option>
        <option>华仔</option>
        <option>富城</option>
    </select>
</body>
</html>

onsubmit 表单提交事件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" >
        // 静态注册表单提交事务
        function onsubmitFun(){
            // 要验证所有表单项是否合法,如果,有一个不合法就阻止表单提交
            alert("静态注册表单提交事件----发现不合法");

            return flase;
        }

        window.onload = function () {
            //1 获取标签对象
            var formObj = document.getElementById("form01");
            //2 通过标签对象.事件名 = function(){}
            formObj.onsubmit = function () {
                // 要验证所有表单项是否合法,如果,有一个不合法就阻止表单提交
                alert("动态注册表单提交事件----发现不合法");

                return false;
            }
        }

    </script>
</head>
<body>
    <!--return false 可以阻止 表单提交 -->
    <form action="http://localhost:8080" method="get" onsubmit="return onsubmitFun();">
        <input type="submit" value="静态注册"/>
    </form>
    <form action="http://localhost:8080" id="form01">
        <input type="submit" value="动态注册"/>
    </form>

</body>
</html>

10 、DOM 模型 模型

DOM 全称是 Document Object Model 文档对象模型
大白话,就是把文档中的标签,属性,文本,转换成为对象来管理

10.1 、Document 对象

1641739507514

1641739542676

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

<body>
	<div id="div01">div01</div>
</body>

模拟对象化,相当于:

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

10.2、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

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

10.3、节点的常用属性和方法

节点就是标签对象

方法:

通过具体的元素节点调用
xxx元素节点.getElementsByTagName()方法,获取当前节点的指定标签名孩子节点

xxx元素节点.appendChild( ChildNode )方法,可以添加一个子节点,ChildNode 是要添加的孩子节点

1641740068867

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>dom查询</title>
<link rel="stylesheet" type="text/css" href="style/css.css" />
<script type="text/javascript">
	window.onload = function(){
		//1.查找#bj节点
		document.getElementById("btn01").onclick = function () {
			var bjObj = document.getElementById("bj");
			alert(bjObj.innerHTML);
		}
		//2.查找所有li节点
		var btn02Ele = document.getElementById("btn02");
		btn02Ele.onclick = function(){
			var lis = document.getElementsByTagName("li");
			alert(lis.length)
		};
		//3.查找name=gender的所有节点
		var btn03Ele = document.getElementById("btn03");
		btn03Ele.onclick = function(){
			var genders = document.getElementsByName("gender");
			alert(genders.length)
		};
		//4.查找#city下所有li节点
		var btn04Ele = document.getElementById("btn04");
		btn04Ele.onclick = function(){
			//1 获取id为city的节点
			//2 通过city节点.getElementsByTagName按标签名查子节点
			var lis = document.getElementById("city").getElementsByTagName("li");
			alert(lis.length)
		};
		//5.返回#city的所有子节点
		var btn05Ele = document.getElementById("btn05");
		btn05Ele.onclick = function(){
			//1 获取id为city的节点
			//2 通过city获取所有子节点
			alert(document.getElementById("city").childNodes.length);
		};
		//6.返回#phone的第一个子节点
		var btn06Ele = document.getElementById("btn06");
		btn06Ele.onclick = function(){
			// 查询id为phone的节点
			alert( document.getElementById("phone").firstChild.innerHTML );
		};
		//7.返回#bj的父节点
		var btn07Ele = document.getElementById("btn07");
		btn07Ele.onclick = function(){
			//1 查询id为bj的节点
			var bjObj = document.getElementById("bj");
			//2 bj节点获取父节点
			alert( bjObj.parentNode.innerHTML );
		};
		//8.返回#android的前一个兄弟节点
		var btn08Ele = document.getElementById("btn08");
		btn08Ele.onclick = function(){
			// 获取id为android的节点
			// 通过android节点获取前面兄弟节点
			alert( document.getElementById("android").previousSibling.innerHTML );
		};
		//9.读取#username的value属性值
		var btn09Ele = document.getElementById("btn09");
		btn09Ele.onclick = function(){
			alert(document.getElementById("username").value);
		};
		//10.设置#username的value属性值
		var btn10Ele = document.getElementById("btn10");
		btn10Ele.onclick = function(){
			document.getElementById("username").value = "国哥你真牛逼";
		};
		//11.返回#bj的文本值
		var btn11Ele = document.getElementById("btn11");
		btn11Ele.onclick = function(){
			alert(document.getElementById("city").innerHTML);
			// alert(document.getElementById("city").innerText);
		};
	};
</script>
</head>
<body>
<div id="total">
	<div class="inner">
		<p>
			你喜欢哪个城市?
		</p>

		<ul id="city">
			<li id="bj">北京</li>
			<li>上海</li>
			<li>东京</li>
			<li>首尔</li>
		</ul>

		<br>
		<br>

		<p>
			你喜欢哪款单机游戏?
		</p>

		<ul id="game">
			<li id="rl">红警</li>
			<li>实况</li>
			<li>极品飞车</li>
			<li>魔兽</li>
		</ul>

		<br />
		<br />

		<p>
			你手机的操作系统是?
		</p>

		<ul id="phone"><li>IOS</li><li id="android">Android</li><li>Windows Phone</li></ul>
	</div>

	<div class="inner">
		gender:
		<input type="radio" name="gender" value="male"/>
		Male
		<input type="radio" name="gender" value="female"/>
		Female
		<br>
		<br>
		name:
		<input type="text" name="name" id="username" value="abcde"/>
	</div>
</div>
<div id="btnList">
	<div><button id="btn01">查找#bj节点</button></div>
	<div><button id="btn02">查找所有li节点</button></div>
	<div><button id="btn03">查找name=gender的所有节点</button></div>
	<div><button id="btn04">查找#city下所有li节点</button></div>
	<div><button id="btn05">返回#city的所有子节点</button></div>
	<div><button id="btn06">返回#phone的第一个子节点</button></div>
	<div><button id="btn07">返回#bj的父节点</button></div>
	<div><button id="btn08">返回#android的前一个兄弟节点</button></div>
	<div><button id="btn09">返回#username的value属性值</button></div>
	<div><button id="btn10">设置#username的value属性值</button></div>
	<div><button id="btn11">返回#bj的文本值</button></div>
</div>
</body>
</html>

03-jQuery(上)

1 、jQuery 介绍

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

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

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

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

2 、jQuery 的初体验!!!

需求:使用 jQuery 给一个按钮绑定单击事件?

<script type="text/javascript" src="../script/jquery-1.7.2.js"></script>
	<script type="text/javascript">
		// window.onload = function () {
		// 	var btnObj = document.getElementById("btnId");
		// 	// alert(btnObj);//[object HTMLButtonElement]   ====>>>  dom对象
		// 	btnObj.onclick = function () {
		// 		alert("js 原生的单击事件");
		// 	}
		// }

		$(function () { // 表示页面加载完成 之后,相当 window.onload = function () {}
			var $btnObj = $("#btnId"); // 表示按id查询标签对象
			$btnObj.click(function () { // 绑定单击事件
				alert("jQuery 的单击事件");
			});
		});

</script>

常见问题?

1、使用 jQuery 一定要引入 jQuery 库吗? 	答案: 是,必须
2、 jQuery 中的$到底是什么? 	答案: 它是一个函数
3、怎么为按钮添加点击响应函数的? 	答案:
	1、使用 jQuery 查询到标签对象
	2、使用标签对象.click( function(){} );

3 、jQuery 核心函数

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

1、传入参数为 [ 函数 ] 时:
表示页面加载完成之后。相当于 window.onload = function(){}

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

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

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

4 、jQuery 对象和 dom 对象区分

4.1 、什么是 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]

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

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

4.3 、jQuery 对象和 Dom 对象使用区别 对象使用区别

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

4.4 、Dom 对象和 jQuery 对象互转

1 、dom 对象转化为 jQuery 对象( 重点)*
​ 1、先有 DOM 对象
​ 2、$( DOM 对象 ) 就可以转换成为 jQuery 对象
2 、jQuery 对象转为 dom 对象( 重点)*
​ 1、先有 jQuery 对象
​ 2、jQuery 对象[下标]取出相应的 DOM 对象

1641742390782

5 、jQuery 选择器(***** 重点)

5.1 、基本选择器(****重点)

1641742434973

#ID 选择器:		根据 id 查找标签对象
.class 选择器:		根据 class 查找标签对象
element 选择器:	根据标签名查找标签对象
* 选择器:			表示任意的,所有的元素
selector1,selector2 组合选择器:合并选择器 1,选择器 2 的结果并返回
p.myClass		表示标签名必须是 p 标签,而且 class 类型还要是 myClass

5.2 、层级选择器(重点)

1641743601732

ancestor descendant 后代选择器 :在给定的祖先元素下匹配所有的后代元素
parent > child 子元素选择器:在给定的父元素下匹配所有的子元素
prev + next 相邻元素选择器:匹配所有紧接在 prev 元素后的 next 元素
prev ~ sibings 之后的兄弟元素选择器:匹配 prev 元素之后的所有 siblings 元素

5.3、过滤选择器

1641743729609

:first 获取第一个元素
:last 获取最后个元素
:not(selector) 去除所有与给定选择器匹配的元素
:even 匹配所有索引值为偶数的元素,从 0 开始计数
:odd 匹配所有索引值为奇数的元素,从 0 开始计数
:eq(index) 匹配一个给定索引值的元素
:gt(index) 匹配所有大于给定索引值的元素
:lt(index) 匹配所有小于给定索引值的元素
:header 匹配如 h1, h2, h3 之类的标题元素
:animated 匹配所有正在执行动画效果的元素

内容过滤器:

1641743832064

:contains(text) 匹配包含给定文本的元素
:empty 匹配所有不包含子元素或者文本的空元素
:parent 匹配含有子元素或者文本的元素
:has(selector) 匹配含有选择器所匹配的元素的元素

属性过滤器:

1641743900964

[attribute] 匹配包含给定属性的元素。
[attribute=value] 匹配给定的属性是某个特定值的元素
[attribute!=value] 匹配所有不含有指定的属性,或者属性不等于特定值的元素。
[attribute^=value] 匹配给定的属性是以某些值开始的元素
[attribute$=value] 匹配给定的属性是以某些值结尾的元素
[attribute*=value] 匹配给定的属性是以包含某些值的元素
[attrSel1][attrSel2][attrSelN] 复合属性选择器,需要同时满足多个条件时使用。

表单过滤器:

1641743996572

:input 匹配所有 input, textarea, select 和 button 元素
:text 匹配所有 文本输入框
:password 匹配所有的密码输入框
:radio 匹配所有的单选框
:checkbox 匹配所有的复选框
:submit 匹配所有提交按钮
:image 匹配所有 img 标签
:reset 匹配所有重置按钮
:button 匹配所有 input type=button <button>按钮
:file 匹配所有 input type=file 文件上传
:hidden 匹配所有不可见元素 display:none 或 input type=hidden

表单对象属性过滤器:

1641744077623

:enabled 匹配所有可用元素
:disabled 匹配所有不可用元素
:checked 匹配所有选中的单选,复选,和下拉列表中选中的 option  标签对象
:selected 匹配所有选中的 option

6 、jQuery

1641744137710

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 对象中

04-jQuery(下)

1 、jQuery 的属性操作

1641744995186

html() 它可以设置和获取起始标签和结束标签中的内容。 跟 dom 属性 innerHTML 一样。
text() 它可以设置和获取起始标签和结束标签中的文本。 跟 dom 属性 innerText 一样。
val() 它可以设置和获取表单项的 value 属性值。 跟 dom 属性 value 一样

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

<!DOCTYPE html>
<html lang="zh_CN">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="script/jquery-1.7.2.js"></script>
    <script type="text/javascript">

        $(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"]);
        });

    </script>
</head>
<body>
<body>
单选:
<input name="radio" type="radio" value="radio1"/>radio1
<input name="radio" type="radio" value="radio2"/>radio2
<br/>
多选:
<input name="checkbox" type="checkbox" value="checkbox1"/>checkbox1
<input name="checkbox" type="checkbox" value="checkbox2"/>checkbox2
<input name="checkbox" type="checkbox" value="checkbox3"/>checkbox3
<br/>

下拉多选 :
<select id="multiple" multiple="multiple" size="4">
    <option value="mul1">mul1</option>
    <option value="mul2">mul2</option>
    <option value="mul3">mul3</option>
    <option value="mul4">mul4</option>
</select>
<br/>

下拉单选 :
<select id="single">
    <option value="sin1">sin1</option>
    <option value="sin2">sin2</option>
    <option value="sin3">sin3</option>
</select>
</body>
</body>
</html>

1641745303912

attr() 可以设置和获取属性的值,不推荐操作 checked、readOnly、selected、disabled 等等
attr 方法还可以操作非标准的属性。比如自定义属性:abc,bbj
prop() 可以设置和获取属性的值,只推荐操作 checked、readOnly、selected、disabled 等等

2 、jQuery 练习

全选,全不选,反选

<!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(){
		
		var $items = $(":checkbox[name=items]");
		var items = $("[name='items']");
		//全选按钮
		$("#checkedAllBtn").click(function(){
			items.attr("checked",true);
			$("#checkedAllBox").attr("checked",true);
		});
		//全不选按钮
		$("#checkedNoBtn").click(function(){
			items.attr("checked",false);
			$("#checkedAllBox").attr("checked",false);
		});
		
		//反选按钮
		$("#checkedRevBtn").click(function(){
			items.each(function(){
				this.checked = !this.checked;
			});
			var flag = $("[name='items']:checked").length==4;
			$("#checkedAllBox").attr("checked",flag);
		});
		
		//提交按钮
		$("#sendBtn").click(function(){
			$(":checkbox[name='items']:checked").each(function(){
				alert(this.value);
			});
		});
		
		//全选/全不选复选框
		$("#checkedAllBox").click(function(){
			items.attr("checked",this.checked);
		});
		
		//全选/全不选复选框与items状态同步
		$("[name='items']").click(function(){
			var flag = $("[name='items']:checked").length==4;
			$("#checkedAllBox").attr("checked",flag);
		});
	});
	
</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>

3、DOM 的增删改

1641745530435

内部插入:

appendTo() 		a.appendTo(b) 把 a 插入到 b 子元素末尾,成为最后一个子元素
prepend() 		a.prepend(b) 把b插入到a的末尾

外部插入:

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

替换:

replaceWith() 		a.replaceWith(b) 用 b 替换掉 a
replaceAll() 		a.replaceAll(b) 用 a 替换掉所有 b

删除:

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

4、CSS 样式操作

addClass() 添加样式
removeClass() 删除样式
toggleClass() 有就删除,没有就添加样式。
offset() 获取和设置元素的坐标。

5 、jQuery 动画

基本动画
show() 将隐藏的元素显示
hide() 将可见的元素隐藏。
toggle() 可见就隐藏,不可见就显示。
以上动画方法都可以添加参数。
	1、第一个参数是动画 执行的时长,以毫秒为单位
	2、第二个参数是动画的回调函数 (动画完成后自动调用的函数)
	
淡入淡出动画
fadeIn() 淡入(慢慢可见)
fadeOut() 淡出(慢慢消失)
fadeTo() 在指定时长内慢慢的将透明度修改到指定的值。0 透明,1 完成可见,0.5 半透明
fadeToggle() 淡入/淡出 切换

练习

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
		<title>Untitled Document</title>
		<link href="css/style.css" type="text/css" rel="stylesheet" />
		<script type="text/javascript" src="script/jquery-1.7.2.js"></script>
	
<script type="text/javascript">
	/* 	
		基本
		show([speed,[easing],[fn]]) 
		hide([speed,[easing],[fn]]) 
		toggle([speed],[easing],[fn]) 
		滑动
		slideDown([spe],[eas],[fn]) 
		slideUp([speed,[easing],[fn]]) 
		slideToggle([speed],[easing],[fn]) 
		淡入淡出
		fadeIn([speed],[eas],[fn]) 
		fadeOut([speed],[eas],[fn]) 
		fadeTo([[spe],opa,[eas],[fn]]) 
		fadeToggle([speed,[eas],[fn]])
		*/
		$(function(){
			//显示   show()
			$("#btn1").click(function(){
				$("#div1").show(2000,function () {
					alert("show动画完成 ")
				});
			});		
			//隐藏  hide()
			$("#btn2").click(function(){
				$("#div1").hide(1000,function () {
					alert("hide动画 执行完成 ")
				});
			});	
			//切换   toggle()
			$("#btn3").click(function(){
				$("#div1").toggle(1000,function () {
					alert("toggle动画 完成 ")
				});
			});

			// var abc = function(){
			// 	$("#div1").toggle(1000,abc);
			// }
			// abc();

			//淡入   fadeIn()
			$("#btn4").click(function(){
				$("#div1").fadeIn(2000,function () {
					alert("fadeIn完成 ")
				});
			});	
			//淡出  fadeOut()
			$("#btn5").click(function(){
				$("#div1").fadeOut(2000,function () {
					alert("fadeOut完成 ")
				});
			});	
			
			//淡化到  fadeTo()
			$("#btn6").click(function(){
				$("#div1").fadeTo(2000,0.5,function () {
					alert('fadeTo完成 ')
				});
			});	
			//淡化切换  fadeToggle()
			$("#btn7").click(function(){
				$("#div1").fadeToggle(1000,function () {
					alert("fadeToggle完成 ")
				});
			});	
		})
</script>
	
	</head>
	<body>
		<table style="float: left;">
			<tr>
				<td><button id="btn1">显示show()</button></td>
			</tr>
			<tr>
				<td><button id="btn2">隐藏hide()</button></td>
			</tr>
			<tr>
				<td><button id="btn3">显示/隐藏切换 toggle()</button></td>
			</tr>
			<tr>
				<td><button id="btn4">淡入fadeIn()</button></td>
			</tr>
			<tr>
				<td><button id="btn5">淡出fadeOut()</button></td>
			</tr>
			<tr>
				<td><button id="btn6">淡化到fadeTo()</button></td>
			</tr>
			<tr>
				<td><button id="btn7">淡化切换fadeToggle()</button></td>
			</tr>
		</table>
		
		<div id="div1" style="float:left;border: 1px solid;background-color: blue;width: 300px;height: 200px;">
			jquery动画定义了很多种动画效果,可以很方便的使用这些动画效果
		</div>
	</body>

</html>

6、jQuery 事件操作

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

他们分别是在什么时候触发?
1、jQuery 的页面加载完成之后是浏览器的内核解析完页面的标签创建好 DOM 对象之后就会马上执行。
2、原生 js 的页面加载完成之后,除了要等浏览器内核解析完标签创建好 DOM 对象,还要等标签显示时需要的内容加载
完成

//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 同时对多个事件绑定同一个函数。怎么获取当前操作是什么事件

$("#areaDiv").bind("mouseover mouseout",function (event) {
	if (event.type == "mouseover") {
		console.log(" 鼠标移入");
	} else if (event.type == "mouseout") {
		console.log(" 鼠标移出");
	}
});

05-xml

1.XML 简介

xml 是可扩展的标记性语言。

xml 的作用?

xml 的主要作用有:
1、用来保存数据,而且这些数据具有自我描述性
2、它还可以做为项目或者模块的配置文件
3、还可以做为网络传输数据的格式(现在 JSON 为主)。

2、xml 语法

  1. 文档声明。
  2. 元素(标签)
  3. xml 属性
  4. xml 注释
  5. 文本区域(CDATA 区)

2.1、文档声明

我们先创建一个简单 XML 文件,用来描述图书信息。

1 )创建一个 xml 文件

1641773792216

文件名:

1641773807120

<?xml version="1.0" encoding="UTF-8"?> xml 声明。
<!-- xml 声明 version 是版本的意思 encoding 是编码 -->
而且这个<?xml 要连在一起写,否则会有报错

1641773905496

2 )图书有 id 性 属性 一 表示唯一 标识,书名,有作者,价格的信息

<?xml version="1.0" encoding="utf-8" ?>
<!-- xml声明 version是版本的意思   encoding是编码  -->
<books> <!-- 这是xml注释 -->
    <book id="SN123123413241"> <!-- book标签描述一本图书   id属性描述 的是图书 的编号  -->
        <name>java编程思想</name> <!-- name标签描述 的是图书 的信息 -->
        <author>华仔</author>		<!-- author单词是作者的意思 ,描述图书作者 -->
        <price>9.9</price>		<!-- price单词是价格,描述的是图书 的价格 -->
    </book>
    <book id="SN12341235123">	<!-- book标签描述一本图书   id属性描述 的是图书 的编号  -->
        <name>葵花宝典</name>	<!-- name标签描述 的是图书 的信息 -->
        <author>班长</author>	<!-- author单词是作者的意思 ,描述图书作者 -->
        <price>5.5</price>	<!-- price单词是价格,描述的是图书 的价格 -->
    </book>
</books>

在浏览器中可以查看到文档

1641774058352

2.2 、xml 注释

html 和 XML 注释 一样 : <!-- html 注释 -->

2.3、元素(标签)

咱们先回忆一下:

咱们先回忆一下:
	html 标签:
	格式:<标签名>封装的数据</标签名>
	单标签: <标签名 /> 
	<br /> 换行 
	<hr />水平线
	双标签 <标签名>封装的数据</标签名>
	标签名大小写不敏感
	标签有属性,有基本属性和事件属性
	标签要闭合(不闭合 ,html 中不报错。但我们要养成良好的书写习惯。闭合)
1 )什么是 xml

1641774760998

元素是指从开始标签到结束标签的内容。

例如:<title>java 编程思想</title>

元素 我们可以简单的理解为是 标签。Element 译 翻译 元素

2 )XML 命名规则

XML 元素必须遵循以下命名规则:
2.1)名称可以含字母、数字以及其他的字符

例如:

<book id="SN213412341"> <!-- 描述一本书 -->
	<author>班导</author> <!-- 描述书的作者信息 -->
	<name>java 编程思想</name> <!-- 书名 -->
	<price>9.9</price> <!-- 价格 -->
</book>

2.2)名称不能以数字或者标点符号开始

1641775018617

2.3)名称不推荐以字符 “xml”(或者 XML、Xml)开始 (它是可以的)

1641775093384

2.4)名称不能包含空格

1641777953234

3 )xml 也 中的元素(标签)也 成 分成 单标签和双标签:

单标签
​ 格式: <标签名 属性=”值” 属性=”值” ...... />
双标签
​ 格式:< 标签名 属性=”值” 属性=”值” ......>文本数据或子标签</标签名>

1641778077273

2.4 、xml 属性

xml 的标签属性和 html 的标签属性是非常类似的,属性可以提供元素的额外信息

在标签上可以书写属性:
​ 一个标签上可以书写多个属性。用 每个属性的值必须使用 号 引号 引起来。的规则和标签的书写规则一致。

1641778337912

1) 属性必须使用引号引起来,不引会报错示例代码

1641778392227

2.5、语法规则:

2.5.1) 所有 XML 元素都须有关闭标签 (也就是闭合)

1641778436875

2.5.2 )XML 标签对大小写敏感

1641778542257

2.5.3 )XML 必须正确地嵌套

1641778655734

2.5.4 )XML 文档必须有根元素

根元素就是顶级元素,没有父标签的元素,叫顶级元素。
根元素是没有父标签的顶级元素,而且是唯一一个才行。

1641778830791

2.5.5 )XML 的属性值须加引号

1641778849914

2.5.6 )XML 中的特殊字符

1641778860119

2.5.7 )文本区域(CDATA 区)

CDATA 语法可以告诉 xml 解析器,我 CDATA 里的文本内容,只是纯文本,不需要 xml 语法解析

CDATA 格式:

<![CDATA[ 这里可以把你输入的字符原样显示,不会解析 xml ]]>

1641778981440

3 、xml 解析技术介绍

xml 可扩展的标记语言。不管是 html 文件还是 xml 文件它们都是标记型文档都可以使用 w3c 组织制定的 dom 技术来解析

1641779103623

document 对象表示的是整个文档(可以是 html 文档,也可以是 xml 文档)

早期 JDK 为我们提供了两种 xml 解析技术: DOMSax ( 已经过时,但我们需要知道这两种技术

dom 解析技术是 W3C 组织制定的,而所有的编程语言都对这个解析技术使用了自己语言的特点进行实现(底层封装)。Java 对 dom 技术解析标记也做了实现

SAX:

sun 公司在 JDK5 版本对 dom 解析技术进行升级:SAX( Simple API for XML )
SAX 解析,它跟 W3C 制定的解析不太一样。它是以类似事件机制通过回调告诉用户当前正在解析的内容。它是一行一行的读取 xml 文件进行解析的,不会创建大量的 dom 对象。
所以它在解析 xml 的时候,在内存的使用上和性能上都优于 Dom 解析。

1641779522694

1641779532370

4 、dom4j 解析技术 (重点*****)

由于 dom4j 它不是 sun 公司的技术,而属于第三方公司的技术,我们需要使用 dom4j 就需要到 dom4j 官网下载 dom4j的 jar 包(自行下载)。

4.1 、Dom4j 类库的使用

1641779603375

解压后:

1641779616122

4.2 、dom4j 目录的介绍:

1 )docs 是 文 档 目录

1641779701868

2) 查 如何查 Dom4j 的文档

1641779715630

3 )Dom4j 快速入门

1641779728485

4)lib 目录

1641779741651

5)src 目录是第三方类库的源码目录:

1641779761441

4.3 、dom4j 编程步骤:

第一步: 先加载 xml 文件创建 Document 对象
第二步:通过 Document 对象拿到根元素对象
第三步:通过根元素.elelemts(标签名); 可以返回一个集合,这个集合里放着所有你指定的标签名的元素对象
第四步:找到你想要修改、删除的子元素,进行相应在的操作
第五步,保存到硬盘上

4.4 、获取 document 对象

创建一个 lib 目录,并添加 dom4j 的 jar 包。并添加到类路径。

1641779895621

需要解析的 books.xml 文件内容

<?xml version="1.0" encoding="utf-8" ?>
<!-- xml声明 version是版本的意思   encoding是编码  -->
<books> <!-- 这是xml注释 -->
    <book id="SN123123413241"> <!-- book标签描述一本图书   id属性描述 的是图书 的编号  -->
        <name>java编程思想</name> <!-- name标签描述 的是图书 的信息 -->
        <author>华仔</author>		<!-- author单词是作者的意思 ,描述图书作者 -->
        <price>9.9</price>		<!-- price单词是价格,描述的是图书 的价格 -->
    </book>
    <book id="SN12341235123">	<!-- book标签描述一本图书   id属性描述 的是图书 的编号  -->
        <name>葵花宝典</name>	<!-- name标签描述 的是图书 的信息 -->
        <author>班长</author>	<!-- author单词是作者的意思 ,描述图书作者 -->
        <price>5.5</price>	<!-- price单词是价格,描述的是图书 的价格 -->
    </book>
</books>

解析获取 Document 对象的代码
第一步,先创建 SaxReader 对象。这个对象,用于读取 xml 文件,并创建 Document

    @Test
    public void getDocument() throws DocumentException {
// 要创建一个 Document 对象,需要我们先创建一个 SAXReader 对象
        SAXReader reader = new SAXReader();
// 这个对象用于读取 xml 文件,然后返回一个 Document。
        Document document = reader.read("src/books.xml");
// 打印到控制台,看看是否创建成功
        System.out.println(document);
    }

3.5、遍历 标签 ---->获取所有标签中的内容

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

    /*
     * 读取 xml 文件中的内容
     */
    @Test
    public void readXML() throws DocumentException {
        // 需要分四步操作:
        // 第一步,通过创建 SAXReader 对象。来读取 xml 文件,获取 Document 对象
        // 第二步,通过 Document 对象。拿到 XML 的根元素对象
        // 第三步,通过根元素对象。获取所有的 book 标签对象
        // 第四步,遍历每个 book 标签对象。然后获取到 book 标签对象内的每一个元素,再通过 getText() 方法拿到起始标签和结束标签之间的文本内容

        // 第一步,通过创建 SAXReader 对象。来读取 xml 文件,获取 Document 对象
        SAXReader reader = new SAXReader();
        Document document = reader.read("src/books.xml");
        // 第二步,通过 Document 对象。拿到 XML 的根元素对象
        Element root = document.getRootElement();
        // 打印测试
        // Element.asXML() 它将当前元素转换成为 String 对象
        // System.out.println( root.asXML() );
        // 第三步,通过根元素对象。获取所有的 book 标签对象
        // Element.elements(标签名)它可以拿到当前元素下的指定的子元素的集合
        List<Element> books = root.elements("book");
        // 第四步,遍历每个 book 标签对象。然后获取到 book 标签对象内的每一个元素,
        for (Element book : books) {
            // 测试
            // System.out.println(book.asXML());
            // 拿到 book 下面的 name 元素对象
            Element nameElement = book.element("name");
            // 拿到 book 下面的 price 元素对象
            Element priceElement = book.element("price");
            // 拿到 book 下面的 author 元素对象
            Element authorElement = book.element("author");
            // 再通过 getText() 方法拿到起始标签和结束标签之间的文本内容
            System.out.println("书名" + nameElement.getText() + " , 价格:"
                    + priceElement.getText() + ", 作者:" + authorElement.getText());
        }
    }

打印内容:

1641780729153

05- Tomcat (下)

1.JavaWeb 的概念

a) 什么是 JavaWeb

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

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

b)什么是请求

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

c)什么是响应

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

d)请求和响应的关系

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

1641781345249

2.Web 资源的分类

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

静态资源: html、css、js、txt、mp4 视频 , jpg 图片
动态资源: jsp 页面、Servlet 程序

3.常用的 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 规范,而且不断的完善以适应新的开发要求,适合大型项目(收费,用的不多,适合大公司)。

4.Tomcat 服务器和 Servlet 版本的对应关系

1641781676997

Servlet 程序从 2.5 版本是现在世面使用最多的版本(xml 配置)
到了 Servlet3.0 之后。就是注解版本的 Servlet 使用

以 2.5 版本为主线讲解 Servlet 程序。

5.Tomcat 的使用

a)安装

找到你需要用的 Tomcat 版本对应的 zip 压缩包,解压到需要安装的目录即可。

b)目录介绍 目录介绍

1641781806747

c) 如何启动 Tomcat 服务器

启动方式一:

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

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

打开浏览器,在浏览器地址栏中输入以下地址测试:
​ 1、http://localhost:8080
​ 2、http://127.0.0.1:8080
​ 3、http://真实 ip:8080
当出现如下界面,说明 Tomcat 服务器启动成功!!!

1641781894573

常见的启动失败的情况:,双击 startup.bat 文件,就会出现一个小黑窗口一闪而来。这个时候,失败的原因基本上都是因为没有配置好 JAVA_HOME 环境变量。

配置 JAVA_HOME 环境变量:

1641781945734

常见的 JAVA_HOME 配置错误有以下几种情况:
​ 一:JAVA_HOME 必须全大写。
​ 二:JAVA_HOME 中间必须是下划线,不是减号-
​ 三:JAVA_HOME 配置的路径只需要配置到 jdk 的安装目录即可。不需要带上 bin 目录。

启动 tomcat 服务器方式二

1、打开命令行
2、cd 到 你的 Tomcat 的 bin 目录下

1641782062362

3、敲入启动命令: catalina run

d)Tomcat 的停止

1、点击 tomcat 服务器窗口的 x 关闭按钮
2、把 Tomcat 服务器窗口置为当前窗口,然后按快捷键 Ctrl+C
3、 找到 Tomcat 的 的 bin 目录下的 shutdown.bat 双击,就可以停止 Tomcat 服务器

e) 如何修改 Tomcat 的端口号

Mysql 默认的端口号是:3306
Tomcat 默认的端口号是:8080

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

1641782223981

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

f) 如何部暑 web 工程到 Tomcat 中

第一种部署方法:只需要把 web 工程的目录拷贝到 Tomcat 的 的 webapps 目录下
即可。

1 、在 webapps 目录下创建一个 book

2、把上午做的书城第一阶段的内容拷贝到里面:

1641782413569

3 、如何访问 Tomcat 下的 web 工程。

只需要在浏览器中输入访问地址格式如下:
http://ip:port/工程名/目录下/文件名

第二种部署方法:

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

1641783507543

abc.xml 配置文件内容如下:

<!-- Context 表示一个工程上下文
path 表示工程的访问路径:/abc
docBase 表示你的工程目录在哪里
-->
<Context path="/abc" docBase="E:\book" />

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

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

手托 html 页面的原理:

1641785137988

输入访问地址访问的原因:

1641785150327

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

1641785236711

6.IDEA 整合 Tomcat 服务器

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

1641785295997

配置你的 Tomcat 安装目录:

1641785312463

就可以通过创建一个 Model 查看是不是配置成功!!!

1641785325307

7.IDEA 中动态 web

a)IDEA 中如何创建动态 web 工程

1、创建一个新模块:

1641785361762

2、选择你要创建什么类型的模块:

1641785387293

1641785394231

3、输入你的模块名,点击【Finish】完成创建。

1641785412735

4、创建成功如下图:

1641785425873

b)Web 工程的目录介绍

1641785512150

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

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

1641785537760

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

1641785554140

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

1641785675259

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

1641785691468

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

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

1641785739864

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

1641785806246

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

1641785821967

4、在 IDEA 中如何运行,和停止 Tomcat 实例。

4.1、正常启动 Tomcat 实例:

1641785845413

4.2、Debug 方式启动 Tomcat 运行实例:

1641785854343

4.3、停止 Tomcat 运行实例:

1641785868994

4.4、重启 Tomcat 运行实例

1641785883556

e)修改工程访问路径

1641785900412

f) 修改运行的端口号 修改运行的端口号

1641785923261

g)修改运行使用的浏览器

1641785942363

h)配置资源热部署

1641785957324

06-Servlet

1.Servlet

a) 什么是 Servlet

1、Servlet 是 JavaEE 规范之一。规范就是接口
2、Servlet 就 JavaWeb 三大组件之一。三大组件分别是:Servlet 程序、Filter 过滤器、Listener 监听器
3、Servlet 是运行在服务器上的一个 java 小程序,它可以接收客户端发送过来的请求,并响应数据给客户端

b) 手动实现 Servlet 程序 程序

1、编写一个类去实现 Servlet 接口
2、实现 service 方法,处理请求,并响应数据
3、到 web.xml 中去配置 servlet 程序的访问地址

Servlet 程序的示例代码:

public class HelloServlet implements Servlet {
/**
* service 方法是专门用来处理请求和响应的
* @param servletRequest
* @param servletResponse
* @throws ServletException
* @throws IOException
*/
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
	System.out.println("Hello Servlet  被访问了");
}
}

web.xml 中的配置:

<?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">

    <!--context-param是上下文参数(它属于整个web工程)-->
    <context-param>
        <param-name>username</param-name>
        <param-value>context</param-value>
    </context-param>
      <!--context-param是上下文参数(它属于整个web工程)-->
    <context-param>
        <param-name>password</param-name>
        <param-value>root</param-value>
    </context-param>


    <!-- servlet标签给Tomcat配置Servlet程序 -->
    <servlet>
        <!--servlet-name标签 Servlet程序起一个别名(一般是类名) -->
        <servlet-name>HelloServlet</servlet-name>
        <!--servlet-class是Servlet程序的全类名-->
        <servlet-class>com.atguigu.servlet.HelloServlet</servlet-class>
        <!--init-param是初始化参数-->
        <init-param>
            <!--是参数名-->
            <param-name>username</param-name>
            <!--是参数值-->
            <param-value>root</param-value>
        </init-param>
        <!--init-param是初始化参数-->
        <init-param>
            <!--是参数名-->
            <param-name>url</param-name>
            <!--是参数值-->
            <param-value>jdbc:mysql://localhost:3306/test</param-value>
        </init-param>
    </servlet>
    <!--servlet-mapping标签给servlet程序配置访问地址-->
    <servlet-mapping>
        <!--servlet-name标签的作用是告诉服务器,我当前配置的地址给哪个Servlet程序使用-->
        <servlet-name>HelloServlet</servlet-name>
        <!--
            url-pattern标签配置访问地址                                     <br/>
               / 斜杠在服务器解析的时候,表示地址为:http://ip:port/工程路径          <br/>
               /hello 表示地址为:http://ip:port/工程路径/hello              <br/>
        -->
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>  
</web-app>

常见的错误 1:url-pattern 中配置的路径没有以斜杠打头。

image-20220105234924185

常见错误 2:servlet-name 配置的值不存在:

image-20220105234939331

常见错误 3:servlet-class 标签的全类名配置错误:

image-20220105234956617

c)url 地址到 Servlet 程序的访问

image-20220105235020705

d)Servlet 的生命周期 的生命周期

1、执行 Servlet 构造器方法
2、执行 init 初始化方法
第一、二步,是在第一次访问,的时候创建 Servlet 程序会调用。
3、执行 service 方法
第三步,每次访问都会调用。
4、执行 destroy 销毁方法
第四步,在 web 工程停止的时候调用。

e)GET 和 和 POST 请求的分发处理

package com.atguigu.servlet;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

public class HelloServlet implements Servlet {

    public HelloServlet() {
        System.out.println("1 构造器方法");
    }

    @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("初始化参数username的值是;" + servletConfig.getInitParameter("username"));
        System.out.println("初始化参数url的值是;" + servletConfig.getInitParameter("url"));
//        3、获取ServletContext对象
        System.out.println(servletConfig.getServletContext());
    }

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

    /**
     * service方法是专门用来处理请求和响应的
     * @param servletRequest
     * @param servletResponse
     * @throws ServletException
     * @throws IOException
     */
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("3 service === Hello Servlet 被访问了");
        // 类型转换(因为它有getMethod()方法)
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        // 获取请求的方式
        String method = httpServletRequest.getMethod();

        if ("GET".equals(method)) {
            doGet();
        } else if ("POST".equals(method)) {
           doPost();
        }

    }

    /**
     * 做get请求的操作
     */
    public void doGet(){
        System.out.println("get请求");
        System.out.println("get请求");
    }
    /**
     * 做post请求的操作
     */
    public void doPost(){
        System.out.println("post请求");
        System.out.println("post请求");
    }


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

    @Override
    public void destroy() {
        System.out.println("4 . destroy销毁方法");
    }
}

注:service方法是专门用来处理请求和响应的

f) 通过继承 HttpServlet 实现 Servlet 程序

一般在实际项目开发中,都是使用继承 HttpServlet 类的方式去实现 Servlet 程序。
1、编写一个类去继承 HttpServlet 类
2、根据业务需要重写 doGet 或 doPost 方法
3、到 web.xml 中的配置 Servlet 程序的访问地址

package com.atguigu.servlet;

import javax.servlet.ServletConfig;
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 HelloServlet2 extends HttpServlet {

    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        System.out.println("重写了init初始化方法,做了一些工作");
    }

    /**
     * doGet()在get请求的时候调用
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        int i =  12 / 0;

        System.out.println("HelloServlet2 的doGet方法");
        // 也可以使用.
        ServletConfig servletConfig = getServletConfig();
        System.out.println(servletConfig);

        //        2、获取初始化参数init-param
        System.out.println("初始化参数username的值是;" + servletConfig.getInitParameter("username"));
        System.out.println("初始化参数url的值是;" + servletConfig.getInitParameter("url"));


    }
    /**
     * doPost()在post请求的时候调用
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("HelloServlet2 的doPost方法");
    }
}

web.xml 中的配置:

<servlet>
	<servlet-name>HelloServlet2</servlet-name>
	<servlet-class>com.atguigu.servlet.HelloServlet2</servlet-class>
</servlet>
<servlet-mapping>
	<servlet-name>HelloServlet2</servlet-name>
	<url-pattern>/hello2</url-pattern>
</servlet-mapping>

g) 使用 IDEA 创建 Servlet 程序

image-20220105235546291

配置 Servlet 的信息:

image-20220105235601677

h)Servlet 类的继承体系 类的继承体系

image-20220105235631307

2.ServletConfig 类

ServletConfig 类从类名上来看,就知道是 Servlet 程序的配置信息类。
Servlet 程序和 ServletConfig 对象都是由 Tomcat 负责创建,我们负责使用。
Servlet 程序默认是第一次访问的时候创建,ServletConfig 是每个 Servlet 程序创建时,就创建一个对应的 ServletConfig 对象

a)ServletConfig 类的三大作用

1、可以获取 Servlet 程序的别名 servlet-name 的值
2、获取初始化参数 init-param
3、获取 ServletContext 对象

web.xml 中的配置:

 <!-- servlet标签给Tomcat配置Servlet程序 -->
    <servlet>
        <!--servlet-name标签 Servlet程序起一个别名(一般是类名) -->
        <servlet-name>HelloServlet</servlet-name>
        <!--servlet-class是Servlet程序的全类名-->
        <servlet-class>com.atguigu.servlet.HelloServlet</servlet-class>
        <!--init-param是初始化参数-->
        <init-param>
            <!--是参数名-->
            <param-name>username</param-name>
            <!--是参数值-->
            <param-value>root</param-value>
        </init-param>
        <!--init-param是初始化参数-->
        <init-param>
            <!--是参数名-->
            <param-name>url</param-name>
            <!--是参数值-->
            <param-value>jdbc:mysql://localhost:3306/test</param-value>
        </init-param>
    </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>

Servlet 中的代码:

    /**
     * doGet()在get请求的时候调用
     */
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        int i =  12 / 0;

        System.out.println("HelloServlet2 的doGet方法");
        // 也可以使用.
        ServletConfig servletConfig = getServletConfig();
        System.out.println(servletConfig);

        //        2、获取初始化参数init-param
        System.out.println("初始化参数username的值是;" + servletConfig.getInitParameter("username"));
        System.out.println("初始化参数url的值是;" + servletConfig.getInitParameter("url"));


    }

注意点: 如果重写init方法,一定要调用父类的init方法

image-20220106001104721

3.ServletContext 类

a) 什么是 ServletContext?

1、ServletContext 是一个接口,它表示 Servlet 上下文对象
2、一个 web 工程,只有一个 ServletContext 对象实例。
3、ServletContext 对象是一个域对象。
4、ServletContext 是在 web 工程部署启动的时候创建。在 web 工程停止的时候销毁。
什么是域对象?
域对象,是可以像 Map 一样存取数据的对象,叫域对象。
这里的域指的是存取数据的操作范围,整个 web 工程。

image-20220106001407213

b)ServletContext 类的四个作用 类的四个作用

1、获取 web.xml 中配置的上下文参数 context-param
2、获取当前的工程路径,格式: /工程路径
3、获取工程部署后在服务器硬盘上的绝对路径
4、像 Map 一样存取数据

web.xml 中的配置:

<?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">

    <!--context-param是上下文参数(它属于整个web工程)-->
    <context-param>
        <param-name>username</param-name>
        <param-value>context</param-value>
    </context-param>
    <!--context-param是上下文参数(它属于整个web工程)-->
    <context-param>
        <param-name>password</param-name>
        <param-value>root</param-value>
    </context-param>
 
</web-app>

ServletContext 演示代码

   protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//        1、获取web.xml中配置的上下文参数context-param
        ServletContext context = getServletConfig().getServletContext();

        String username = context.getInitParameter("username");
        System.out.println("context-param参数username的值是:" + username);
        System.out.println("context-param参数password的值是:" + context.getInitParameter("password"));
//        2、获取当前的工程路径,格式: /工程路径
        System.out.println( "当前工程路径:" + context.getContextPath() );
//        3、获取工程部署后在服务器硬盘上的绝对路径
        /**
         *  / 斜杠被服务器解析地址为:http://ip:port/工程名/  映射到IDEA代码的web目录<br/>
         */
        System.out.println("工程部署的路径是:" + context.getRealPath("/"));
        System.out.println("工程下css目录的绝对路径是:" + context.getRealPath("/css"));
        System.out.println("工程下imgs目录1.jpg的绝对路径是:" + context.getRealPath("/imgs/1.jpg"));
    }

ServletContext 像 Map 一样存取数据:

ContextServlet1 代码:

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 获取ServletContext对象
        ServletContext context = getServletContext();
        System.out.println(context);
        System.out.println("保存之前: Context1 获取 key1的值是:"+ context.getAttribute("key1"));

        context.setAttribute("key1", "value1");

        System.out.println("Context1 中获取域数据key1的值是:"+ context.getAttribute("key1"));
    }

ContextServlet2 代码:

public class ContextServlet2 extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ServletContext context = getServletContext();
        System.out.println(context);
        System.out.println("Context2 中获取域数据key1的值是:"+ context.getAttribute("key1"));
    }
}

4.HTTP

a) 什么是 HTTP 协议

什么是协议?
​ 协议是指双方,或多方,相互约定好,大家都需要遵守的规则,叫协议。
所谓 HTTP 协议,就是指,客户端和服务器之间通信时,发送的数据,需要遵守的规则,叫 HTTP 协议。
HTTP 协议中的数据又叫报文。

b) 请求的 HTTP 协议格式

客户端给服务器发送数据叫请求
服务器给客户端回传数据叫响应。
请求又分为 GET 请求,和 POST 请求两种

i. GET 请求

image-20220106002153456

image-20220106002201989

ii. POST 请求

image-20220106002237811

image-20220106002250985

iii. 常用请求头的说明

Accept: 表示客户端可以接收的数据类型
Accpet-Languege: 表示客户端可以接收的语言类型
User-Agent: 表示客户端浏览器的信息
Host: 表示请求时的服务器 ip 和端口号

iv. 哪些是 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

c) 响应的 HTTP 协议格式

image-20220106002502773

image-20220106002521010

d)常用的响应码说明

200 表示请求成功
302 表示请求重定向
404 表示请求服务器已经收到了,但是你要的数据不存在(请求地址错误)
500 表示服务器已经收到请求,但是服务器内部错误(代码错误)

e)MIME 类型说明

MIME 是 HTTP 协议中数据类型。
MIME 的英文全称是"Multipurpose Internet Mail Extensions" 多功能 Internet 邮件扩充服务。MIME 类型的格式是“大类型/小
类型”,并与某一种文件的扩展名相对应。

常见的 MIME 类型:

image-20220106002647986

image-20220106002659239

谷歌浏览器如何查看 HTTP 协议:

image-20220106002711766

火狐浏览器如何查看 HTTP 协议:

image-20220106002721794

5.常见请求和响应头

HTTP t Request r Header

image-20220106130549686

image-20220106130606028

HTTP s Responses r Header

image-20220106130617803

image-20220106130628692

image-20220106130642211

07-Servlet(下)

1.HttpServletRequest 类

a)HttpServletRequest 类有什么作用。

每次只要有请求进入 Tomcat 服务器,Tomcat 服务器就会把请求过来的 HTTP 协议信息解析好封装到 Request 对象中。然后传递到 service 方法(doGet 和 doPost)中给我们使用。我们可以通过 HttpServletRequest 对象,获取到所有请求的
信息

b)HttpServletRequest 类的常用方法

image-20220106131255424

常用 API 示例代码:

public class RequestAPIServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
 //        i.getRequestURI()					获取请求的资源路径
        System.out.println("URI => " + req.getRequestURI());
//        ii.getRequestURL()					获取请求的统一资源定位符(绝对路径)
        System.out.println("URL => " + req.getRequestURL());
//        iii.getRemoteHost()				获取客户端的ip地址
        /**
         * 在IDEA中,使用localhost访问时,得到的客户端 ip 地址是 ===>>> 127.0.0.1<br/>
         * 在IDEA中,使用127.0.0.1访问时,得到的客户端 ip 地址是 ===>>> 127.0.0.1<br/>
         * 在IDEA中,使用 真实ip 访问时,得到的客户端 ip 地址是 ===>>> 真实的客户端 ip 地址<br/>
         */
        System.out.println("客户端 ip地址 => " + req.getRemoteHost());
//        iv.getHeader()						获取请求头
        System.out.println("请求头User-Agent ==>> " + req.getHeader("User-Agent"));
//        vii.getMethod()					获取请求的方式GET或POST
        System.out.println("请求的方式 ==>> " + req.getMethod());
    }
}

c)如何获取请求参数 如何获取请求参数

表单:

<body>
    <form action="http://localhost:8080/07_servlet/parameterServlet" method="post">
        用户名:<input type="text" name="username"><br/>
        密码:<input type="password" name="password"><br/>
        兴趣爱好:<input type="checkbox" name="hobby" value="cpp">C++
        <input type="checkbox" name="hobby" value="java">Java
        <input type="checkbox" name="hobby" value="js">JavaScript<br/>
        <input type="submit">
    </form>
</body>

Java 代码:

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("-------------doGet------------");

        // 获取请求参数
        String username = req.getParameter("username");

        //1 先以iso8859-1进行编码
        //2 再以utf-8进行解码
        username = new String(username.getBytes("iso-8859-1"), "UTF-8");

        String password = req.getParameter("password");
        String[] hobby = req.getParameterValues("hobby");

        System.out.println("用户名:" + username);
        System.out.println("密码:" + password);
        System.out.println("兴趣爱好:" + Arrays.asList(hobby));
    }

doGet 请求的中文乱码解决(tomcat8版本以上不用再处理doget请求的乱码了,因为Tomcat8版本以上默认编码、解码都是utf-8):

//1 先以iso8859-1进行编码
//2 再以utf-8进行解码
username = new String(username.getBytes("iso-8859-1"), "UTF-8");

乱码产生的原因

在网络传递过程中编码是不确定的,和浏览器的默认设置有关,通常是iso-8859-1编码。但是tomcat中对于uri默认的解码方式是通过iso-8859-1来解码,那么编码和解码不一致导致了乱码**

解决办法

在Tomcat下面conf文件夹下的server.xml文件中添加URIEncoding="UTF-8",这样tomcat就会默认用utf-8来解析uri了

修改前:
<Connector port="8080" protocol="HTTP/1.1" 
               maxThreads="150" connectionTimeout="20000" 
               redirectPort="8443" />
修改后:
<Connector port="8080" protocol="HTTP/1.1" 
               maxThreads="150" connectionTimeout="20000" 
               redirectPort="8443" URIEncoding="UTF-8" />

d)POST 请求的中文乱码解决 请求的中文乱码解决

设置req.setCharacterEncoding("UTF-8");

    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 设置请求体的字符集为UTF-8,从而解决post请求的中文乱码问题
        // 也要在获取请求参数之前调用才有效
        req.setCharacterEncoding("UTF-8");

        System.out.println("-------------doPost------------");
        // 获取请求参数
        String username = req.getParameter("username");
        System.out.println("username" + username);
        String password = req.getParameter("password");
        String[] hobby = req.getParameterValues("hobby");

        System.out.println("用户名:" + username);
        System.out.println("密码:" + password);
        System.out.println("兴趣爱好:" + Arrays.asList(hobby));
    }

解决乱码最好的方式:定义过滤器

request.setCharacterEncoding("UTF-8");

response.setContentType("text/html;charset=utf-8");

public class EncodingFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
       public class EncodingFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        request.setCharacterEncoding("UTF-8"); 
        response.setContentType("text/html;charset=utf-8");
        chain.doFilter(request,response);
    }

    @Override
    public void destroy() {

    }
}
        chain.doFilter(request,response);
    }

    @Override
    public void destroy() {

    }
}

总结解决乱码的方法大纲:

网上这边文章写得很详细:https://www.freesion.com/article/4052449233/

1ddd47626327d1b77b139b90a9ff197e

书城第二阶段 ——用户注册和登陆

1.JavaEE 项目的三层架构

1641796618199

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

1641796703247

搭建书城项目开发环境:

1641796735858

1、先创建书城需要的数据库和表。

drop DATABASE if EXISTS book;
create DATABASE book;
use 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@atguigu.com');
select * from t_user;

2、编写数据库表对应的 JavaBean 对象。

public class User {
    private Integer id;
    private String username;
    private String password;
    private String email;
}

3、编写 工具类 JdbcUtils

3.1 、导入需要的 jar 包(数据库和连接池需要):

druid-1.1.9.jar
mysql-connector-java-5.1.7-bin.jar

以下是测试需要:
hamcrest-core-1.3.jar
junit-4.12.jar

3.2 、在 src 源码目录下编写 jdbc.properties

username=root
password=abc123
url=jdbc:mysql://localhost:3306/book
driverClassName=com.mysql.jdbc.Driver
initialSize=5
maxActive=10

3.3 、编写 JdbcUtils 工具类

public class JdbcUtils {

    private static DruidDataSource dataSource;

    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 = null;

        try {
            conn = dataSource.getConnection();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return conn;
    }

    /**
     * 关闭连接,放回数据库连接池
     *
     * @param conn
     */
    public static void close(Connection conn) {
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

}

3.4 、JdbcUtils 测试

public class JdbcUtilsTest {

    @Test
    public void testJdbcUtils() {
        for (int i = 0; i < 100; i++) {
            Connection connection = JdbcUtils.getConnection();
            System.out.println(connection);
            JdbcUtils.close(connection);
        }
    }

}

4、编写 编写 BaseDao

4.1 、导入 DBUtils 的 的 jar 包 包

commons-dbutils-1.3.jar

1641798534888

4.2 、编写 BaseDao

public abstract class BaseDao {

    //使用DbUtils操作数据库
    private QueryRunner queryRunner = new QueryRunner();

    /**
     * update() 方法用来执行:Insert\Update\Delete语句
     *
     * @return 如果返回-1,说明执行失败<br/>返回其他表示影响的行数
     */
    public int update(String sql, Object... args) {
        Connection connection = JdbcUtil.getConnection();
        try {
            return queryRunner.update(connection, sql, args);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.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 con = JdbcUtil.getConnection();
        try {
            return queryRunner.query(con, sql, new BeanHandler<T>(type), args);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(con);
        }
        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 con = JdbcUtil.getConnection();
        try {
            return queryRunner.query(con, sql, new BeanListHandler<T>(type), args);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(con);
        }
        return null;
    }

    /**
     * 执行返回一行一列的sql语句
     *
     * @param sql  执行的sql语句
     * @param args sql对应的参数值
     * @return
     */
    public Object queryForSingleValue(String sql, Object... args) {

        Connection conn = JdbcUtil.getConnection();

        try {
            return queryRunner.query(conn, sql, new ScalarHandler(), args);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(conn);
        }
        return null;

    }

}

5 、编写 UserDao 和测试

UserDao 接口:

public interface UserDao {



    /**
     * 根据用户名查询用户信息
     * @param username 用户名
     * @return 如果返回null,说明没有这个用户。反之亦然
     */
    public User queryUserByUsername(String username);

    /**
     * 根据 用户名和密码查询用户信息
     * @param username
     * @param password
     * @return 如果返回null,说明用户名或密码错误,反之亦然
     */
    public User queryUserByUsernameAndPassword(String username,String password);

    /**
     * 保存用户信息
     * @param user
     * @return 返回-1表示操作失败,其他是sql语句影响的行数
     */
    public int saveUser(User user);

}

UserDaoImpl 实现类:

public class UserDaoImpl extends BaseDao implements UserDao {
    @Override
    public User queryUserByUsername(String username) {
        String sql = "select `id`,`username`,`password`,`email` from t_user where username = ?";
        return queryForOne(User.class, sql, username);
    }

    @Override
    public User queryUserByUsernameAndPassword(String username, String password) {
        String sql = "select `id`,`username`,`password`,`email` 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());
    }
}

UserDao 测试:

public class UserDaoTest {

    UserDao userDao = new UserDaoImpl();

    @Test
    public void queryUserByUsername() {

        if (userDao.queryUserByUsername("admin1234") == null) {
            System.out.println("用户名可用!");
        } else {
            System.out.println("用户名已存在!");
        }
    }

    @Test
    public void queryUserByUsernameAndPassword() {
        if (userDao.queryUserByUsernameAndPassword("admin", "admin1234") == null) {
            System.out.println("用户名或密码错误,登录失败");
        } else {
            System.out.println("查询成功");
        }
    }

    @Test
    public void saveUser() {
        System.out.println(userDao.saveUser(new User(null, "wzg168", "123456", "wzg168@qq.com")));
    }
}

6 、编写 UserService 和测试

UserService 接口

public interface UserService {
    /**
     * 注册用户
     *
     * @param user
     */
    public void registUser(User user);

    /**
     * 登录
     *
     * @param user
     * @return 如果返回null,说明登录失败,返回有值,是登录成功
     */
    public User login(User user);

    /**
     * 检查 用户名是否可用
     *
     * @param username
     * @return 返回true表示用户名已存在,返回false表示用户名可用
     */
    public boolean existsUsername(String username);
}

UserServiceImpl 实现类:

public class UserServiceImpl implements UserService {

    private UserDao userDao = new UserDaoImpl();

    @Override
    public void registUser(User user) {
        userDao.saveUser(user);
    }

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

    @Override
    public boolean existsUsername(String username) {

        if (userDao.queryUserByUsername(username) == null) {
           // 等于null,说明没查到,没查到表示可用
           return false;
        }

        return true;

    }
}

UserService测试

public class UserServiceTest {

    UserService userService = new UserServiceImpl();

    @Test
    public void registUser() {
        userService.registUser(new User(null, "bbj168", "666666", "bbj168@qq.com"));
        userService.registUser(new User(null, "abc168", "666666", "abc168@qq.com"));
    }

    @Test
    public void login() {
        System.out.println(userService.login(new User(null, "wzg168", "123456", null)));
    }

    @Test
    public void existsUsername() {
        if (userService.existsUsername("wzg16888")) {
            System.out.println("用户名已存在!");
        } else {
            System.out.println("用户名可用!");
        }
    }
}

7 、编写 web 层

7.1 、实现用户注册的功能

7.1.1 、图解用户注册的流程

1641800734627

7.1.2 、修改 regist.html 和 regist_success.html

1、添加 base 标签

<!-- 写 base 标签,永远固定相对路径跳转的结果 -->
<base href="http://localhost:8080/book/">

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

以下是几个修改的示例:
<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、修改注册表单的提交地址和请求方式

1641800916278

7.1.3 、编写 RegistServlet 程序
public class RegistServlet extends HttpServlet {

    private UserService userService = new UserServiceImpl();

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //  1、获取请求的参数
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String email = req.getParameter("email");
        String code = req.getParameter("code");

//        2、检查 验证码是否正确  === 写死,要求验证码为:abcde
        if ("abcde".equalsIgnoreCase(code)) {
//        3、检查 用户名是否可用
            if (userService.existsUsername(username)) {
                System.out.println("用户名[" + username + "]已存在!");
//        跳回注册页面
                req.getRequestDispatcher("/pages/user/regist.html").forward(req, resp);
            } else {
                //      可用
//                调用Sservice保存到数据库
                userService.registUser(new User(null, username, password, email));
//
//        跳到注册成功页面 regist_success.html
                req.getRequestDispatcher("/pages/user/regist_success.html").forward(req, resp);
            }
        } else {
            System.out.println("验证码[" + code + "]错误");
            req.getRequestDispatcher("/pages/user/regist.html").forward(req, resp);
        }
    }
}

7.2 、IDEA 中 中 Debug 调试的使用

7.2.1 、Debug 点 调试代码

首先需要两个元素:断点 + Debug

1、断点,只需要在代码需要停的行的左边上单击,就可以添加和取消
2、Debug 启动 Tomcat 运行代码:

1641801164861

7.2.2 、测试工具栏

1641801244009

1641801270554

7.2.3 、变量窗口

变量窗口:它可以查看当前方法范围内所有有效的变量

1641801299672

7.2.4 、方法调用栈窗口

1、方法调用栈可以查看当前线程有哪些方法调用信息
2、下面的调用上一行的方法

1641801434794

其他常用调试相关按钮:

1641801445932

7.3、用户登录功能的实现 、用户登录功能的实现

7.3.1 、图解用户登录

1641801501203

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

1、添加 base 标签

<!-- 写 base 标签,永远固定相对路径跳转的结果 -->
<base href="http://localhost:8080/book/">

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

以下是几个修改的示例:

<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、修改 login.html 表单的提交地址和请求方式

1641801664173

7.3.3 、LoginServlet 程序
public class LoginServlet extends HttpServlet {

    private UserService userService = new UserServiceImpl();

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //  1、获取请求的参数
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        // 调用 userService.login()登录处理业务
        User loginUser = userService.login(new User(null, username, password, null));
        // 如果等于null,说明登录 失败!
        if (loginUser == null) {
            //   跳回登录页面
            req.getRequestDispatcher("/pages/user/login.html").forward(req, resp);
        } else {
            // 登录 成功
            //跳到成功页面login_success.html
            req.getRequestDispatcher("/pages/user/login_success.html").forward(req, resp);
        }
    }
}

项目阶段二:用户注册和登陆的实现。

需求 1 :用户注册
需求如下:

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

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

08-jsp

1.什么是jsp,它有什么用?

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

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

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

Servlet回传html页面数据的代码:

public class PringHtml extends HttpServlet {


    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

//        JspWriter
        
        // 通过响应的回传流回传html页面数据
        resp.setContentType("text/html; charset=UTF-8");
        PrintWriter writer = resp.getWriter();
        writer.write("<!DOCTYPE html>\r\n");
        writer.write("  <html lang=\"en\">\r\n");
        writer.write("  <head>\r\n");
        writer.write("      <meta charset=\"UTF-8\">\r\n");
        writer.write("      <title>Title</title>\r\n");
        writer.write("  </head>\r\n");
        writer.write(" <body>\r\n");
        writer.write("    这是html页面数据 \r\n");
        writer.write("  </body>\r\n");
        writer.write("</html>\r\n");
        writer.write("\r\n");
    }
}

jsp回传一个简单html页面的代码:

<%@pagecontentType="text/html;charset=UTF-8"language="java"%>
<html>
    <head>
        <title>Title</title>
    </head>
    <body>这是html页面数据</body>
</html>

jsp的小结:

​ 1、如何创建jsp的页面?

1641547230898

输入文件名敲回车即可! !

1641547429469

2、jsp 如何访问:

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

比如:

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

​ web 目录

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

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

2.jsp 的本质是什么。

jsp 页面本质上是一个 Servlet 程序。

当我们第一次访问 jsp 页面的时候。Tomcat 服务器会帮我们把 jsp 页面翻译成为一个 java 源文件。并且对它进行编译成 为.class 字节码程序。我们打开 java 源文件不难发现其里面的内容是:

1641547663233

我们跟踪原代码发现,HttpJspBase 类。它直接地继承了 HttpServlet 类。也就是说。jsp 翻译出来的 java 类,它间接了继 承了 HttpServlet 类。也就是说,翻译出来的是一个 Servlet 程序

1641547707126

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

大家也可以去观察翻译出来的 Servlet 程序的源代码,不难发现。其底层实现,也是通过输出流把 html 页面数据回传给客户端

 public void _jspService(final HttpServletRequest request, final HttpServletResponse response)
      throws java.io.IOException, ServletException {

    final String _jspx_method = request.getMethod();
    if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method) && !DispatcherType.ERROR.equals(request.getDispatcherType())) {
      response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSP 只允许 GET、POST 或 HEAD。Jasper 还允许 OPTIONS");
      return;
    }

    final PageContext pageContext;
    HttpSession session = null;
    final ServletContext application;
    final ServletConfig config;
    JspWriter out = null;
    final Object page = this;
    JspWriter _jspx_out = null;
    PageContext _jspx_page_context = null;


    try {
      response.setContentType("text/html;charset=UTF-8");
      pageContext = _jspxFactory.getPageContext(this, request, response,
      			null, true, 8192, true);
      _jspx_page_context = pageContext;
      application = pageContext.getServletContext();
      config = pageContext.getServletConfig();
      session = pageContext.getSession();
      out = pageContext.getOut();
      _jspx_out = out;

      out.write("\r\n");
      out.write("\r\n");
      out.write("<html>\r\n");
      out.write("<head>\r\n");
      out.write("    <title>Title</title>\r\n");
      out.write("</head>\r\n");
      out.write("<body>\r\n");
      out.write("<h1>aa.jsp</h1>\r\n");
      out.write("\r\n");
      out.write("</body>\r\n");
      out.write("</html>\r\n");
    } catch (Throwable t) {
      if (!(t instanceof SkipPageException)){
        out = _jspx_out;
        if (out != null && out.getBufferSize() != 0)
          try {
            if (response.isCommitted()) {
              out.flush();
            } else {
              out.clearBuffer();
            }
          } catch (java.io.IOException e) {}
        if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
        else throw new ServletException(t);
      }
    } finally {
      _jspxFactory.releasePageContext(_jspx_page_context);
    }
  }

3.jsp 的三种语法

a)jsp 头部的 page 指令

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

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

1641550070206

<!-- errorPage 表示错误后自动跳转去的路径 
 这个路径一般都是以斜杠打头,它表示请求地址为 http://ip:port/工程路径/映射到代码的 Web 目录 -->

1641550204334

b)jsp 中的常用脚本

i. 声明脚本(极少使用)

声明脚本的格式是: <%! 声明 java 代码 %>

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

练习:

1、声明类属性

2、声明 static 静态代码块

3、声明类方法

4、声明内部类

代码示例:

	<%--1 、声明类属性 --%>
	<%!private Integer id;
	private String name;
	private static Map<String, Object> map;%>
	<%--2 、声明 static 静态代码块 --%>
	<%!static {
		map = new HashMap<String, Object>();
		map.put("key1", "value1");
		map.put("key2", "value2");
		map.put("key3", "value3");
	}%>
	<%--3 、声明类方法 --%>
	<%!public int abc() {
		return 12;
	}%>
	<%--4 、声明内部类 --%>
	<%!public static class A {
		private Integer id = 12;
		private String abc = "abc";
	}%>

声明脚本代码翻译对照:

image-20220107215249540

ii. 表达式脚本 (常用)

表达式脚本的格式是:<%=表达式%>
表达式脚本的作用是:的 jsp 页面上输出数据
表达式脚本的特点:
1、所有的表达式脚本都会被翻译到_jspService()方法中
2、表达式脚本都会被翻译成为 out.print()输出到页面上
3、由于表达式脚本翻译的内容都在_jspService() 方法中,所以_jspService()方法中的对象都可以直接使用

image-202201072204078854、表达式脚本中的表达式不能以分号结束
练习:

  1. 输出整型
  2. 输出浮点型
  3. 输出字符串
  4. 输出对象

示例代码:

	<%=12%>
	<br>
	<%=12.12%>
	<br>
	<%=" 我是字符串"%>
	<br>
	<%=map%>
	<br>
	<%=request.getParameter("username")%>

翻译对照:

image-20220107220547741

iii. 代码脚本

代码脚本的格式是:

<%
java 语句
%>

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

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

练习:

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

示例代码:

<body>
	<%-- 练习: --%>
	<%--1. 代码脚本 ----if 语句 --%>
	<%
		int i = 13;
		if (i == 12) {
	%>
	<h1>国哥好帅</h1>
	<%
		} else {
	%>
	<h1>国哥又骗人了!</h1>
	<%
		}
	%>
	<br>
	<%--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);
	%>

翻译之后的对比:

image-20220107221154762

c)jsp 中的三种注释

i. html 注释

<!-- 这是 html 注释 -->

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

ii. java 注释
<%
	// 单行 java 注释
	/* 多行 java 注释 */
 %>

单行注释和多行注释能在翻译后的 java 源代码中看见。

iii. jsp 注释

<%-- 这是 jsp 注释 --%>

jsp 注释在翻译的时候会直接被忽略掉

image-20220107223828799

4.jsp 九大内置对象

jsp 中的内置对象,是指 Tomcat 在翻译 jsp 页面成为 Servlet 源代码后,内部提供的九大对象,叫内置对象。

image-20220107224002500

request 	对象 请求对象,可以获取请求信息
response 	对象 响应对象。可以设置响应信息
pageContext 对象 当前页面上下文对象。可以在当前上下文保存属性信息
session 	对象 会话对象。可以获取会话信息。
exception 	对象 异常对象只有在 jsp 页面的 page 指令中设置 isErrorPage="true" 的时候才会存在
application 对象 ServletContext 对象实例,可以获取整个工程的一些信息。
config 		对象 ServletConfig 对象实例,可以获取 Servlet 的配置信息
out 		对象 输出流。
page 		对象 表示当前 Servlet 对象实例(无用,用它不如使用 this 对象)。

九大内置对象 , 都是我们可以在 【 代码脚本 】 中或 【 表达式脚本 】 中直接使用的对
象。

5.jsp 四大域对象

image-20220107224253883

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

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

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

1) jsp 中 中 out 和 和 response 的 的 writer 的区别演示 的区别演示

	<%
		// out 输出
		out.write("这是 out 的第一次输出<br/>");
		// out flush 之后。会把输出的内容写入 writer 的缓冲区中
		out.flush();
		// 最后一次的输出,由于没有手动 flush,会在整个页面输出到客户端的时候,自动写入到 writer
		缓冲区
		out.write("这是 out 的第二次输出<br/>");
		// writer 的输出
		response.getWriter().write("这是 writer 的第一次输出<br/>");
		response.getWriter().write("这是 writer 的第二次输出<br/>");
%>

在浏览器里输入 http://127.0.0.1:8080/day09/output.jsp 运行查看的结果:

image-20220107225144147

2) 图解 图解 out 流和 writer 流的两个缓冲区如何工作

image-20220107225207332

由于 jsp 翻译之后,底层源代码都是使用 out 来进行输出,所以一般情况下。我们在 jsp 页面中统一使用 out 来进行输出。避免打乱页面输出内容的顺序。

out.write() 输出字符串没有问题
out.print() 输出任意数据都没有问题(都转换成为字符串后调用的 write 输出)

深入源码,浅出结论:在 jsp 页面中,可以统一使用 out.print()来进行输出

7.jsp 的常用标签

a)jsp 静态包含

示例说明:

	<%--
	<%@ include file=""%>
	
	就是静态包含 file 属性指定你要包含的 jsp 页面的路径 地址中第一个斜杠 / 表示为 http://ip:port/ 工程路径 /
	映射到代码的 web 目录 静态包含的特点: 
	1 、静态包含不会翻译被包含的 jsp 页面。 
	2 、静态包含其实是把被包含的 jsp页面的代码拷贝到包含的位置执行输出。
	 --%>
	<%@ include file="/include/footer.jsp"%>

b)jsp 动态包含 动态包含

示例说明:

<body>
	<%--
		<jsp:include page=""></jsp:include> 这是动态包含
		page 属性是指定你要包含的 jsp 页面的路径
		动态包含也可以像静态包含一样。把被包含的内容执行输出到包含位置
		动态包含的特点:
			1 、动态包含会把包含的 jsp 页面也翻译成为 java 代码
			2 、动态包含底层代码使用如下代码去调用被包含的 jsp 页面执行输出。
			JspRuntimeLibrary.include(request, response,"/include/footer.jsp", out, false);
			3 、动态包含,还可以传递参数
			动态包含会把包含的 jsp 页面单独翻译成 servlet 文件,然后在执行到时候再调用翻译的 servlet 程序。并把计算的结果返回。
			动态包含是在执行的时候,才会加载。所以叫动态包含。
--%>
	<jsp:include page="/include/footer.jsp">
		<jsp:param name="username" value="bbj" />
		<jsp:param name="password" value="root" />
	</jsp:include>
</body>

image-20220107231733219

c)jsp 标签-转发

示例说明:

<%--
	<jsp:forward page=""></jsp:forward> 是请求转发标签,它的功能就是请求转发
page 属性设置请求转发的路径
--%>
<jsp:forward page="/scope2.jsp"></jsp:forward>

静态包含和动态包含的区别:

image-20220107232010889

在这里需要补充说明一点:我们在工作中,几乎都是使用静态包含。理由很简单。因为 jsp 页面虽然可以写 java 代码,做其他的功能操作。但是由于 jsp 在开发过程中被定位为专门用来展示页面的技术。也就是说。jsp 页面中,基本上只有 html,css,js。还有一些简单的 EL,表达式脚本等输出语句。所以我们都使用静态包含。

8、 Listener 监听器

1、Listener 监听器它是 JavaWeb 的三大组件之一。JavaWeb 的三大组件分别是:Servlet 程序、Filter 过滤器、Listener 监听器
2、Listener 它是 JavaEE 的规范,就是接口
3、监听器的作用是,监听某种事物的变化。然后通过回调函数,反馈给客户(程序)去做一些相应的处理

8.1 、ServletContextListener 监听器 监听器

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

ServletContext 对象在 web 工程启动的时候创建,在 web 工程停止的时候销毁。
如果监听到 创建和销毁之后都会分别调用 ServletContextListener 监听器的方法反馈。

监听器的使用步骤:

第一步:我们需要定义一个类。然后去继承生命周期的监听器接口。
第二步:然后在 Web.xml 文件中配置。
ServletContextListener 监听器,一定要在 web.xml 文件中配置之后才会生效

<listener>
	<listener-class>全类名</listener-class>
</listener>
生命周期监听器两个方法:
public void contextInitialized(ServletContextEvent sce) 是 ServletContext 对象的创建回调
public void contextDestroyed(ServletContextEvent sce) 是 ServletContext 对象的销毁回调

我们以 ServletContext 的监听器为例演示如下:

1 )创建一个 ServletContextListenerImpl 类实现 ServletContextListener

public class MyServletContextListenerImpl implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("ServletContext对象被创建了");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("ServletContext对象被销毁了");
    }
}

2 )在 web.xml 文件中的配置如下:

<listener>
	<listener-class>com.atguigu.listener.RequestListener</listener-class>
</listener>

3 )这个时候,启动 web 工程和正常停止 web 工程,后台都会如下打印:

image-20220108000238943

09(上)-EL 式 表达式 & JSTL 标签库

1.EL 表达式

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

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

<body>
	<%
		request.setAttribute("key", " 值");
	%>
表达式脚本输出 key 的值是:
<%=request.getAttribute("key1") == null ? "" : request.getAttribute("key1")%><br />

EL 表达式输出 key 的值是:${key1}
</body>

EL 表达式的格式是:${表达式}
EL 表达式在输出 null 值的时候,输出的是空串。jsp 表达式脚本输出 null 值的时候,输出的是 null 字符串。

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

EL 表达式主要是在 jsp 页面中输出数据。
主要是输出域对象中的数据。

四个域中都有相同的 key 的数据的时候,EL 表达式会按照四个域的从小到大的顺序去进行搜索,找到就输出(pageScope---->requestScope------>sessionScope------->applicationScope)

<body>
	<%
		// 往四个域中都保存了相同的 key 的数据。
		pageContext.setAttribute("key", "pageContext");
		request.setAttribute("key", "request");
		session.setAttribute("key", "session");
		application.setAttribute("key", "application");
		
	%>
	${ key }
</body>

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

i. 需求——输出 Person 类中普通属性,数组属性。list 集合属性和 map 集合属性。

Person 类

public class Person {
//    i.需求——输出Person类中普通属性,数组属性。list集合属性和map集合属性。
    private String name;
    private String[] phones;
    private List<String> cities;
    private Map<String,Object> map;

    public int getAge() {
        return 18;
    }

    public Person(String name, String[] phones, List<String> cities, Map<String, Object> map) {
        this.name = name;
        this.phones = phones;
        this.cities = cities;
        this.map = map;
    }

    public Person() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String[] getPhones() {
        return phones;
    }

    public void setPhones(String[] phones) {
        this.phones = phones;
    }

    public List<String> getCities() {
        return cities;
    }

    public void setCities(List<String> cities) {
        this.cities = cities;
    }

    public Map<String, Object> getMap() {
        return map;
    }

    public void setMap(Map<String, Object> map) {
        this.map = map;
    }


    @Override
    public String toString() {
        return "Person{" +
                "name=" + name +
                ", phones=" + Arrays.toString(phones) +
                ", cities=" + cities +
                ", map=" + map +
                '}';
    }
}
<body>
    <%
        Person person = new Person();
        person.setName("国哥好帅!");
        person.setPhones(new String[]{"18610541354","18688886666","18699998888"});

        List<String> cities = new ArrayList<String>();
        cities.add("北京");
        cities.add("上海");
        cities.add("深圳");
        person.setCities(cities);

        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的name属性:${p.name} <br>
    输出Person的pnones数组属性值:${p.phones[2]} <br>
    输出Person的cities集合中的元素值:${p.cities} <br>
    输出Person的List集合中个别元素值:${p.cities[2]} <br>
    输出Person的Map集合: ${p.map} <br>
    输出Person的Map集合中某个key的值: ${p.map.key3} <br>
    输出Person的age属性:${p.age} <br>
</body>

d)EL 表达式 表达式 —— 运算

语法:${ 运算表达式 } , EL 表达式支持如下运算符:

1) 关系运算

image-20220108003441441

  1. 逻辑运算

image-20220108003513895

  1. 算数运算

image-20220108003540188

image-20220108003555149

i. empty 运算

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

以下几种情况为空:
​ 1、值为 null 值的时候,为空
​ 2、值为空串的时候,为空
​ 3、值是 Object 类型数组,长度为零的时候
​ 4、list 集合,元素个数为零
​ 5、map 集合,元素个数为零

<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/>

    <hr>
    ${ 12 != 12 ? "国哥帅呆":"国哥又骗人啦" }

</body>
ii. 三元运算

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

示例:

${ 12 != 12 ? " 国哥帅呆":" 国哥又骗人啦" }
iii. “ . ”算 点运算 和 和 [] 中括号运算符

.点运算,可以输出 Bean 对象中某个属性的值。
[]中括号运算,可以输出有序集合中某个元素的值。
并且[]中括号运算,还可以输出 map 集合中 key 里含有特殊字符的 key 的值。

<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 表达式中自己定义的,可以直接使用。

image-20220108085552060

image-20220108004503685

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

pageScope ====== pageContext 域
requestScope ====== Request 域
sessionScope ====== Session 域
applicationScope ====== ServletContext 域

<body>
    <%
        pageContext.setAttribute("key1", "pageContext1");
        pageContext.setAttribute("key2", "pageContext2");
        request.setAttribute("key2", "request");
        session.setAttribute("key2", "session");
        application.setAttribute("key2", "application");
    %>
    ${ applicationScope.key2 }
</body>
ii. pageContext 对象的使用
  1. 协议:
  2. 服务器 ip:
  3. 服务器端口:
  4. 获取工程路径:
  5. 获取请求方法:
  6. 获取客户端 ip 地址:
  7. 获取会话的 id 编号:
  8. 获取jsp九大内置对象
<body>
	<%--
		request.getScheme() 它可以获取请求的协议
		request.getServerName() 获取请求的服务器 ip 或域名
		request.getServerPort() 获取请求的服务器端口号
		getContextPath() 获取当前工程路径
		request.getMethod() 获取请求的方式( GET 或 POST )
		request.getRemoteHost() 获取客户端的 ip 地址
		session.getId() 获取会话的唯一标识
	--%>
	<%
		pageContext.setAttribute("req", request);
	%>
	<%=request.getScheme()%>
	<br> 1.协议: ${ req.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>
iii. EL 表达式其他隐含对象的使用

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

示例代码:

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

请求地址:

1641707233675

示例代码:

输出请求头【User-Agent】的值:${ header['User-Agent'] } <br>
输出请求头【Connection】的值:${ header.Connection } <br>
输出请求头【User-Agent】的值:${ headerValues['User-Agent'][0] } <br>

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

示例代码:

获取 Cookie 的名称:${ cookie.JSESSIONID.name } <br>
获取 Cookie 的值:${ cookie.JSESSIONID.value } <br>

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

web.xml 中的配置:

<context-param>
	<param-name>username</param-name>
	<param-value>root</param-value>
</context-param>
<context-param>
	<param-name>url</param-name>
	<param-value>jdbc:mysql:///test</param-value>
</context-param>

示例代码:

输出&lt;Context-param&gt;username 的值:${ initParam.username } <br>
输出&lt;Context-param&gt;url 的值:${ initParam.url } <br>

2 、JSTL 标签库

JSTL 标签库 全称是指 JSP Standard Tag Library JSP 标准标签库。是一个不断完善的开放源代码的 JSP 标
签库。
EL 表达式主要是为了替换 jsp 中的表达式脚本,而JSTL标签库则是为了替换代码脚本。这样使得整个 jsp 页面变得更佳简洁

JSTL 由五个不同功能的标签库组成。

1641707749197

在 jsp 标签库中使用 taglib 指令引入标签库

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 指令引入标签库<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

g)core 核心库使用

i. <c:set />:set 标签可以往域中保存数据

ii.<c:if />:if标签用来做if判断,test属性表示判断的条件(使用EL表达式输出)

iii.<c:choose /> <c:when /> <c:otherwise />标签 :多路判断,跟switch ... case .... default非常接近

标签使用时需要注意的点:
​ 1、标签里不能使用html注释,要使用jsp注释
​ 2、when标签的父标签一定要是choose标签

<body>
    <%--
    i.<c:set />
        作用:set标签可以往域中保存数据

        域对象.setAttribute(key,value);
        scope 属性设置保存到哪个域
            page表示PageContext域(默认值)
            request表示Request域
            session表示Session域
            application表示ServletContext域
        var属性设置key是多少
        value属性设置值
    --%>
    保存之前:${ sessionScope.abc } <br>
    <c:set scope="session" var="abc" value="abcValue"/>
    保存之后:${ sessionScope.abc } <br>
    <hr>

    <%--
       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>
    <hr>

    <%--
    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>
        <%-- 这是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>

</body>

iv. <c:forEach />:遍历输出使用。

<body>
	<%--
    1.遍历1到10,输出
        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>
    --%>
	<hr>
	<%-- 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> --%>
	<hr>
    
    <%--3.遍历Map集合-----%>
    
	<%
		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>
	<hr>
    
	<%--4.遍历List集合---list中存放 Student类,有属性:编号,用户名,密码,年龄,电话信息--%>
	<%
		List<Student> studentList = new ArrayList<Student>();
		for (int i = 1; i <= 10; i++) {
			studentList.add(new Student(i, "username" + i, "pass" + i, 18 + i, "phone" + i));
		}
		request.setAttribute("stus", studentList);
	%>
	<form action="" enctype=""></form>
	<table>
		<tr>
			<th>编号</th>
			<th>用户名</th>
			<th>密码</th>
			<th>年龄</th>
			<th>电话</th>
			<th>操作</th>
		</tr>
		<%--
            items 表示遍历的集合
            var 表示遍历到的数据
            begin表示遍历的开始索引值
            end 表示结束的索引值
            step 属性表示遍历的步长值
            varStatus 属性表示当前遍历到的数据的状态
            for(int i = 1; i < 10; i+=2)
        --%>
		<c:forEach begin="2" end="7" step="2" varStatus="status"
			items="${requestScope.stus}" var="stu">
			<tr>
				<td>${stu.id}</td>
				<td>${stu.username}</td>
				<td>${stu.password}</td>
				<td>${stu.age}</td>
				<td>${stu.phone}</td>
				<td>${status.step}</td>
			</tr>
		</c:forEach>
	</table>
</body>

09(下)-文件的上传和下载

1 、文件的上传介绍(***** 重点

1、要有一个 form 标签,method=post 请求
2、form 标签的 encType 属性值必须为 multipart/form-data 值
3、在 form 标签中使用 input type=file 添加上传的文件
4、编写服务器代码(Servlet 程序)接收,处理上传的数据。

encType=multipart/form-data 表示提交的数据,以多段(每一个表单项一个数据段)的形式进行拼接,然后以二进制流的形式发送给服务器

1.1、 文件上传, 文件上传,HTTP 协议的说明。 协议的说明。

1641712227492

1.2 、commons-fileupload.jar 常用 API 介绍说明 介绍说明

commons-fileupload.jar 赖 需要依赖 commons-io.jar

第一步,就是需要导入两个 jar 包:
​ commons-fileupload-1.2.1.jar
​ commons-io-1.4.jar

commons-fileupload.jar 和 和 commons-io.jar 包中,我们常用的类有哪些类?

ServletFileUpload 类,用于解析上传的数据。
FileItem 类,表示每一个表单项。

boolean ServletFileUpload.isMultipartContent(HttpServletRequest request);
​ 判断当前上传的数据格式是否是多段的格式。

public List parseRequest(HttpServletRequest request)
​ 解析上传的数据

boolean FileItem.isFormField()
​ 判断当前这个表单项,是否是普通的表单项。还是上传的文件类型。
​ true 表示普通类型的表单项
​ false 表示上传的文件类型

String FileItem.getFieldName()
​ 获取表单项的 name 属性值

String FileItem.getString()
​ 获取当前表单项的值。

String FileItem.getName();
​ 获取上传的文件名

void FileItem.write( file );
​ 将上传的文件写到 参数 file 所指向抽硬盘位置 。

1.3 、fileupload 类库的使用

上传文件的表单:

<form action="http://192.168.31.74:8080/09_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>

解析上传的数据的代码:

@Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        //1 先判断上传的数据是否多段数据(只有是多段的数据,才是文件上传的)
        if (ServletFileUpload.isMultipartContent(req)) {
//           创建FileItemFactory工厂实现类
            FileItemFactory fileItemFactory = new DiskFileItemFactory();
            // 创建用于解析上传数据的工具类ServletFileUpload类
            ServletFileUpload servletFileUpload = new ServletFileUpload(fileItemFactory);
            try {
                // 解析上传的数据,得到每一个表单项FileItem
                List<FileItem> list = servletFileUpload.parseRequest(req);
                // 循环判断,每一个表单项,是普通类型,还是上传的文件
                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("e:\\" + fileItem.getName()));
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

2、文件下载

1641716646028

下载的常用 API 说明:
response.getOutputStream();
servletContext.getResourceAsStream();
servletContext.getMimeType();
response.setContentType();

public class Download extends HttpServlet {


    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        1、获取要下载的文件名
        String downloadFileName = "2.jpg";
//        2、读取要下载的文件内容 (通过ServletContext对象可以读取)
        ServletContext servletContext = getServletContext();
        // 获取要下载的文件类型
        String mimeType = servletContext.getMimeType("/file/" + downloadFileName);
        System.out.println("下载的文件类型:" + mimeType);
//        4、在回传前,通过响应头告诉客户端返回的数据类型
        resp.setContentType(mimeType);
//        5、还要告诉客户端收到的数据是用于下载使用(还是使用响应头)
        // Content-Disposition响应头,表示收到的数据怎么处理
        // attachment表示附件,表示下载使用
        // filename= 表示指定下载后的文件名
        // url编码是把汉字转换成为%xx%xx的格式
        if (req.getHeader("User-Agent").contains("Firefox")) {
            // 如果是火狐浏览器使用Base64编码
            resp.setHeader("Content-Disposition", "attachment; filename==?UTF-8?B?" + new BASE64Encoder().encode("中国.jpg".getBytes("UTF-8")) + "?=");
        } else {
            // 如果不是火狐,是IE或谷歌,使用URL编码操作
            resp.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode("中国.jpg", "UTF-8"));
        }
        /**
         * /斜杠被服务器解析表示地址为http://ip:prot/工程名/  映射 到代码的Web目录
         */
        InputStream resourceAsStream = servletContext.getResourceAsStream("/file/" + downloadFileName);
        // 获取响应的输出流
        OutputStream outputStream = resp.getOutputStream();
        //        3、把下载的文件内容回传给客户端
        // 读取输入流中全部的数据,复制给输出流,输出给客户端
        IOUtils.copy(resourceAsStream, outputStream);
    }
}

response.setHeader("Content-Disposition", "attachment; fileName=1.jpg");

这个响应头告诉浏览器。这是需要下载的。而 attachment 表示附件,也就是下载的一个文件。fileName=后面,表示下载的文件名

完成上面的两个步骤,下载文件是没问题了。但是如果我们要下载的文件是中文名的话。你会发现,下载无法正确

显示出正确的中文名。
原因是在响应头中,不能包含有中文字符,只能包含 ASCII 码。

附件中文名乱码问题解决方案:

方案一:URLEncoder 决 解决 IE 和谷歌浏览器的 和谷歌浏览器的 附件中文名问题。

如果客户端浏览器是 IE 浏览器 或者 是谷歌浏览器。我们需要使用 URLEncoder 类先对中文名进行 UTF-8 的编码操作。
因为 IE 浏览器和谷歌浏览器收到含有编码后的字符串后会以 UTF-8 字符集进行解码显示。

// 把中文名进行 UTF-8 编码操作。
String str = "attachment; fileName=" + URLEncoder.encode("中文.jpg", "UTF-8");
// 然后把编码后的字符串设置到响应头中
response.setHeader("Content-Disposition", str);

方案二: :BASE64 编解码 编解码 决 解决 火狐浏览器的附件中文名问题

如果客户端浏览器是火狐浏览器。 那么我们需要对中文名进行 BASE64 的编码操作。

这时候需要把请求头 Content-Disposition: attachment; filename=中文名
编码成为:Content-Disposition: attachment; filename==?charset?B?xxxxx?=

=?charset?B?xxxxx?= 现在我们对这段内容进行一下说明。

 =? 			表示编码内容的开始 
charset 		表示字符集 
B 				表示BASE64 编码 
xxxx 			表示文件名 BASE64 编码后的内容 
?= 				表示编码内容的结束

BASE64 编解码操作:

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 类进行编码操作

// 使用下面的格式进行 BASE64 编码后
String str = "attachment; fileName=" + "=?utf-8?B?"+ new BASE64Encoder().encode("中文.jpg".getBytes("utf-8")) + "?=";
// 设置到响应头中
response.setHeader("Content-Disposition", str)

那么我们如何解决上面两种不同编解码方式呢。我们只需要通过判断请求头中 User-Agent 这个请求头携带过来的 浏览器信息即可判断出是什么浏览器。

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)

10-书城项目第三阶段

1.第三阶段:

a) 页面 jsp 动态化

1、在 html 页面顶行添加 page 指令。
2、修改文件后缀名为:.jsp
3、使用 IDEA 搜索替换.html 为.jsp(快捷键:Ctrl+Shift+R)

1641719671168

b)抽取页面中相同的内容

  1. head.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
    String basePath = request.getScheme()
            + "://"
            + request.getServerName()
            + ":"
            + request.getServerPort()
            + request.getContextPath()
            + "/";
%>

<!--写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>
  1. footer.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<div id="bottom">
		<span>
			尚硅谷书城.Copyright &copy;2015
		</span>
</div>
  1. 登录成功后的菜单 login_success_menu.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<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>
  1. manager
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<div>
    <a href="book_manager.jsp">图书管理</a>
    <a href="order_manager.jsp">订单管理</a>
    <a href="../../index.jsp">返回商城</a>
</div>

c)登录,注册错误提示,及表单回显

以登录回显为示例:
Servlet 程序端需要添加回显信息到 Request 域中

1641720350320

jsp 页面,需要输出回显信息

1641720404023

d)BaseServlet 的抽取

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 程序。

代码优化一: 代码优化:并 合并 LoginServlet 和 和 RegistServlet 程序为 UserServlet

1641803100610

UserServlet 程序:

public class UserServlet extends BaseServlet {

    private UserService userService = new UserServiceImpl();

    /**
     * 处理登录的功能
     *
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    protected void login(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        //  1、获取请求的参数
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        // 调用 userService.login()登录处理业务
        User loginUser = userService.login(new User(null, username, password, null));
        // 如果等于null,说明登录 失败!
        if (loginUser == null) {
            // 把错误信息,和回显的表单项信息,保存到Request域中
            req.setAttribute("msg", "用户或密码错误!");
            req.setAttribute("username", username);
            //   跳回登录页面
            req.getRequestDispatcher("/pages/user/login.jsp").forward(req, resp);
        } else {
            // 登录 成功
            //跳到成功页面login_success.html
            req.getRequestDispatcher("/pages/user/login_success.jsp").forward(req, resp);
        }

    }

    /**
     * 处理注册的功能
     *
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    protected void regist(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        //  1、获取请求的参数
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String email = req.getParameter("email");
        String code = req.getParameter("code");

        //自定义工具栏,把Map中的参数封装到Bean中
        User user = WebUtils.copyParamToBean(req.getParameterMap(), new User());

//        2、检查 验证码是否正确  === 写死,要求验证码为:abcde
        if ("abcde".equalsIgnoreCase(code)) {
//        3、检查 用户名是否可用
            if (userService.existsUsername(username)) {
                System.out.println("用户名[" + username + "]已存在!");

                // 把回显信息,保存到Request域中
                req.setAttribute("msg", "用户名已存在!!");
                req.setAttribute("username", username);
                req.setAttribute("email", email);

//        跳回注册页面
                req.getRequestDispatcher("/pages/user/regist.jsp").forward(req, resp);
            } else {
                //      可用
//                调用Sservice保存到数据库
                userService.registUser(new User(null, username, password, email));
//
//        跳到注册成功页面 regist_success.jsp
                req.getRequestDispatcher("/pages/user/regist_success.jsp").forward(req, resp);
            }
        } else {
            // 把回显信息,保存到Request域中
            req.setAttribute("msg", "验证码错误!!");
            req.setAttribute("username", username);
            req.setAttribute("email", email);

            System.out.println("验证码[" + code + "]错误");
            req.getRequestDispatcher("/pages/user/regist.jsp").forward(req, resp);
        }

    }


}

还要给 login.jsp 添加隐藏域和修改请求地址

1641805291526

给 regist.jsp 页面添加隐藏域 action,和修改请求地址

1641805304844

优化代码二:使用反射优化大量 else if 代码:

    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 程序

1641805379323

BaseServlet 程序代码:

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();
        }
    }
}

修改 UserServlet 程序继承 BaseServlet 程序。

1641805459194

e) 数据的封装和抽取 BeanUtils 的使用

anUtils 工具类,它可以一次性的把所有请求的参数注入到 JavaBean 中。
BeanUtils 工具类,经常用于把 Map 中的值注入到 JavaBean 中,或者是对象属性值的拷贝操作。

BeanUtils 它不是 Jdk 的类。而是第三方的工具类。所以需要导包。
​ 1、导入需要的 jar 包:
​ commons-beanutils-1.8.0.jar
​ commons-logging-1.1.1.jar
​ 2、编写 WebUtils 工具类使用:

自定义WebUtils 工具类:

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;
    }

}

11-书城第四阶段 使用 EL 表达式修改表单回显

1641806318393

12- 书城项目第五阶段- 图书模块

MVC 概念

MVC 全称:Model 模型、 View 视图、 Controller 控制器。

MVC 最早出现在 JavaEE 三层中的 Web 层,它可以有效的指导 Web 层的代码如何有效分离,单独工作。

View 视图:只负责数据和界面的显示,不接受任何与显示数据无关的代码,便于程序员和美工的分工合作——JSP/HTML。

Controller 控制器:只负责接收请求,调用业务层的代码处理请求,然后派发页面,是一个“调度者”的角色——Servlet。转到某个页面。或者是重定向到某个页面。

Model 模型:用于封装与应用程序业务逻辑相关的数据以及对数据的处理方法。

MVC 是一种思想
MVC 的理念是将软件代码拆分成为组件,单独开发,组合使用( 目的还是为了降低耦合度)。

1641808370873

书城第五阶段

1、图书模块

1.1、编写图书模块的数据库表
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 id,name,author,price,sales,stock,img_path from t_book;
1.2 、编写图书模块的 JavaBean
public class Book {
    private Integer id;
    private String name;
    private String author;
    private BigDecimal price;
    private Integer sales;
    private Integer stock;
    private String imgPath = "static/img/default.jpg";
}
1.3 、编写图书模块的 Dao 和测试 Dao

bookDao 接口

public interface UserDao {

    /**
     * 根据用户名查询用户信息
     * @param username 用户名
     * @return 如果返回null,说明没有这个用户。反之亦然
     */
    public User queryUserByUsername(String username);

    /**
     * 根据 用户名和密码查询用户信息
     * @param username
     * @param password
     * @return 如果返回null,说明用户名或密码错误,反之亦然
     */
    public User queryUserByUsernameAndPassword(String username, String password);

    /**
     * 保存用户信息
     * @param user
     * @return 返回-1表示操作失败,其他是sql语句影响的行数
     */
    public int saveUser(User user);


}

BookDaoImpl 实现类:

public class BookDaoImpl extends BaseDao implements BookDao {
    @Override
    public int addBook(Book book) {

        String sql = "insert into t_book(`name`,`author`,`price`,`sales`,`stock`,`img_path`) values(?,?,?,?,?,?)";

        return update(sql, book.getName(),book.getAuthor(),book.getPrice(),book.getSales(),book.getStock(),book.getImgPath());

    }

    @Override
    public int deleteBookById(Integer id) {
        String sql = "delete from t_book where id = ?";
        return update(sql, id);
    }

    @Override
    public int updateBook(Book book) {
        String sql = "update t_book set `name`=?,`author`=?,`price`=?,`sales`=?,`stock`=?,`img_path`=? where id = ?";
        return update(sql,book.getName(),book.getAuthor(),book.getPrice(),book.getSales(),book.getStock(),book.getImgPath(),book.getId());
    }

    @Override
    public Book queryBookById(Integer id) {
        String sql = "select `id` , `name` , `author` , `price` , `sales` , `stock` , `img_path` imgPath from t_book where id = ?";
        return queryForOne(Book.class, sql,id);
    }

    @Override
    public List<Book> queryBooks() {
        String sql = "select `id` , `name` , `author` , `price` , `sales` , `stock` , `img_path` imgPath from t_book";
        return queryForList(Book.class, sql);
    }
}

BaseDao抽象类

public abstract class BaseDao {

    //使用DbUtils操作数据库
    private QueryRunner queryRunner = new QueryRunner();

    /**
     * update() 方法用来执行:Insert\Update\Delete语句
     *
     * @return 如果返回-1,说明执行失败<br/>返回其他表示影响的行数
     */
    public int update(String sql, Object... args) {
        Connection connection = JdbcUtils.getConnection();
        try {
            return queryRunner.update(connection, sql, args);
        } catch (SQLException e) {
            e.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 con = JdbcUtils.getConnection();
        try {
            return queryRunner.query(con, sql, new BeanHandler<T>(type), args);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JdbcUtils.close(con);
        }
        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 con = JdbcUtils.getConnection();
        try {
            return queryRunner.query(con, sql, new BeanListHandler<T>(type), args);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JdbcUtils.close(con);
        }
        return null;
    }

    /**
     * 执行返回一行一列的sql语句
     * @param sql   执行的sql语句
     * @param args  sql对应的参数值
     * @return
     */
    public Object queryForSingleValue(String sql, Object... args){

        Connection conn = JdbcUtils.getConnection();

        try {
            return queryRunner.query(conn, sql, new ScalarHandler(), args);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtils.close(conn);
        }
        return null;

    }

}

13、Cookie 和 和 Session

1、Cookie 翻译过来是饼干的意思。
2、Cookie 是服务器通知客户端保存键值对的一种技术
3、客户端有了 Cookie 后,每次请求都发送给服务器。
4、每个 Cookie 的大小不能超过 4kb

1642037899721

Servlet 程序中的代码:

    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[]

1642038046900

Cookie 的工具类:

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 程序中的代码:

    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");
        }
    }

d)Cookie 值的修改 值的修改

方案一:
​ 1、先创建一个要修改的同名(指的就是 key)的 Cookie 对象
​ 2、在构造器,同时赋于新的 Cookie 值。
​ 3、调用 response.addCookie( Cookie );

方案二:
​ 1、先查找到需要修改的 Cookie 对象
​ 2、调用 setValue()方法赋于新的 Cookie 值。
​ 3、调用 response.addCookie()通知客户端保存修改

    protected void updateCookie(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        方案一:
//        1、先创建一个要修改的同名的Cookie对象
//         2、在构造器,同时赋于新的Cookie值。
//        Cookie cookie = new Cookie("key1","newValue1");
//        3、调用response.addCookie( Cookie ); 通知 客户端 保存修改
//        resp.addCookie(cookie);

//        方案二:
//        1、先查找到需要修改的Cookie对象
        Cookie cookie = CookieUtils.findCookie("key2", req.getCookies());
        if (cookie != null) {
//            2、调用setValue()方法赋于新的Cookie值。
            cookie.setValue("newValue2");
//        3、调用response.addCookie()通知客户端保存修改
            resp.addCookie(cookie);
        }
        
        resp.getWriter().write("key1的Cookie已经修改好");

    }

谷歌浏览器如何查看 Cookie:

1642038549379

火狐浏览器如何查看 Cookie:

1642038615037

Cookie 的生命控制指的是如何管理 Cookie 什么时候被销毁(删除)

setMaxAge()
​ 正数,表示在指定的秒数后过期
​ 负数,表示浏览器一关,Cookie 就会被删除(默认值是-1)
​ 零,表示马上删除 Cookie

g)Cookie 有效路径 Path 的设置

Cookie 的 path 属性可以有效的过滤哪些 Cookie 可以发送给服务器。哪些不发
path 属性是通过请求的地址来进行有效的过滤。

CookieA path=/工程路径
CookieB path=/工程路径/abc

请求地址如下:
http://ip:port/工程路径/a.html
​ CookieA 发送
​ CookieB 不发送

http://ip:port/工程路径/abc/a.html
​ CookieA 发送
​ CookieB 发送

public class CookieServlet extends BaseServlet {


    protected void testPath(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        Cookie cookie = new Cookie("path1", "path1");
        // getContextPath() ===>>>>  得到工程路径
        System.out.println("getContextPath:" + req.getContextPath());
        cookie.setPath(req.getContextPath() + "/abc"); // ===>>>>  /工程路径/abc
        resp.addCookie(cookie);
        resp.getWriter().write("创建了一个带有Path路径的Cookie");
    }


    protected void updateCookie(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        方案一:
//        1、先创建一个要修改的同名的Cookie对象
//         2、在构造器,同时赋于新的Cookie值。
//        Cookie cookie = new Cookie("key1","newValue1");
//        3、调用response.addCookie( Cookie ); 通知 客户端 保存修改
//        resp.addCookie(cookie);

//        方案二:
//        1、先查找到需要修改的Cookie对象
        Cookie cookie = CookieUtils.findCookie("key2", req.getCookies());
        if (cookie != null) {
//            2、调用setValue()方法赋于新的Cookie值。
            cookie.setValue("newValue2");
//        3、调用response.addCookie()通知客户端保存修改
            resp.addCookie(cookie);
        }

        resp.getWriter().write("key1的Cookie已经修改好");

    }

    /**
     * 设置存活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);
    }

    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");
        }
    }

    protected void createCookie(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        resp.setCharacterEncoding("utf-8");    //设置 HttpServletResponse使用utf-8编码
//        resp.setHeader("Content-Type", "text/html;charset=utf-8");  //设置响应头的编码
        //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创建成功");
    }
}

1642044243930

login.jsp

<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>

LoginServlet程序

   @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 值。

SessionServlet.java

    protected void createOrGetSession(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 创建和获取Session会话对象
        HttpSession session = req.getSession();
        // 判断 当前Session会话,是否是新创建出来的
        boolean isNew = session.isNew();
        // 获取Session会话的唯一标识 id
        String id = session.getId();

        resp.getWriter().write("得到的Session,它的id是:" + id + " <br /> ");
        resp.getWriter().write("这个Session是否是新创建的:" + isNew + " <br /> ");
        
    }

k)Session 域数据的存取

SessionServlet.java

    /**
     * 往Session中保存数据
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    protected void setAttribute(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.getSession().setAttribute("key1", "value1");
        resp.getWriter().write("已经往Session中保存了数据");

    } 

/**
     * 获取Session域中的数据
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    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 分钟。

<session-config>
	<session-timeout>30</session-timeout>
</session-config>

如果说。你希望你的 web 工程,默认的 Session 的超时时长为其他时长。你可以在你自己的 web.xml 配置文件中做以上相同的配置。就可以修改你的 web 工程所有 Seession 的默认超时时长。

web.xml

    <!--表示当前web工程。创建出来 的所有Session默认是20分钟 超时时长-->
    <session-config>
        <session-timeout>20</session-timeout>
    </session-config>

如果你想只修改个别 Session 的超时时长。就可以使用上面的 API。setMaxInactiveInterval(int interval)来进行单独的设置。
session.setMaxInactiveInterval(int interval)单独设置超时时长。

Session 超时的概念介绍:

1642045269617

实例代码:

    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 马上被超时示例:

    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 技术来实现的。

1642045447966

3 、项目第六阶段

3.1 、登陆---显示用户名

UserServlet 程序中保存用户登录的信息

1642056643691

修改 login_succuess_menu.jsp

1642056682537

还要修改首页 index.jsp 页面的菜单 :

1642056736858

3.2 、登出---注销用户 注销用户

1、销毁 Session 中用户登录的信息(或者销毁 Session)
2、重定向到首页(或登录页面)。

UserServlet 程序中添加 logout 方法

    /**
     * 注销
     */
    protected void logout(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        1、销毁Session中用户登录的信息(或者销毁Session)
        req.getSession().invalidate();
//        2、重定向到首页(或登录页面)。
        resp.sendRedirect(req.getContextPath());
    }

修改【注销】的菜单地址

<a href="userServlet?action=logout">注销</a>

3.3 、表单重复提交之-----验证码

表单重复提交有三种常见的情况:
一:提交完表单。服务器使用请求转发进行页面跳转。这个时候,用户按下功能键 F5,就会发起最后一次的请求。造成表单重复提交问题。解决方法:使用重定向来进行跳转

二:用户正常提交服务器,但是由于网络延迟等原因,迟迟未收到服务器的响应,这个时候,用户以为提交失败,就会着急,然后多点了几次提交操作,也会造成表单重复提交。

三:用户正常提交服务器。服务器也没有延迟,但是提交完成后,用户回退浏览器。重新提交。也会造成表单重复提交。

1642059965407

3.4 、谷歌 kaptcha 图片验证码的使用

谷歌验证码 kaptcha 使用步骤如下:
​ 1、导入谷歌验证码的 jar 包
​ kaptcha-2.3.2.jar
​ 2、在 web.xml 中去配置用于生成验证码的 Servlet 程序

    <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 标签去显示验证码图片并使用它

<form action="http://localhost:8080/tmp/registServlet" method="get">
	用户名:<input type="text" name="username" > <br>
	验证码:<input type="text" style="width: 80px;" name="code">
	<img src="http://localhost:8080/tmp/kaptcha.jpg" alt="" style="width: 100px; height: 28px;"> <br>
	<input type="submit" value=" 登录">
</form>

4、在服务器获取谷歌生成的验证码和客户端发送过来的验证码比较使用。

public class RegistServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取 Session 中的验证码
        String token = (String) req.getSession().getAttribute(KAPTCHA_SESSION_KEY);
// 删除 Session 中的验证码
        req.getSession().removeAttribute(KAPTCHA_SESSION_KEY);
        String code = req.getParameter("code");
// 获取用户名
        String username = req.getParameter("username");
        if (token != null && token.equalsIgnoreCase(code)) {
            System.out.println(" 保存到数据库:" + username);
            resp.sendRedirect(req.getContextPath() + "/ok.jsp");
        } else {
            System.out.println(" 请不要重复提交表单");
        }
    }
}

切换验证码:

// 给验证码的图片,绑定单击事件
$("#code_img").click(function () {
// 在事件响应的 function 函数中有一个 this 对象。这个 this 对象,是当前正在响应事件的 dom 对象
// src 属性表示验证码 img 标签的 图片路径。它可读,可写
// alert(this.src);
this.src = "${basePath}kaptcha.jpg?d=" + new Date();
});

kaptcha参数

1642730558126

4、项目第七阶段

1、购物车

1.1、购物车模块分析

1642947822113

1.2、购物车模型编写

1.2.1 、购物车模型:

Cart.java

public class Cart {

    private Integer totalCount = 0;//总数量
    private BigDecimal totalPrice = new BigDecimal(0);//总价格
    private Map<Integer, CartItem> items = new LinkedHashMap<Integer, CartItem>();
    private static Cart cart;

    static {
        cart = new Cart();
    }

    public static Cart getInstance() {
        return cart;
    }

    public Cart() {
    }

    public Cart(Integer totalCount, BigDecimal totalPrice, Map<Integer, CartItem> items) {
        this.totalCount = totalCount;
        this.totalPrice = totalPrice;
        this.items = items;
    }

    public void setTotalCount(Map<Integer, CartItem> items) {
        totalCount = 0;
        for (Map.Entry<Integer, CartItem> entry : items.entrySet()) {
            totalCount += entry.getValue().getCount();
        }
    }


    public void setTotalPrice(Map<Integer, CartItem> items) {
        totalPrice = new BigDecimal(0);
        for (Map.Entry<Integer, CartItem> entry : items.entrySet()) {
            totalPrice = totalPrice.add(entry.getValue().getTotalPrice());
        }
    }

    public Map<Integer, CartItem> getItems() {
        return items;
    }

    public void setItems(Map<Integer, CartItem> items) {
        this.items = items;
    }

    // 先查看购物车中是否已经添加过此商品,如果已添加,则数量累加,总金额更新,如果没有添加过,直接放到集合中即可
    public void addItem(CartItem cartItem) {
        CartItem item = items.get(cartItem.getId());
        if (item == null) {
            items.put(cartItem.getId(), cartItem);
        } else {
            item.setCount(item.getCount() + cartItem.getCount()); //每一个cartItem数量累加
        }
        item = items.get(cartItem.getId());
        item.setTotalPrice(item.getPrice().multiply(new BigDecimal(item.getCount())));  //计算每一个cartItem总价格

        setTotalCount(items);
        setTotalPrice(items);
    }

    /**
     * 删除商品项
     */
    public void deleteItem(Integer id) {

        items.remove(id);
        setTotalPrice(items);
        setTotalCount(items);
    }

    /**
     * 清空购物车
     */
    public void clear() {
        items.clear();
        setTotalCount(items);
        setTotalPrice(items);
    }

    /**
     * 修改商品数量
     */
    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())));// 更新总金额

            cart.setTotalPrice(items);
            cart.setTotalCount(items);
        }
    }

    /**
     * 获取总数量
     */
    public Integer getTotalCount() {
//        for (Map.Entry<Integer, CartItem> entry : items.entrySet()) {
//            this.totalCount += entry.getValue().getCount();
//        }
        return totalCount;
    }

    /**
     * 获取总价格
     */
    public BigDecimal getTotalPrice() {
//        for (Map.Entry<Integer, CartItem> entry : items.entrySet()) {
//            totalPrice = totalPrice.add(entry.getValue().getTotalPrice());
//        }
        return totalPrice;
    }

    @Override
    public String toString() {
        return "Cart{" +
                "totalCount=" + totalCount +
                ", totalPrice=" + totalPrice +
                ", items=" + items +
                '}';
    }
}

CartItem.java

public class CartItem {
    private Integer id;
    private String name;
    private Integer count;
    private BigDecimal price;
    private BigDecimal totalPrice;

    public CartItem() {
    }

    public CartItem(Integer id, String name, Integer count, BigDecimal price) {
        this.id = id;
        this.name = name;
        this.count = count;
        this.price = price;
    }

    public CartItem(Integer id, String name, Integer count, BigDecimal price, BigDecimal totalPrice) {
        this(id, name, count, price);
        this.totalPrice = totalPrice;
    }

    @Override
    public String toString() {
        return "CartItem{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", count=" + count +
                ", price=" + price +
                ", totalPrice=" + totalPrice +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getCount() {
        return count;
    }

    public void setCount(Integer count) {
        this.count = count;
    }

    public BigDecimal getPrice() {
        return price;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }

    public BigDecimal getTotalPrice() {
        return totalPrice;
    }

    public void setTotalPrice(BigDecimal totalPrice) {
        this.totalPrice = totalPrice;
    }
}

1.2.2 、购物车的测试

CartTest.java

public class CartTest {
    Cart cart = Cart.getInstance();
    @Test
    public void addItem() {

        cart.addItem(new CartItem(1, "java ", 2, new BigDecimal(1000)));
        cart.addItem(new CartItem(2, "java 从入门到精通", 3, new BigDecimal(1000)));
        cart.addItem(new CartItem(3, " 数据结构与算法", 4, new BigDecimal(100)));

        System.out.println(cart);
    }

    @Test
    public void deleteItem(){
        cart.addItem(new CartItem(1, "java 从入门到精通", 2, new BigDecimal(1000)));
        cart.addItem(new CartItem(2, "java 从入门到精通", 3, new BigDecimal(1000)));
        cart.addItem(new CartItem(3, " 数据结构与算法", 4, new BigDecimal(100)));
        cart.deleteItem(1);
        System.out.println(cart);
    }

    @Test
    public void clear(){
        cart.addItem(new CartItem(1, "java 从入门到精通", 1, new BigDecimal(1000),new BigDecimal(1000)));
        cart.addItem(new CartItem(2, "java 从入门到精通", 1, new BigDecimal(1000),new BigDecimal(1000)));
        cart.addItem(new CartItem(3, " 数据结构与算法", 1, new BigDecimal(100),new BigDecimal(100)));
        cart.clear();
        System.out.println(cart);
    }

    @Test
    public void updateCount(){
        cart.addItem(new CartItem(1, "java 从入门到精通", 1, new BigDecimal(1000),new BigDecimal(1000)));
        cart.addItem(new CartItem(2, "java 从入门到精通", 1, new BigDecimal(1000),new BigDecimal(1000)));
        cart.addItem(new CartItem(3, " 数据结构与算法", 1, new BigDecimal(100),new BigDecimal(100)));
        cart.updateCount(1, 10);
        System.out.println(cart);
    }
}
1.3、加入购物车功能的实现

CartServlet.java

public class CartServlet extends BaseServlet {
    BookService bookService = new BookServiceImpl();

    /**
     * 加入购物车
     * @param req
     * @param resp
     */
    protected void addItem(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        // 获取请求的参数 商品编号
        int id = WebUtils.parseInt(req.getParameter("id"), 0);
        // 把图书信息,转换成为 CartItem 商品项
        Book book = bookService.queryBookById(id);
        // 调用 Cart.addItem(CartItem); 添加商品项
        CartItem cartItem = new CartItem(book.getId(), book.getName(), 1, book.getPrice());
        Cart cart = (Cart) req.getSession().getAttribute("cart");
        if (cart == null) {
            cart = new Cart();
            req.getSession().setAttribute("cart",cart);
        }
        cart.addItem(cartItem);
        req.getSession().setAttribute("lastName",book.getName());
        System.out.println(cart);
        System.out.println(" 请求头 Referer  的值:" + req.getHeader("Referer"));
        // 重定向回原来商品所在的地址页面
        resp.sendRedirect(req.getHeader("Referer"));
    }

    /*
    *删除商品
     */
    protected void deleteItem(HttpServletRequest req,HttpServletResponse resp) throws 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"));
        }
    }

    /*
    *清空购物车
     */
    protected void clear(HttpServletRequest req,HttpServletResponse resp) throws IOException {
        // 1 获取购物车对象
        Cart cart = (Cart) req.getSession().getAttribute("cart");
        if (cart != null){
            // 清空购物车
            cart.clear();
            // 重定向回原来购物车展示页面
            resp.sendRedirect(req.getHeader("Referer"));
        }
    }

    /*
    *修改商品数量
     */
    protected void updateCount(HttpServletRequest req,HttpServletResponse resp) throws IOException {
        int id = WebUtils.parseInt(req.getParameter("id"), 0);
        int count = WebUtils.parseInt(req.getParameter("count"), 1);
        Cart cart = (Cart) req.getSession().getAttribute("cart");
        if (cart != null){
            cart.updateCount(id,count);

            resp.sendRedirect(req.getHeader("Referer"));

        }
    }
}

index.jsp 页面 js 的代码:

1642948214196

    <script>
        $(function () {
            //// 给加入购物车按钮绑定单击事件
            $("button.addToCart").click(function () {
                /**
                 * 在事件响应的 function 函数 中,有一个 this 对象,这个 this 对象,是当前正在响应事件的 dom 对象
                 * @type {jQuery}
                 */
                let bookId = $(this).attr("bookId");
                location.href = "${pageScope.get("basePath")}" + "cartServlet?action=addItem&id=" + bookId;
            });
        });
    </script>

图解说明,如何跳回添加商品的页面:

1642948323160

1.4、购物车的展示

cart.jsp

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>购物车</title>

    <%-- 静态包含 base标签、css样式、jQuery文件 --%>
    <%@ include file="/pages/common/head.jsp" %>

    <script type="text/javascript">
        $(function () {
            // 给 【删除】绑定单击事件
            $("a.deleteItem").click(function () {
                return confirm("你确定要删除【" + $(this).parent().parent().find("td:first").text() + "】吗?")
            });
            $("#clearCart").click(function () {
                return confirm("你确定要清除购物车吗?")
            });
            $(".updateCount").change(function () {
                let name = $(this).parent().parent().find("td:first").text();
                let id = $(this).attr(`bookId`);
                let count = this.value;

                if (confirm(" 你确定要将【" + name + " 】商品修改数量为:" + count + "  吗?")) {
                    // 发起请求。给服务器保存修改
                    location.href = "${pageScope.basePath}cartServlet?action=updateCount&id=" + id + "&count=" + count
                } else {
                    // defaultValue 属性是表单项 Dom 对象的属性。它表示默认的 value 属性值。
                    this.value = this.defaultValue;
                }
            })
        })
    </script>
</head>
<body>

<div id="header">
    <img class="logo_img" alt="" src="static/img/logo.gif">
    <span class="wel_word">购物车</span>

    <%--静态包含,登录 成功之后的菜单 --%>
    <%@ include file="/pages/common/login_success_menu.jsp" %>


</div>

<div id="main">

    <table>
        <tr>
            <td>商品名称</td>
            <td>数量</td>
            <td>单价</td>
            <td>金额</td>
            <td>操作</td>
        </tr>
        <c:if test="${empty sessionScope.cart.items}">
            <tr>
                <td colspan="5">
                    <a href="index.jsp">亲,当前购物车为空!快跟小伙伴们去浏览商品吧!!!</a>
                </td>
            </tr>
        </c:if>
        <c:if test="${not empty sessionScope.cart.items}">
            <c:forEach items="${sessionScope.cart.items}" var="entry">
                <tr>
                    <td>${entry.value.name}</td>
                    <td><input class="updateCount" bookId="${entry.value.id}" type="text" value="${entry.value.count}"
                               style="width: 18%;text-align: center;"></td>
                    <td>${entry.value.price}</td>
                    <td>${entry.value.totalPrice}</td>
                    <td><a class="deleteItem" href="cartServlet?action=deleteItem&id=${entry.value.id}">删除</a></td>
                </tr>
            </c:forEach>
        </c:if>
    </table>

    <c:if test="${ not empty sessionScope.cart.items}">
        <div class="cart_info">
            <span class="cart_span">购物车中共有<span class="b_count">${sessionScope.cart.totalCount}</span>件商品</span>
            <span class="cart_span">总金额<span class="b_price">${sessionScope.cart.totalPrice}</span>元</span>
            <span class="cart_span"><a id="clearCart" href="cartServlet?action=clear">清空购物车</a></span>
            <span class="cart_span"><a href="orderServlet?action=createOrder">去结账</a></span>
        </div>
    </c:if>


</div>


<%--静态包含页脚内容--%>
<%@include file="/pages/common/footer.jsp" %>


</body>
</html>
1.5、删除购物车商品项

CartServlet 程序:

    /*
    *删除商品
     */
    protected void deleteItem(HttpServletRequest req,HttpServletResponse resp) throws 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 页面的代码:

删除的请求地址:

1642948503107

删除的确认提示操作:

    <script type="text/javascript">
        $(function () {
            // 给 【删除】绑定单击事件
            $("a.deleteItem").click(function () {
                return confirm("你确定要删除【" + $(this).parent().parent().find("td:first").text() + "】吗?")
            });
        //清空购物车
            $("#clearCart").click(function () {
                return confirm("你确定要清除购物车吗?")
            });
            $(".updateCount").change(function () {
                let name = $(this).parent().parent().find("td:first").text();
                let id = $(this).attr(`bookId`);
                let count = this.value;

                if (confirm(" 你确定要将【" + name + " 】商品修改数量为:" + count + "  吗?")) {
                    // 发起请求。给服务器保存修改
                    location.href = "${pageScope.basePath}cartServlet?action=updateCount&id=" + id + "&count=" + count
                } else {
                    // defaultValue 属性是表单项 Dom 对象的属性。它表示默认的 value 属性值。
                    this.value = this.defaultValue;
                }
            })
        })
    </script>
1.6、清空购物车

CartServlet程序

    /*
    *清空购物车
     */
    protected void clear(HttpServletRequest req,HttpServletResponse resp) throws IOException {
        // 1 获取购物车对象
        Cart cart = (Cart) req.getSession().getAttribute("cart");
        if (cart != null){
            // 清空购物车
            cart.clear();
            // 重定向回原来购物车展示页面
            resp.sendRedirect(req.getHeader("Referer"));
        }
    }

cart.jsp 页面的内容
给清空购物车添加请求地址:

1642948815901

清空的确认提示操作:

// 给清空购物车绑定单击事件
$("#clearCart").click(function () {
     return confirm("你确定要清除购物车吗?")
});
1.7、修改购物车商品数量

CartServlet程序

    /*
    *修改商品数量
     */
    protected void updateCount(HttpServletRequest req,HttpServletResponse resp) throws IOException {
        int id = WebUtils.parseInt(req.getParameter("id"), 0);
        int count = WebUtils.parseInt(req.getParameter("count"), 1);
        Cart cart = (Cart) req.getSession().getAttribute("cart");
        if (cart != null){
            cart.updateCount(id,count);

            resp.sendRedirect(req.getHeader("Referer"));

        }
    }

修改 pages/cart/cart.jsp 购物车页面:

1642948929846

修改商品数量 js 代码:

// 给输入框绑定 onchange 内容发生改变事件            
$(".updateCount").change(function () {
                let name = $(this).parent().parent().find("td:first").text();
                let id = $(this).attr(`bookId`);
                let count = this.value;

                if (confirm(" 你确定要将【" + name + " 】商品修改数量为:" + count + "  吗?")) {
                    // 发起请求。给服务器保存修改
                    location.href = "${pageScope.basePath}cartServlet?action=updateCount&id=" + id + "&count=" + count
                } else {
                    // defaultValue 属性是表单项 Dom 对象的属性。它表示默认的 value 属性值。
                    this.value = this.defaultValue;
                }
            })
1.8、首页,购物车数据回显

在添加商品到购物车的时候,保存最后一个添加的商品名称:

1642949012235

在 pages/client/index.jsp 页面中输出购物车信息:

<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、订单模块的分析:

1642949110488

2.2:订单模块的实现 :

2.2.1、创建订单模块的数据库表

use book;
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 、创建订单模块的数据模型

Order.java

public class Order {
    private String orderId; //订单号
    private Date createTime;    //日期
    private BigDecimal price;   //价格
    private Integer status = 0;     //状态  0 未发货, 1 已发货, 2 表示已签收
    private Integer userId;     //用户Id

    public Order() {
    }

    public Order(String orderId, Date createTime, BigDecimal price, Integer status, Integer userId) {
        this.orderId = orderId;
        this.createTime = createTime;
        this.price = price;
        this.status = status;
        this.userId = userId;
    }

    @Override
    public String toString() {
        return "Order{" +
                "orderId='" + orderId + '\'' +
                ", createTime=" + createTime +
                ", price=" + price +
                ", status=" + status +
                ", userId=" + userId +
                '}';
    }

    public String getOrderId() {
        return orderId;
    }

    public void setOrderId(String orderId) {
        this.orderId = orderId;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    public BigDecimal getPrice() {
        return price;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }
}

OrderItem.java

public class OrderItem {
    private Integer id;
    private String name;
    private Integer count;
    private BigDecimal price;
    private BigDecimal totalPrice;
    private String orderId;

    public OrderItem() {
    }

    public OrderItem(Integer id, String name, Integer count, BigDecimal price, BigDecimal totalPrice, String orderId) {
        this.id = id;
        this.name = name;
        this.count = count;
        this.price = price;
        this.totalPrice = totalPrice;
        this.orderId = orderId;
    }

    @Override
    public String toString() {
        return "OrderItem{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", count=" + count +
                ", price=" + price +
                ", totalPrice=" + totalPrice +
                ", orderId='" + orderId + '\'' +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getCount() {
        return count;
    }

    public void setCount(Integer count) {
        this.count = count;
    }

    public BigDecimal getPrice() {
        return price;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }

    public BigDecimal getTotalPrice() {
        return totalPrice;
    }

    public void setTotalPrice(BigDecimal totalPrice) {
        this.totalPrice = totalPrice;
    }

    public String getOrderId() {
        return orderId;
    }

    public void setOrderId(String orderId) {
        this.orderId = orderId;
    }
}

2.2.3 、编写订单模块的 Dao 程序和测试

OrderDao接口

public interface OrderDao {
    //保存订单
    public int saveOrder(Order order);

    //根据用户Id查看自己的订单
    public List<Order> queryOrdersByUserId(Integer userId);
}

OrderDao 实现

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.getUserId());
    }

    @Override
    public List<Order> queryOrdersByUserId(Integer userId) {
        String sql = "select order_id as orderId ,create_time as createTime ,price,status,user_id as userId from t_order where user_id = ?";
        List<Order> orders = queryForList(Order.class, sql, userId);
        return orders;
    }
}

测试

public class OrderDaoTest {
    
	@Test
	public void saveOrder() {
		OrderDao orderDao = new OrderDaoImpl();
		orderDao.saveOrder(new Order("1234567891",new Date(),new BigDecimal(100),0, 1));
	}
}


public class OrderItemDaoTest {
	@Test
	public void saveOrderItem() {
		OrderItemDao orderItemDao = new OrderItemDaoImpl();
		orderItemDao.saveOrderItem(new OrderItem(null,"java  从入门到精通", 1,new BigDecimal(100),new BigDecimal(100),"1234567890"));
		orderItemDao.saveOrderItem(new OrderItem(null,"javaScript  从入门到精通", 2,new BigDecimal(100),new BigDecimal(200),"1234567890"));
		orderItemDao.saveOrderItem(new OrderItem(null,"Netty  入门", 1,new BigDecimal(100),new BigDecimal(100),"1234567890"));
}
}

2.2.4 、编写订单模块的 Service 和测试

OrderService 接口

public interface OrderService {
    public String createOrder(Cart cart,Integer userId);
    public List<Order> showMyOrders(Integer userId);

    List<OrderItem> queryOrderItemsByOrderId(String orderId);
}

OrderService 实现类

public class OrderServiceImpl implements OrderService {
    OrderDao orderDao = new OrderDaoImpl();
    OrderItemDao orderItemDao = new OrderItemDaoImpl();
    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;
    }

    @Override
    public List<Order> showMyOrders(Integer userId) {
        List<Order> orders = orderDao.queryOrdersByUserId(userId);
        return orders;
    }

    //根据订单号查询详情
    @Override
    public List<OrderItem> queryOrderItemsByOrderId(String orderId) {
        List<OrderItem> orderItems = orderItemDao.queryOrderItemsByOrderId(orderId);
        return orderItems;
    }
}

测试

public class OrderServiceTest {
    OrderService orderService = new OrderServiceImpl();

    @Test
    public void createOrder() {
        Cart cart = new Cart();
        Integer userId = 1;
        cart.addItem(new CartItem(1, "java 从入门到精通", 1, new BigDecimal(1000), new BigDecimal(1000)));
        cart.addItem(new CartItem(2, "java 从入门到精通", 1, new BigDecimal(1000), new BigDecimal(1000)));
        cart.addItem(new CartItem(3, " 数据结构与算法", 1, new BigDecimal(100), new BigDecimal(100)));

        String orderId = orderService.createOrder(cart, userId);
        System.out.println("订单号是:"+orderId);

    }
}

2.2.5 、编写订单模块的 web 层和页面联调

修改 OrderService 程序:

public class OrderServiceImpl implements OrderService {
    OrderDao orderDao = new OrderDaoImpl();
    OrderItemDao orderItemDao = new OrderItemDaoImpl();
    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;
    }

    @Override
    public List<Order> showMyOrders(Integer userId) {
        List<Order> orders = orderDao.queryOrdersByUserId(userId);
        return orders;
    }

    //根据订单号查询详情
    @Override
    public List<OrderItem> queryOrderItemsByOrderId(String orderId) {
        List<OrderItem> orderItems = orderItemDao.queryOrderItemsByOrderId(orderId);
        return orderItems;
    }
}

OrderServlet 程序:

public class OrderServlet extends BaseServlet {

    private OrderService orderService = new OrderServiceImpl();

    //创建订单
    protected void createOrder(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
        Cart cart = (Cart) req.getSession().getAttribute("cart");
        if (cart == null) {
            resp.sendRedirect(req.getHeader("Referer"));
        }
        User loginUser = (User) req.getSession().getAttribute("user");
        if (loginUser == null) {
            req.getRequestDispatcher("/pages/user/login.jsp").forward(req, resp);
            return;
        }
        Integer userId = loginUser.getId();
        String orderId = orderService.createOrder(cart, userId);
        req.getSession().setAttribute("orderId", orderId);
        resp.sendRedirect(req.getContextPath() + "/pages/cart/checkout.jsp");
    }

    //查看订单
    protected void showMyOrders(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        User user = (User) req.getSession().getAttribute("user");
        if (user == null) {
            resp.sendRedirect(req.getContextPath() + "/pages/user/login.jsp");
            return;
        }
        Integer id = user.getId();
        List<Order> orders = orderService.showMyOrders(id);
        req.getSession().setAttribute("orders", orders);
        resp.sendRedirect(req.getContextPath() + "/pages/order/order.jsp");
    }

    //查看详情
    protected void showOrderDetail(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        String orderId = req.getParameter("orderId");
        List<OrderItem> orderItems = orderService.queryOrderItemsByOrderId(orderId);
        req.getSession().setAttribute("orderItems",orderItems);
        resp.sendRedirect(req.getContextPath() + "/pages/order/orderDetails.jsp");
    }
}

修改 pages/cart/cart.jsp 页面,结账的请求地址:

1642949860320

修改 pages/cart/checkout.jsp 页面,输出订单号:

1642949877583

14.过滤器

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 中否包含有用户登录的信息即可!!!

<%
	Object user = session.getAttribute("user");
	// 如果等于 null ,说明还没有登录
	if (user == null) {
		request.getRequestDispatcher("/login.jsp").forward(request,response);
		return;
	}
%>

1644324924161

Filter的代码

public class AdminFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    /**
     * doFilter 方法,专门用于拦截请求。可以做权限检查
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;

        Object user = httpServletRequest.getSession().getAttribute("user");
            // 如果等于 null ,说明还没有登录
        if (user == null) {
            httpServletRequest.getRequestDispatcher("/pages/user/login.jsp").forward(servletRequest, servletResponse);
        } else {
            // 让程序继续往下访问用户的目标资源
            filterChain.doFilter(servletRequest, servletResponse);
        }
    }

    @Override
    public void destroy() {

    }
}

web.xml 中的配置:

<!--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 的拦截路径

完整的用户登录

login.jsp 页面 == 登录表单

这是登录页面。login.jsp 页面 <br>
<form action="http://localhost:8080/15_filter/loginServlet" method="get">
	用户名:<input type="text" name="username"/> <br>
	密 码:<input type="password" name="password"/> <br>
	<input type="submit" />
</form>

LoginServlet 程序

public class LoginServlet extends HttpServlet {

    private UserService userService = new UserServiceImpl();

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //  1、获取请求的参数
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        // 调用 userService.login()登录处理业务
        User loginUser = userService.login(new User(null, username, password, null));
        // 如果等于null,说明登录 失败!
        if (loginUser == null) {
            // 把错误信息,和回显的表单项信息,保存到Request域中
            req.setAttribute("msg","用户或密码错误!");
            req.setAttribute("username", username);
            //   跳回登录页面
            req.getRequestDispatcher("/pages/user/login.jsp").forward(req, resp);
        } else {
            // 登录 成功
            //跳到成功页面login_success.html
            req.getRequestDispatcher("/pages/user/login_success.jsp").forward(req, resp);
        }
    }
}

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 对象

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>

java 代码:

@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());
}

5 、FilterChain 过滤器链

Filter ----->过滤器
Chain ----->链,链条
FilterChain ----->就是过滤器链(多个过滤器如何一起工作)

1644326358402

6 、Filter 的拦截路径

精确匹配 精确匹配

<url-pattern>/target.jsp</url-pattern>

以上配置的路径,表示请求地址必须为:http://ip:port/工程路径/target.jsp

目录匹配

<url-pattern>/*</url-pattern>

拦截所有请求

<url-pattern>/admin/*</url-pattern>

以上配置的路径,表示请求地址必须为:http://ip:port/工程路径/admin/*

后缀名匹配

<url-pattern>*.html</url-pattern>

以上配置的路径,表示请求地址必须以.html 结尾才会拦截到

<url-pattern>*.do</url-pattern>

以上配置的路径,表示请求地址必须以.do 结尾才会拦截到

<url-pattern>*.action</url-pattern>

以上配置的路径,表示请求地址必须以.action 结尾才会拦截到

注:Filter 过滤器它只关心请求的地址是否匹配,不关心请求的资源是否存在!!!

7 、书城第八阶段

1 、使用 Filter 过滤器拦截/pages/manager/所有内容,实现权限检查

public class ManagerFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    /**
     * doFilter 方法,专门用于拦截请求。可以做权限检查
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;

        Object user = httpServletRequest.getSession().getAttribute("user");
            // 如果等于 null ,说明还没有登录
        if (user == null) {
            httpServletRequest.getRequestDispatcher("/pages/user/login.jsp").forward(servletRequest, servletResponse);
        } else {
            // 让程序继续往下访问用户的目标资源
            filterChain.doFilter(servletRequest, servletResponse);
        }
    }

    @Override
    public void destroy() {

    }
}

web.xml 中的配置:

<filter>
	<filter-name>ManagerFilter</filter-name>
	<filter-class>com.atguigu.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 虚拟自动释放。

测试类:

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() {
            String name = Thread.currentThread().getName();
            System.out.println("在线程[" + name + "]保存的值是:bbj");
            threadLocal.set("bbj");

            /*
            // 在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();
//        }

        Map<String, Object> map = new HashMap<>();
        System.out.println(map.get("key"));

        System.out.println(threadLocal.get()); // 不保存,也想取一个有效的值。怎么办,需要你事务准备这个值。
    }
}

3 、使用 Filter 和 和 ThreadLocal 组合管理事务

3.1 、使用 ThreadLocal 来确保所有 dao 操作都在同一个 Connection 连接对象中完成

原理分析图:

1644366462096

JdbcUtils 工具类的修改

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 (Exception e) {
                e.printStackTrace();
            }
        }
        return conn;
    }

    /**
     * 提交事务,并关闭释放连接
     */
    public static void commitAndClose() {
        Connection connection = conns.get();
        // 如果不等于null,说明 之前使用过连接,操作过数据库
        if (connection != 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

/**
 * @program:
 * @description: 封装常用的增删改查方法, 供其他类继承,然后调用方法即可
 * @author: CGQ
 * @create: 2022-01-11 11:43
 **/
abstract class BaseDao {
    //使用DbUtils操作数据库
    private QueryRunner queryRunner = new QueryRunner();

    /**
     * update() 方法用来执行:Insert\Update\Delete语句
     *
     * @return 如果返回-1,说明执行失败<br/>返回其他表示影响的行数
     */
    public int update(String sql, Object... args) {
        Connection connection = JdbcUtils.getConnection();
        try {
            return queryRunner.update(connection, sql, args);
        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException(e);   //手动抛出异常,供过滤器处理
        } 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 con = JdbcUtils.getConnection();
        try {
            return queryRunner.query(con, sql, new BeanHandler<T>(type), args);
        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException(e);  //手动抛出异常,供过滤器处理
        } finally {
//            JdbcUtils.close(con);
//            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 con = JdbcUtils.getConnection();
        try {
            return queryRunner.query(con, sql, new BeanListHandler<T>(type), args);
        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException(e);  //手动抛出异常,供过滤器处理
        } finally {
//            JdbcUtils.close(con);
        }
    }

    /**
     * 执行返回一行一列的sql语句
     *
     * @param sql  执行的sql语句
     * @param args sql对应的参数值
     * @return
     */
    public Object queryForSingleValue(String sql, Object... args) {

        Connection conn = JdbcUtils.getConnection();

        try {
            return queryRunner.query(conn, sql, new ScalarHandler(), args);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);  //手动抛出异常,供过滤器处理
        } finally {
//            JdbcUtils.close(conn);
        }
    }
}
3.2 、使用 Filter 过滤器统一给所有的 Service 方法都加上 try-catch 。来进行实现的管理

原理分析图:

1644375676059

Filter 类代码:

public class TransactionFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @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();
            throw new RuntimeException(e);
        }
    }

    @Override
    public void destroy() {

    }
}

在 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 过滤器

1644375900559

/**
 * @description:
 * @author: CGQ
 * @create: 2022-01-11 15:38
 **/
public class BaseServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println(this);

        // 解决post请求中文乱码问题
        // 一定要在获取请求参数之前调用才有效
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=utf-8");
        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();
            throw new RuntimeException(e);// 把异常抛给 Filter 过滤器
        }
    }
}
3.3 、将所有异常都统一交给 Tomcat ,让 Tomcat展示友好的错误信息页面。

在 web.xml 中我们可以通过错误页面配置来进行管理。

    <!--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>

error500.jsp页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <%@include file="/pages/common/head.jsp" %>
</head>
<body>
很抱歉,您访问的后台程序出现了错误,程序猿小哥,正在努力的为您抢修!!! <br>
<a href="index.jsp">返回首页</a>
</body>
</html>

error404.jsp页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <%@include file="/pages/common/head.jsp" %>
</head>

<body>
很抱歉。您访问的页面不存在,或已经被删除!!! <br>
<a href="index.jsp">返回首页</a>
</body>
</html>

15、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 定义示例:

<script type="text/javascript">
        // json的定义
        var jsonObj = {
            "key1": 12,
            "key2": "abc",
            "key3": true,
            "key4": [11, "arr", false],
            "key5": {
                "key5_1": 551,
                "key5_2": "key5_2_value"
            },
            "key6": [{
                "key6_1_1": 6611,
                "key6_1_2": "key6_1_2_value"
            }, {
                "key6_2_1": 6621,
                "key6_2_2": "key6_2_2_value"
            }]
        };

    </script>
1.1.2 、json 的访问

json 本身是一个对象。

json 中的 key 我们可以理解为是对象中的一个属性。
json 中的 key 访问就跟访问对象的属性一样: json 对象.key

json 访问示例:

alert(typeof(jsonObj));// object json 就是一个对象
alert(jsonObj.key1); //12
alert(jsonObj.key2); // abc
alert(jsonObj.key3); // true
alert(jsonObj.key4);// 得到数组 [11,"arr",false]
// json 中 数组值的遍历
for(var i = 0; i < jsonObj.key4.length; i++) {
alert(jsonObj.key4[i]);
}
alert(jsonObj.key5.key5_1);//551
alert(jsonObj.key5.key5_2);//key5_2_value
alert( jsonObj.key6 );// 得到 json 数组
// 取出来每一个元素都是 json 对象
var jsonItem = jsonObj.key6[0];
// alert( jsonItem.key6_1_1 ); //6611
alert( jsonItem.key6_1_2 ); //key6_1_2_value
1.1.3 、json 的两个常用方法

json 的存在有两种形式。
一种是:对象的形式存在,我们叫它 json 对象
一种是:字符串的形式存在,我们叫它 json 字符串
一般我们要操作 json 中的数据的时候,需要 json 对象的格式。
一般我们要在客户端和服务器之间进行数据交换的时候,使用 json 字符串

JSON.stringify() 把 json 对象转换成为 json 字符串
JSON.parse() 把 json 字符串转换成为 json 对象

示例代码:

// 把 json 对象转换成为 json 字符串
var jsonObjString = JSON.stringify(jsonObj); // 特别像 Java 中对象的 toString
alert(jsonObjString)
// 把 json 字符串。转换成为 json 对象
var jsonObj2 = JSON.parse(jsonObjString);
alert(jsonObj2.key1);// 12
alert(jsonObj2.key2);// abc

1.2 、JSON 在 在 java 中的使用

1.2.1 、javaBean 和 和 json 的互转
    // 1.2.1、javaBean和json的互转
    @Test
    public void test1() {
        Person person = new Person(1, "国哥好帅!");
        // 创建Gson对象实例
        Gson gson = new Gson();
        // toJson方法可以把java对象转换成为json字符串
        String personJsonString = gson.toJson(person);
        System.out.println(personJsonString);
        // fromJson把json字符串转换回Java对象
        // 第一个参数是json字符串
        // 第二个参数是转换回去的Java对象类型
        Person person1 = gson.fromJson(personJsonString, Person.class);
        System.out.println(person1);
    }
1.2.2 、List 和 和 json 的互转

注:json字符串转集合(List、Map)时,需要继承TypeToken类

    //1.2.2、List 和json的互转

public class PersonListType extends TypeToken<ArrayList<Person>> {
}

    @Test
    public void test2() {
        List<Person> personList = new ArrayList<>();

        personList.add(new Person(1, "国哥"));
        personList.add(new Person(2, "康师傅"));

        Gson gson = new Gson();

        // 把List转换为json字符串
        String personListJsonString = gson.toJson(personList);
        System.out.println(personListJsonString);

        List<Person> list = gson.fromJson(personListJsonString, new PersonListType().getType());//PersonListType类需要继承TypeToken类
        System.out.println(list);
        Person person = list.get(0);
        System.out.println(person);
    }
1.2.3 、map 和 和 json 的互转
    //1.2.3、map 和json的互转
	public class PersonMapType extends TypeToken<HashMap<Integer, Person>> {}
    

@Test
    public void test3() {
        Map<Integer, Person> personMap = new HashMap<>();

        personMap.put(1, new Person(1, "国哥好帅"));
        personMap.put(2, new Person(2, "康师傅也好帅"));

        Gson gson = new Gson();
        // 把 map 集合转换成为 json字符串
        String personMapJsonString = gson.toJson(personMap);
        System.out.println(personMapJsonString);

 //方式一:
//        Map<Integer,Person> personMap2 = gson.fromJson(personMapJsonString, new PersonMapType().getType());
        
 //方式二:匿名内部类
        Map<Integer, Person> personMap2 = gson.fromJson(personMapJsonString, new TypeToken<HashMap<Integer, Person>>() {
        }.getType());

        System.out.println(personMap2);
        Person p = personMap2.get(1);
        System.out.println(p);

    }

2 、AJAX 请求

2.1 、什么是 AJAX 请求 请求

AJAX 即“Asynchronous Javascript And XML”(异步 JavaScript 和 XML),是指一种创建交互式网页应用的网页开发技术

ajax 是一种浏览器通过 js 异步发起请求,局部更新页面的技术。

Ajax 请求的局部更新,浏览器地址栏不会发生变化

局部更新不会舍弃原来页面的内容

2.2 、原生 AJAX 请求的示例(了解即可):

AjaxServlet.java:

供以下ajax方法参考

public class AjaxServlet extends BaseServlet {

    protected void javaScriptAjax(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("Ajax请求过来了");
        Person person = new Person(1, "国哥");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // json格式的字符串
        Gson gson = new Gson();
        String personJsonString = gson.toJson(person);

        resp.getWriter().write(personJsonString);
    }

    protected void jQueryAjax(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("  jQueryAjax == 方法调用了");
        Person person = new Person(1, "国哥");
        // json格式的字符串
        Gson gson = new Gson();
        String personJsonString = gson.toJson(person);

        resp.getWriter().write(personJsonString);
    }

    protected void jQueryGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("  jQueryGet  == 方法调用了");
        Person person = new Person(1, "国哥");
        // json格式的字符串
        Gson gson = new Gson();
        String personJsonString = gson.toJson(person);

        resp.getWriter().write(personJsonString);
    }

    protected void jQueryPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("  jQueryPost   == 方法调用了");
        Person person = new Person(1, "国哥");
        // json格式的字符串
        Gson gson = new Gson();
        String personJsonString = gson.toJson(person);

        resp.getWriter().write(personJsonString);
    }
    
    protected void jQueryGetJSON(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("  jQueryGetJSON   == 方法调用了");
        Person person = new Person(1, "国哥");
        // json格式的字符串
        Gson gson = new Gson();
        String personJsonString = gson.toJson(person);

        resp.getWriter().write(personJsonString);
    }

    protected void jQuerySerialize(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("  jQuerySerialize   == 方法调用了");

        System.out.println("用户名:" + req.getParameter("username"));
        System.out.println("密码:" + req.getParameter("password"));

        Person person = new Person(1, "国哥");
        // json格式的字符串
        Gson gson = new Gson();
        String personJsonString = gson.toJson(person);

        resp.getWriter().write(personJsonString);
    }
}

ajax.html

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="pragma" content="no-cache"/>
    <meta http-equiv="cache-control" content="no-cache"/>
    <meta http-equiv="Expires" content="0"/>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
    <script type="text/javascript">
        // 在这里使用javaScript语言发起Ajax请求,访问服务器AjaxServlet中javaScriptAjax
        function ajaxRequest() {
// 				1、我们首先要创建XMLHttpRequest 
            var xmlhttprequest = new XMLHttpRequest();
// 				2、调用open方法设置请求参数
            xmlhttprequest.open("GET", "http://localhost:8080/16_json_ajax_i18n/ajaxServlet?action=javaScriptAjax", true);
// 				4、在send方法前绑定onreadystatechange事件,处理请求完成后的操作。
            xmlhttprequest.onreadystatechange = function () {
                if (xmlhttprequest.readyState == 4 && xmlhttprequest.status == 200) {
                    alert("收到服务器返回的数据:" + xmlhttprequest.responseText);
                    var jsonObj = JSON.parse(xmlhttprequest.responseText);
                    // 把响应的数据显示在页面上
                    document.getElementById("div01").innerHTML = "编号:" + jsonObj.id + " , 姓名:" + jsonObj.name;
                }
            }
// 				3、调用send方法发送请求
            xmlhttprequest.send();


            alert("我是最后一行的代码");

        }
    </script>
</head>
<body>
<!--		<a href="http://localhost:8080/16_json_ajax_i18n/ajaxServlet?action=javaScriptAjax">非Ajax</a>-->
<button onclick="ajaxRequest()">ajax request</button>
<button onclick="ajaxRequest()">ajax request</button>
<button onclick="ajaxRequest()">ajax request</button>
<button onclick="ajaxRequest()">ajax request</button>
<button onclick="ajaxRequest()">ajax request</button>
<div id="div01">
</div>
<table border="1">
    <tr>
        <td>1.1</td>
        <td>1.2</td>
    </tr>
    <tr>
        <td>2.1</td>
        <td>2.2</td>
    </tr>
</table>
</body>
</html>

2.3 、jQuery 中的 AJAX 请求

1644393455508

 // ajax请求
	$("#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(" ajax 编号:" + data.id + " , 姓名:" + data.name);
            },
            dataType: "json"
        });
    });

$.get 方法和$.post 方法
url 请求的 url 地址
data 发送的数据
callback 成功的回调函数
dataType 返回的数据类型

		// 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请求
			$.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 成功的回调函数

// 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 的形式进行拼接。

		// 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 验证用户名是否可用

1644394306597

UserServlet 程序中 ajaxExistsUsername 方法:

    protected void ajaxExistsUsername(HttpServletRequest req, HttpServletResponse resp) throws 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 页面中的代码:

$("#username").blur(function () {
        let username = this.value;
        $.getJSON("<%=basePath%>userServlet", "action=ajaxExistsUsername&username=" + username, function (data) {
            if (data.existsUsername) {
                $("span.errorMsg").text("用户名已存在!");
            } else {
                $("span.errorMsg").text("用户名可用!");
            }
        });
    });

3.2 、使用 AJAX 修改把商品添加到购物车

1644398511796

CartServlet 程序:

    /**
     * 加入购物车
     * @param req
     * @param resp
     */
    protected void ajaxAddItem(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        // 获取请求的参数 商品编号
        int id = WebUtils.parseInt(req.getParameter("id"), 0);
        // 把图书信息,转换成为 CartItem 商品项
        Book book = bookService.queryBookById(id);
        // 调用 Cart.addItem(CartItem); 添加商品项
        CartItem cartItem = new CartItem(book.getId(), book.getName(), 1, book.getPrice());
        Cart cart = (Cart) req.getSession().getAttribute("cart");
        if (cart == null) {
            cart = new Cart();
            req.getSession().setAttribute("cart",cart);
        }
        cart.addItem(cartItem);
        req.getSession().setAttribute("lastName",book.getName());
        System.out.println(" 请求头 Referer  的值:" + req.getHeader("Referer"));

        //6 、返回购物车总的商品数量和最后一个添加的商品名称
        Map<String, Object> resultMap = new HashMap<>();

        resultMap.put("totalCount",cart.getTotalCount());
        resultMap.put("lastName",cartItem.getName());

        Gson gson = new Gson();
        String resultMapJsonString = gson.toJson(resultMap);
        resp.getWriter().write(resultMapJsonString);
    }

pages/client/index.jsp 页面

html 代码:

<div style="text-align: center">
    <c:if test="${empty sessionScope.cart.items}">
        <%-- 购物车为空的输出 --%>
        <span id="cartTotalCount"> </span>
        <div>
            <span style="color: red" id="cartLastName">当前购物车为空</span>
        </div>
    </c:if>
    <c:if test="${not empty sessionScope.cart.items}">
        <span id="cartTotalCount">您的购物车中有${sessionScope.cart.totalCount}件商品</span>
        <div>
            您刚刚将<span id="cartLastName" style="color: red">${sessionScope.lastName}</span>加入到了购物车中
        </div>
    </c:if>
</div>

javaScript代码:

<script>
        $(function () {
            // 给加入购物车按钮绑定单击事件
            $("button.addToCart").click(function () {
                /**
                 * 在事件响应的 function 函数 中,有一个 this 对象,这个 this 对象,是当前正在响应事件的 dom 对象
                 * @type {jQuery}
                 */
                let bookId = $(this).attr("bookId");
                $.getJSON("${pageScope.get('basePath')}cartServlet","action=ajaxAddItem&id="+bookId,function (data) {
                    $("#cartTotalCount").text("有 您的购物车中有 " + data.totalCount + "  件商品");
                    $("#cartLastName").text(data.lastName);
                })
                <%--location.href = "${pageScope.get("basePath")}" + "cartServlet?action=addItem&id=" + bookId;--%>
            });
        });
    </script>

4 、i18n 国际化 (了解内容)

4.1 、什么是 i18n 国际化?

国际化(Internationalization)指的是同一个网站可以支持多种不同的语言,以方便不同国家,不同语种的用户访问。

关于国际化我们想到的最简单的方案就是为不同的国家创建不同的网站,比如苹果公司,他的英文官网是:http://www.apple.com 而中国官网是 http://www.apple.com/cn

苹果公司这种方案并不适合全部公司,而我们希望相同的一个网站,而不同人访问的时候可以根据用户所在的区域显示不同的语言文字,而网站的布局样式等不发生改变。

于是就有了我们说的国际化,国际化总的来说就是同一个网站不同国家的人来访问可以显示出不同的语言。但实际上这种需求并不强烈,一般真的有国际化需求的公司,主流采用的依然是苹果公司的那种方案,为不同的国家创建不同的页面。所以国际化的内容我们了解一下即可。

国际化的英文 Internationalization,但是由于拼写过长,老外想了一个简单的写法叫做 I18N,代表的是Internationalization 这个单词,以 I 开头,以 N 结尾,而中间是 18 个字母,所以简写为 I18N。以后我们说 I18N 和国际化是一个意思。

4.2、国际化相关要素介绍

1644401151338

4.3 、国际化资源 properties 测试

配置两个语言的配置文件:

i18n_en_US.properties 英文

username=username
password=password
sex=sex
age=age
regist=regist
boy=boy
email=email
girl=girl
reset=reset
submit=submit

i18n_zh_CN.properties 中文

username=用户名
password=密码
sex=性别
age=年龄
regist=注册
boy=男
girl=女
email=邮箱
reset=重置
submit=提交

国际化测试代码:

public class I18nTest {

    @Test
    public void testLocale() {
//         获取你系统默认的语言。国家信息
        Locale locale = Locale.getDefault();
        System.out.println(locale);

//        for (Locale availableLocale : Locale.getAvailableLocales()) {
//            System.out.println(availableLocale);
//        }

        // 获取中文,中文的常量的Locale对象
        System.out.println(Locale.CHINA);
        // 获取英文,美国的常量的Locale对象
        System.out.println(Locale.US);
    }

    @Test
    public void testI18n() {
        // 得到我们需要的Locale对象
        Locale locale = Locale.CHINA;
        // 通过指定的basename和Locale对象,读取 相应的配置文件
        ResourceBundle bundle = ResourceBundle.getBundle("i18n", locale);

        System.out.println("username:" + bundle.getString("username"));
        System.out.println("password:" + bundle.getString("password"));
        System.out.println("Sex:" + bundle.getString("sex"));
        System.out.println("age:" + bundle.getString("age"));
    }
}

4.4、通过请求头国际化页面

<%@ page import="java.util.Locale" %>
<%@ page import="java.util.ResourceBundle" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
		 pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="pragma" content="no-cache" />
<meta http-equiv="cache-control" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<%
	// 从请求头中获取 Locale 信息(语言)
	Locale locale = request.getLocale();
	System.out.println(locale);
	// 获取读取包(根据 指定的 baseName 和 Locale 读取 语言信息)
	ResourceBundle i18n = ResourceBundle.getBundle("i18n", locale);
%>
	<a href="i18n.jsp?country=cn">中文</a>|
	<a href="i18n.jsp?country=usa">english</a>
	<center>
		<h1><%=i18n.getString("regist")%></h1>
		<table>
		<form>
			<tr>
				<td><%=i18n.getString("username")%></td>
				<td><input name="username" type="text" /></td>
			</tr>
			<tr>
				<td><%=i18n.getString("password")%></td>
				<td><input type="password" /></td>
			</tr>
			<tr>
				<td><%=i18n.getString("sex")%></td>
				<td>
					<input type="radio" /><%=i18n.getString("boy")%>
					<input type="radio" /><%=i18n.getString("girl")%>
				</td>
			</tr>
			<tr>
				<td><%=i18n.getString("email")%></td>
				<td><input type="text" /></td>
			</tr>
			<tr>
				<td colspan="2" align="center">
				<input type="reset" value="<%=i18n.getString("reset")%>" />&nbsp;&nbsp;
				<input type="submit" value="<%=i18n.getString("submit")%>" /></td>
			</tr>
			</form>
		</table>
		<br /> <br /> <br /> <br />
	</center>
	国际化测试:
	<br /> 1、访问页面,通过浏览器设置,请求头信息确定国际化语言。
	<br /> 2、通过左上角,手动切换语言
</body>
</html>

4.5、通过显示的选择语言类型进行国际化

<%@ page import="java.util.Locale" %>
<%@ page import="java.util.ResourceBundle" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
		 pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="pragma" content="no-cache" />
<meta http-equiv="cache-control" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	<%
		// 从请求头中获取Locale信息(语言)
		Locale locale = null;

		String country = request.getParameter("country");
		if ("cn".equals(country)) {
			locale = Locale.CHINA;
		} else if ("usa".equals(country)) {
			locale = Locale.US;
		} else {
			locale = request.getLocale();
		}

		System.out.println(locale);
		// 获取读取包(根据 指定的baseName和Locale读取 语言信息)
		ResourceBundle i18n = ResourceBundle.getBundle("i18n", locale);
	%>
	<a href="i18n.jsp?country=cn">中文</a>|
	<a href="i18n.jsp?country=usa">english</a>
	<center>
		<h1><%=i18n.getString("regist")%></h1>
		<table>
		<form>
			<tr>
				<td><%=i18n.getString("username")%></td>
				<td><input name="username" type="text" /></td>
			</tr>
			<tr>
				<td><%=i18n.getString("password")%></td>
				<td><input type="password" /></td>
			</tr>
			<tr>
				<td><%=i18n.getString("sex")%></td>
				<td>
					<input type="radio" /><%=i18n.getString("boy")%>
					<input type="radio" /><%=i18n.getString("girl")%>
				</td>
			</tr>
			<tr>
				<td><%=i18n.getString("email")%></td>
				<td><input type="text" /></td>
			</tr>
			<tr>
				<td colspan="2" align="center">
				<input type="reset" value="<%=i18n.getString("reset")%>" />&nbsp;&nbsp;
				<input type="submit" value="<%=i18n.getString("submit")%>" /></td>
			</tr>
			</form>
		</table>
		<br /> <br /> <br /> <br />
	</center>
	国际化测试:
	<br /> 1、访问页面,通过浏览器设置,请求头信息确定国际化语言。
	<br /> 2、通过左上角,手动切换语言
</body>
</html>

4.6 、JSTL 标签库实现国际化

<%--1 使用标签设置 Locale 信息 --%>
<fmt:setLocale value="" />

<%--2 使用标签设置 baseName--%>
<fmt:setBundle basename=""/>

< %--3 输出指定 key 的国际化信息 --%>
<fmt:message key="" />
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="pragma" content="no-cache"/>
    <meta http-equiv="cache-control" content="no-cache"/>
    <meta http-equiv="Expires" content="0"/>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
</head>
<body>
<%--1 使用标签设置Locale信息--%>
<fmt:setLocale value="${param.locale}"/>
<%--2 使用标签设置baseName--%>
<fmt:setBundle basename="i18n"/>


<a href="i18n_fmt.jsp?locale=zh_CN">中文</a>|
<a href="i18n_fmt.jsp?locale=en_US">english</a>
<center>
    <h1><fmt:message key="regist"/></h1>
    <table>
        <form>
            <tr>
                <td><fmt:message key="username"/></td>
                <td><input name="username" type="text"/></td>
            </tr>
            <tr>
                <td><fmt:message key="password"/></td>
                <td><input type="password"/></td>
            </tr>
            <tr>
                <td><fmt:message key="sex"/></td>
                <td>
                    <input type="radio"/><fmt:message key="boy"/>
                    <input type="radio"/><fmt:message key="girl"/>
                </td>
            </tr>
            <tr>
                <td><fmt:message key="email"/></td>
                <td><input type="text"/></td>
            </tr>
            <tr>
                <td colspan="2" align="center">
                    <input type="reset" value="<fmt:message key="reset" />"/>&nbsp;&nbsp;
                    <input type="submit" value="<fmt:message key="submit" />"/></td>
            </tr>
        </form>
    </table>
    <br/> <br/> <br/> <br/>
</center>
</body>
</html>
posted @ 2022-02-09 22:55  努力&选择  阅读(162)  评论(0)    收藏  举报