HTML

网页的组成部分

页面由三部分内容组成,分别是内容(结构)、表现、行为

- 内容(结构),是我们在页面中可以看到的数据。我们称之为内容。一般内容我们使用**html 技术**来展示。 
- 表现,指的是这些内容在页面上的展示形式。比如说。布局,颜色,大小等等。一般使用**CSS 技术**实现
- 行为,指的是页面中元素与输入设备交互的响应。一般使用 **javascript技术**实现

HTML 标签

image-20210325122628350

常用标签

1、font 字体标签

font 标签是字体标签,它可以用来修改文本的字体,颜色,大小(尺寸) 
    color 属性修改颜色 
    face 属性修改字体 
    size 属性修改文本大小

2、特殊字符

image-20210418204904524

3、标题标签

h1 - h6 都是标题标签 
h1 最大 
h6 最小
    align 属性是对齐属性 
        left 左对齐(默认) 
        center 剧中 
        right 右对齐

4、超链接

a 标签是 超链接 
    href 属性设置连接的地址 
    target 属性设置哪个目标进行跳转 
        _self 表示当前页面(默认值) 
        _blank 表示打开新页面来进行跳转

5、列表标签

ul 是无序列表 
	type 属性可以修改列表项前面的符号 
li 是列表项

6、img 标签

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

7、表格标签

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

tr 是行标签 
th 是表头标签 
td 是单元格标签 
	align 设置单元格文本对齐方式 
b 是加粗标签

8、跨行跨列表格

colspan 属性设置跨列 
rowspan 属性设置跨行

9、iframe 框架标签

ifarme 和 a 标签组合使用的步骤: 
1)在 iframe 标签中使用 name 属性定义一个名称 
2)在 a 标签的 target 属性上设置 iframe 的 name 的属性值

10、表单标签

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 标签是表单标签 
    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、理论上没有数据长度的限制

11、其他标签

div 标签 默认独占一行 
span 标签 它的长度是封装数据的长度 
p 段落标签 默认会在段落的上方或下方各空出一行来(如果已有就不再空)

相对路径和绝对路径

1.在 JavaSE 中路径也分为相对路径和绝对路径.

1)相对路径:从工程名开始算 
2)绝对路径:盘符:/目录/文件名 

2.在 web 中路径分为相对路径和绝对路径两种

1)相对路径: 
.表示当前文件所在的目录 
..表示当前文件所在的上一级目录 
文件名  表示当前文件所在目录的文件,相当于 ./文件名, ./ 可以省略 

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

二、CSS

2.1 和HTML结合方式

1、在标签的 style 属性上设置”key:value value;”,修改标签样式。
2、在 head 标签中,使用 style 标签来定义各种自己需要的 css 样式。
3、把 css 样式写成一个单独的 css 文件,再通过 link 标签引入即可复用。

2.2 CSS 选择器

1、标签名选择器

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

2、id 选择器

id 选择器的格式是: 
#id 属性值{ 
	属性:值; 
}
id 选择器,可以让我们通过 id 属性选择性的去使用这个样式。

3、class 选择器(类选择器)

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

4、组合选择器

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

2.3 常用样式

1、字体颜色 
color:red; 
颜色可以写颜色名如:black, blue, red, green 等 
颜色也可以写 rgb 值和十六进制表示值:如 rgb(255,0,0),#00F6DE,如果写十六进制值必 须加#

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

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

4、背景颜***ackground-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; }

三、JavaScript

主要是完成页面的数据验证

3.1 变量

1、JavaScript 的变量类型:

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

2、特殊的值

undefined 未定义,所有js变量未赋于初始值,默认值是undefined
null 空值 
NaN 全称是:Not a Number,非数字,非数值。

3.2 常见运算

1、关系运算

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

2、逻辑运算

在JavaScript语言中,所有的变量,都可以做为一个boolean类型的变量去使用。
**0 、null、 undefined、””(空串)** 都认为是 false

1)&& 且运算

第一种:当表达式全为真的时候。返回最后一个表达式的值。
第二种:当表达式中,有一个为假的时候。返回第一个为假的表达式的值

2)|| 或运算

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

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

3.3 数组

1、数组定义方式

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

3.4 函数

1、第一种定义方式

可以使用 function 关键字来定义函数

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

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

2、第二种定义方式

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

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

3、函数的 arguments 隐形参数

就是在 function 函数中不需要定义,但却可以直接用来获取所有参数的变量。我们管它叫隐形参数。

隐形参数特别像 java 基础的可变长参数一样。 public void fun( Object ... args ); 可变长参数其他是一个数组。 那么 js 中的隐形参数也跟 java 的可变长参数一样。操作类似数组。

3.5 自定义对象

1、Object 形式的自定义对象

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

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

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

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

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

3.6 事件

1、常用的事件:

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

2、事件的注册

就是告诉浏览器,当事件响应后要执行哪些操作代码,叫事件注册或事件绑定。

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

2)动态注册事件:是指先通过js代码得到标签的dom对象,然后再通过dom对象.事件名=function(){}这种形式赋于事件响应后的代码,叫动态注册。 
动态注册基本步骤: 
1、获取标签对象 
2、标签对象.事件名 = fucntion(){}
<!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 获取标签对象 var btnObj = document.getElementById("btn01"); // 2 通过标签对象.事件名 = function(){} btnObj.onclick = function () {
      alert("动态注册的onclick事件"); } } </script>
</head>
<body>
    <!--静态注册onClick事件-->
    <button onclick="onclickFun();">按钮1</button>
    <button id="btn01">按钮2</button>
</body>
</html>
javaScript 事件对象 

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

如何获取呢 javascript 事件对象呢? 
在给元素绑定事件的时候,在事件的 function( event ) 参数列表中添加一个参数,这个参数名,我们习惯取名为 event。 
这个 event 就是 javascript 传递参事件处理函数的事件对象。

3.7 DOM模型

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

1、DOM对象

image-20210418185343574
Document对象的理解: 
第一点:Document 它管理了所有的 HTML 文档内容。 
第二点:document 它是一种树结构的文档。有层级关系。 
第三点:它让我们把所有的标签都对象化 
第四点:我们可以通过document访问所有的标签对象。
模拟对象化,相当于: 
class Dom{
    
    private String id; // id 属性 
    private String tagName; //表示标签名 
    private Dom parentNode; //父亲 
    private List<Dom> children; // 孩子结点 
    private String innerHTML; // 起始标签和结束标签中间的内容 
}

2、DOM方法

查找dom对象:
1)document.getElementById(elementId) 通过标签的id属性查找标签dom对象,elementId 是标签的id属性值 
2)document.getElementsByName(elementName) 通过标签的 name 属性查找标签 dom 对象,elementName 标签的 name 属性值 
3)document.getElementsByTagName(tagname) 通过标签名查找标签 dom 对象。tagname 是标签名 
4)document.createElement(tagName) 方法,通过给定的标签名,创建一个标签对象。tagName 是要创建的标签名

document对象的三个查询方法
如果有id属性,优先使用getElementById方法来进行查询
如果没有id 属性,则优先使用getElementsByName方法来进行查询 
如果id 属性和name 属性都没有最后再按标签名查getElementsByTagName 
以上三个方法,一定要在页面加载完成之后执行,才能查询到标签对象。

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

方法: 
通过具体的元素节点调用getElementsByTagName()方法,获取当前节点的指定标签名孩子节点。
appendChild(oChildNode)方法,可以添加一个子节点,oChildNode 是要添加的孩子节点 。

属性: 
childNodes 属性,获取当前节点的所有子节点 
firstChild 属性,获取当前节点的第一个子节点 
lastChild 属性,获取当前节点的最后一个子节点 
parentNode 属性,获取当前节点的父节点 
nextSibling 属性,获取当前节点的下一个节点 
previousSibling 属性,获取当前节点的上一个节点 
className 用于获取或设置标签的 class 属性值 
innerHTML 属性,表示获取/设置起始标签和结束标签中的内容 
innerText 属性,表示获取/设置起始标签和结束标签中的文本

四、jQuery

jQuery就是 JavaScript 和查询(Query),它就是辅助 JavaScript 开发的 js 类库。

4.1 核心函数

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

1、传入参数为 [函数] 时:表示页面加载完成之后。相当于 window.onload = function(){} 
2、传入参数为 [HTML字符串] 时:会对我们创建这个html标签对象 
3、传入参数为 [选择器字符串] 时:
    $(“#id 属性值”); id 选择器,根据id查询标签对象
    $(“标签名”); 标签名选择器,根据指定的标签名查询标签对象 
    $(“.class 属性值”); 类型选择器,可以根据 class 属性查询标签对象 
4、传入参数为[DOM 对象]时: 会把这个dom对象转换为jQuery对象

4.2 jQuery和Dom

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]

2、jQuery 对象的本质

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

3、Dom与jQuery区别

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

4、Dom 对象和 jQuery 对象互转

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

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

4.3 jQuery 选择器

1、基本选择器

#ID 选择器:根据id查找标签对象 
.class 选择器:根据class查找标签对象 
element 选择器:根据标签名查找标签对象 
* 选择器:表示任意的,所有的元素 
selector1,selector2 组合选择器:合并选择器 1,选择器 2 的结果并返回

2、层级选择器

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

3、过滤选择器

1)基本过滤器

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

2)内容过滤器

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

3)属性过滤器

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

4)表单过滤器

: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

5)表单对象属性过滤器

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

4.4 jQuery 元素筛选

eq() 获取给定索引的元素 功能跟 :eq() 一样 
first() 获取第一个元素 功能跟 :first 一样 
last() 获取最后一个元素 功能跟 :last 一样 
filter(exp) 留下匹配的元素 
is(exp) 判断是否匹配给定的选择器,只要有一个匹配就返回,true 
has(exp) 返回包含有匹配选择器的元素的元素 功能跟 :has 一样 
not(exp) 删除匹配选择器的元素 功能跟 :not 一样 
children(exp) 返回匹配给定选择器的子元素 功能跟 parent>child 一样 
find(exp) 返回匹配给定选择器的后代元素 功能跟 ancestor descendant 一样
next() 返回当前元素的下一个兄弟元素 功能跟 prev + next 功能一样 
nextAll() 返回当前元素后面所有的兄弟元素 功能跟 prev ~ siblings 功能一样 
nextUntil() 返回当前元素到指定匹配的元素为止的后面元素 
parent() 返回父元素 
prev(exp) 返回当前元素的上一个兄弟元素 
prevAll() 返回当前元素前面所有的兄弟元素 
prevUnit(exp) 返回当前元素到指定匹配的元素为止的前面元素 
siblings(exp) 返回所有兄弟元素 
add() 把 add 匹配的选择器的元素添加到当前 jquery 对象中

4.5 jQuery 的属性操作

html() 它可以设置和获取起始标签和结束标签中的内容。 跟 dom 属性 innerHTML 一样。 
text() 它可以设置和获取起始标签和结束标签中的文本。 跟 dom 属性 innerText 一样。 
val() 它可以设置和获取表单项的 value 属性值。 跟 dom 属性 value 一样
attr() 可以设置和获取属性的值,不推荐操作 checked、readOnly、selected、disabled 等等 
	attr方法还可以操作非标准的属性。比如自定义属性:abc,bbj 
prop() 可以设置和获取属性的值,只推荐操作 checked、readOnly、selected、disabled 等等

4.6 DOM 的增删改

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

外部插入: 
insertAfter() a.insertAfter(b) 得到 ba 
insertBefore() a.insertBefore(b) 得到 ab 

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

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

4.7 CSS 样式操作

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

4.8 jQuery 动画

基本动画 
show() 将隐藏的元素显示 
hide() 将可见的元素隐藏。 
toggle() 可见就隐藏,不可见就显示。 
以上动画方法都可以添加参数。 
1、第一个参数是动画 执行的时长,以毫秒为单位 
2、第二个参数是动画的回调函数 (动画完成后自动调用的函数) 

淡入淡出动画 
fadeIn() 淡入(慢慢可见) 
fadeOut() 淡出(慢慢消失) 
fadeTo() 在指定时长内慢慢的将透明度修改到指定的值。0 透明,1 完成可见,0.5 半透明 
fadeToggle() 淡入/淡出 切换

4.9 jQuery 事件操作

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

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

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

他们执行的次数? 
1、原生 js 的页面加载完成之后,只会执行最后一次的赋值函数。 
2、jQuery 的页面加载完成之后是全部把注册的 function 函数,依次顺序全部执行。
jQuery 中其他的事件处理方法

click() 它可以绑定单击事件,以及触发单击事件 
mouseover() 鼠标移入事件 
mouseout() 鼠标移出事件 
bind() 可以给元素一次性绑定一个或多个事件。 
one() 使用上跟 bind 一样。但是 one 方法绑定的事件只会响应一次。 
unbind() 跟 bind 方法相反的操作,解除事件的绑定 
live()也是用来绑定事件。它可用来绑定选择器匹配的所有元素的事件。哪怕这个元素是后面动态创建出来的也有效
事件的冒泡 

什么是事件的冒泡? 
事件的冒泡是指,父子元素同时监听同一个事件。当触发子元素的事件的时候,同一个事件也被传递到了父元素的事件里去 响应。 

那么如何阻止事件冒泡呢? 
在子元素事件函数体内,return false; 可以阻止事件的冒泡传递。

五、XML

@面:web.xml文件中可以配置哪些内容

web.xml用于配置Web应用的相关信息,如:***、过滤器、Servlet、相关参数、会话超时时间、安全验证方式、错误页面等。

下面是一些开发中常见的配置:
1)配置Spring上下文加载***,加载Spring配置文件并创建IoC容器:
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
</context-param>

<listener>
    <listener-class>
    org.springframework.web.context.ContextLoaderListener
    </listener-class>
</listener>

2)配置Spring的OpenSessionInView过滤器,解决延迟加载和Hibernate会话关闭的矛盾:
<filter>
    <filter-name>openSessionInView</filter-name>
    <filter-class>
    	org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
    </filter-class>
</filter>

<filter-mapping>
    <filter-name>openSessionInView</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

3)配置会话超时时间为10分钟:
<session-config>
	<session-timeout>10</session-timeout>
</session-config>

4)配置404和Exception的错误页面:
<error-page>
    <error-code>404</error-code>
    <location>/error.jsp</location>
</error-page>

<error-page>
    <exception-type>java.lang.Exception</exception-type>
    <location>/error.jsp</location>
</error-page>

5)配置安全认证方式:
<security-constraint>
    <web-resource-collection>
        <web-resource-name>ProtectedArea</web-resource-name>
        <url-pattern>/admin/*</url-pattern>
        <http-method>GET</http-method>
        <http-method>POST</http-method>
    </web-resource-collection>
    
    <auth-constraint>
    	<role-name>admin</role-name>
    </auth-constraint>
</security-constraint>

<login-config>
	<auth-method>BASIC</auth-method>
</login-config>

<security-role>
	<role-name>admin</role-name>
</security-role>

六、Tomcat

6.1 JavaWeb 的概念

JavaWeb:所有**通过 Java 语言编写可以通过浏览器访问的程序**的总称,叫 JavaWeb。 JavaWeb 是**基于请求和响应**来开发的。

请求:请求是指客户端给服务器发送数据,叫请求 Request。
响应:响应是指服务器给客户端回传数据,叫响应 Response。 

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

6.2 资源的分类

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

6.3 Tomcat 的使用

1.目录介绍

image-20210325125320278

2.部暑 web 工程到 Tomcat

1)第一种部署:

只需要把 web 工程的目录拷贝到 Tomcat 的 webapps 目录下即可。
访问:http://ip:port/工程名/目录下/文件名

2)第二种部署

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

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

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

3.手托和输入 http://ip:端 口号/工程名/访问的区别

image-20210325125949720 image-20210325130009715

4.默认访问

当我们在浏览器地址栏中输入访问地址如下:
http://ip:port/ 		没有工程名的时候,默认访问的是ROOT工程。 

当我们在浏览器地址栏中输入的访问地址如下: 
http://ip:port/工程名/ 		没有资源名,默认访问index.html页面

5.IDEA 中如何创建动态 web 工程

1)Web 工程的目录介绍

image-20210325130358836

2)给动态 web 工程添加额外 jar 包

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

image-20210325130551989

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

image-20210325130612982

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

image-20210325130707671

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

image-20210325130756852

3)在 IDEA 中部署工程到 Tomcat

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

image-20210325130900949

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

image-20210325130931541

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

image-20210325130954441

重启 Tomcat 运行实例:

image-20210325131038050

4)修改工程访问路径

image-20210325131136273

Servlet

Servlet 技术

a)什么是 Servlet

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

b)手动实现 Servlet 程序

步骤:

  • 编写一个类去实现 Servlet 接口
  • 实现 service 方法,处理请求,并响应数据
  • 到 web.xml 中去配置 servlet 程序的访问地址
public class HelloServlet implements Servlet {
    
    /** * service 方法是专门用来处理请求和响应的 */ 
    @Override 
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
    
        System.out.println("Hello Servlet 被访问了"); 
    } 
}
<!-- servlet 标签给Tomcat配置Servlet程序 --> 
<servlet> 
    <!--servlet-name 标签 Servlet 程序起一个别名(一般是类名) --> 
    <servlet-name>HelloServlet</servlet-name> 
    <!--servlet-class 是 Servlet 程序的全类名--> 
    <servlet-class>com.atguigu.servlet.HelloServlet</servlet-class> 
</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>

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wEaeAIev-1648184655235)(E:/Java%E6%9C%AA%E6%9D%A5/%E7%AC%94%E8%AE%B0-%E5%AD%A6%E4%B9%A0/Pic/image-20210325142158345.png)]

d)Servlet 的生命周期

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QjpolNnV-1648184655238)(E:/Java%E6%9C%AA%E6%9D%A5/%E7%AC%94%E8%AE%B0-%E5%AD%A6%E4%B9%A0/Pic/image-20210423081416331.png)]

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

@面:Servlet的生命周期,Servlet和CGI的区别

Servlet被服务器实例化后,容器运行其init方法,请求到达时运行其service方法,service方法自动派遣运行与请求对应的doXXX方法(doGet,doPost)等,当服务器决定将实例销毁的时候调用其destroy方法。

与cgi的区别在于servlet处于服务器进程中,它通过多线程方式运行其service方法,一个实例可以服务于多个请求,并且其实例一般不会销毁,而CGI对每个请求都产生新的进程,服务完成后就销毁,所以效率上低于servlet。

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

public class HelloServlet implements Servlet {
    
    /** * service 方法是专门用来处理请求和响应的 */ 
    @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 请求"); 
    }
    /*** 做 post 请求的操作 */ 
    public void doPost(){
    
        System.out.println("post 请求"); 
    } 
}

f)继承 HttpServlet 实现 Servlet 程序

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

1、编写一个类去**继承 HttpServlet 类** 
2、根据业务需要**重写 doGet 或 doPost 方法** 
3、到 web.xml 中的**配置 Servlet 程序的访问地址**
public class HelloServlet2 extends HttpServlet {
    
    /** * doGet()在 get 请求的时候调用 */ 
    @Override 
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
        System.out.println("HelloServlet2 的 doGet 方法"); 
    }
    /** * doPost()在 post 请求的时候调用 */ 
    @Override 
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
        System.out.println("HelloServlet2 的 doPost 方法"); 
    } 
}
<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-20210325143547592 image-20210325143604565

h)Servlet 类的继承体系

image-20210325143709787

@面:Servlet接口中有哪些方法

Servlet接口定义了5个方法,其中前三个方法与Servlet生命周期相关:
1)void init(ServletConfig config) throws ServletException
2)void service(ServletRequest req, ServletResponse resp) throws ServletException, java.io.IOException
3)void destory()
4)java.lang.String getServletInfo()
5)ServletConfig getServletConfig()

Web容器加载Servlet并将其实例化后,Servlet生命周期开始,容器运行其init()方法进行Servlet的初始化;请求到达时调用Servlet的service()方法,service()方***根据需要调用与请求对应的doGet或doPost等方法;当服务器关闭或项目被卸载时服务器会将Servlet实例销毁,此时会调用Servlet的destroy()方法。

@面:Servlet执行的过程中,实现哪几个方法

1)public void init(ServletConfig config)
init()方法在servlet的生命周期中仅执行一次,在服务器装载servlet时执行。
缺省的init()方法通常是符合要求的,不过也可以根据需要进行override,比如管理服务器端资源,一次性装入GIF图像,初始化数据库连接等,缺省的inti()方法设置了servlet的初始化参数,并用它的ServeltConfig对象参数来启动配置,所以覆盖init()方法时,应调用super.init()以确保仍然执行这些任务。

2)public ServletConfig getServletConfig()
getServletConfig()方法返回一个servletConfig对象,该对象用来返回初始化参servletContext。servletContext接口提供有关servlet的环境信息。

3)public String getServletInfo()
getServletInfo()方法提供有关servlet的信息,如作者,版本,版权。

4)public void service(ServletRequest request,ServletResponse response)
service()方法是servlet的核心,在调用service方法之前,应确保已完成init方法。对于HttpServlet,每当客户请求一个HttpServlet对象,该对象的service()方法就要被调用。
HttpServlet缺省的service()方法的服务功能就是调用与HTTP请求的方法相应的do功能,doPost()和doGet(),所以对于HttpServlet,一般都是重写doPost()和doGet()方法。

5)public void destroy()
destroy()方法在servlet的生命周期中也仅执行一次,即在服务器停止卸载servlet时执行,把servlet作为服务器进程的一部分关闭。
缺省的destroy()方法通常是符合要求的,但也可以override,比如在卸载servlet时将统计数字保存在文件中,或是关闭数据库连接。

7.2 ServletConfig 类

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

a)ServletConfig 类的三大作用

1、可以获取 Servlet **程序的别名** servlet-name 的值 
2、**获取初始化参数** init-param 
3、**获取 ServletContext 对象
<!-- 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>/hello</url-pattern> 
</servlet-mapping>
@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()); 
}

@面:Servlet如何获取用户配置的初始化参数以及服务器上下文参数

可以通过重写Servlet接口的init(ServletConfig)方法并通过ServletConfig对象的getInitParameter()方法来获取Servlet的初始化参数。
可以通过ServletConfig对象的getServletContext()方法获取ServletContext对象,并通过该对象的getInitParameter()方法来获取服务器上下文参数。
ServletContext对象也可以在处理用户请求的方法(如doGet()方法)中通过请求对象的getServletContext()方法来获得。

7.3 ServletContext 类

a)什么是 ServletContext?

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

b)ServletContext 类的四个作用

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

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

web.xml 中的配置:

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

ServletContext 像 Map 一样存取数据:

public class ContextServlet1 extends HttpServlet {
    
    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")); 
    } 
}

7.4 HTTP 协议

a)什么是 HTTP 协议

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

b)请求的 HTTP 协议格式

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

i. GET 请求

1、请求行 
(1) 请求的方式 GET 
(2) 请求的资源路径[+?+请求参数] 
(3) 请求的协议的版本号 HTTP/1.1 
2、请求头 
key:value组成不同的键值对,表示不同的含义。
image-20210325150041728

ii. POST 请求

1、请求行
(1) 请求的方式 POST 
(2) 请求的资源路径[+?+请求参数] 
(3) 请求的协议的版本号 HTTP/1.1 
2、请求头
key:value不同的请求头,有不同的含义 
3、空行 
4、请求体 就是发送给服务器的数据
image-20210325150224799

@面:服务器收到表单数据,调用了哪一个方法

HTML的<form>元素有一个method属性,用来指定提交表单的方式,其值可以是get或post。
我们自定义的Servlet一般情况下会重写doGet()或doPost()两个方法之一或全部,如果是GET请求就调用doGet()方法,如果是POST请求就调用doPost()方法。

我们自定义的Servlet通常继承自HttpServlet,HttpServlet继承自GenericServlet并重写了其中的service()方法,这个方法是Servlet接口中定义的。HttpServlet重写的service方***先获取用户请求的方法,然后根据请求方法调用doGet、doPost、doPut、doDelete等方法,如果在自定义Servlet中重写了这些方法,那么显然会调用重写过的方法,这显然是对模板方法模式的应用。当然,自定义Servlet中也可以直接重写service()方法,那么不管是哪种方式的请求,都可以通过自己的代码进行处理,这对于不区分请求方法的场景比较合适。

@面:get和post请求的区别

1)get将表单中数据按照name=value的形式,添加到action所指向的URL后面,并且两者使用"?"连接,而各个变量之间使用"&"连接;post是将表单中的数据放在HTTP协议的请求头或消息体中,传递到action所指向URL;
2)get传输的数据要受到URL长度限制(1024字节);而post可以传输大量的数据,上传文件通常要使用post方式;
3)使用get时参数会显示在地址栏上,如果这些数据不是敏感数据,那么可以使用get;对于敏感数据还是应用使用post;
4)GET请求只能进行url编码,而POST支持多种编码方式。
5)GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
6)GET只接受ASCII字符的参数的数据类型,而POST没有限制

那么,post那么好为什么还用get?get效率高!。

@面:常用请求头的说明

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

@面:哪些是 GET 请求,是 POST 请求

GET请求有哪些: 
1、form 标签 method=get 
2、a 标签 
3、link 标签引入 css 
4、Script 标签引入 js 文件 
5、img 标签引入图片 
6、iframe 引入 html 页面 
7、在浏览器地址栏中输入地址后敲回车

POST请求有哪些: 
1、form 标签 method=post

c)响应的 HTTP 协议格式

1、响应行 
(1) 响应的协议和版本号 
(2) 响应状态码 
(3) 响应状态描述符 
2、响应头 
key : value 不同的响应头,有其不同含义 
3、空行 
4、响应体  就是回传给客户端的数据
image-20210325150620773

@面:常用的响应码说明

200 表示请求成功 
301 所请求的页面已经转移至新的url
302 所请求的页面已经临时转移至新的url,表示请求重定向
400 错误请求,因发送的请求语法错误,服务器无法正常读取
401 未经授权,需要身份验证后才能获取所请求的内容,类似于403错误。不同点是,401错误后,只要正确输入帐号密码,验证即可通过
403 禁止访问,客户端没有权利访问所请求内容,服务器拒绝本次请求
404 表示请求服务器已经收到了,但是你要的数据不存在(请求地址错误) 
500 表示服务器已经收到请求,但是服务器内部错误(代码错误)

e)MIME 类型说明

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

常见的 MIME 类型:

image-20210325150832679 image-20210325150849241

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

image-20210325150924017

HttpServletRequest 类

a)作用

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

b)HttpServletRequest 类的常用方法

image-20210325153155373

c)如何获取请求参数

<body> 
    <form action="http://localhost:8080/07_servlet/parameterServlet" method="get"> 
        用户名:<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>
public class ParameterServlet extends HttpServlet {
    
    @Override 
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
        // 获取请求参数 
        String username = req.getParameter("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));
    }
}

doGet 请求的中文乱码解决:

// 获取请求参数 
String username = req.getParameter("username"); 
//1 先以 iso8859-1 进行编码 
//2 再以 utf-8 进行解码 
username = new String(username.getBytes("iso-8859-1"), "UTF-8");

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

@Override 
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"); 
    String password = req.getParameter("password"); 
    String[] hobby = req.getParameterValues("hobby"); 
    System.out.println("用户名:" + username); 
    System.out.println("密码:" + password); 
    System.out.println("兴趣爱好:" + Arrays.asList(hobby)); 
}

e)请求的转发

请求转发是指,服务器收到请求后,从一次资源跳转到另一个资源的操作叫请求转发。

image-20210325153815761

Servlet1 代码:

public class Servlet1 extends HttpServlet {
    
    @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
        // 获取请求的参数(办事的材料)查看 
        String username = req.getParameter("username"); 
        System.out.println("在 Servlet1(柜台 1)中查看参数(材料):" + username); 
        
        // 给材料盖一个章,并传递到 Servlet2(柜台 2)去查看 
        req.setAttribute("key1","柜台 1 的章"); 
        // 问路:Servlet2(柜台 2)怎么走 
        /** * 请求转发必须要以斜杠打头, * / 斜杠表示地址为:http://ip:port/工程名/ , 映射到 IDEA 代码的 web 目录 <br/> */ 
        RequestDispatcher requestDispatcher = req.getRequestDispatcher("/servlet2");
        
        // 走向 Sevlet2(柜台 2) 
        requestDispatcher.forward(req,resp);
}}

Servlet2 代码:

public class Servlet2 extends HttpServlet {
    
    @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
        // 获取请求的参数(办事的材料)查看 
        String username = req.getParameter("username"); 
        System.out.println("在 Servlet2(柜台 2)中查看参数(材料):" + username); 
        // 查看 柜台 1 是否有盖章 
        Object key1 = req.getAttribute("key1"); 
        System.out.println("柜台 1 是否有章:" + key1); 
        // 处理自己的业务 
        System.out.println("Servlet2 处理自己的业务 "); 
    } 
}

f) base 标签的作用

image-20210325154401207
<!DOCTYPE html> 
<html lang="zh_CN"> 
    <head> 
        <meta charset="UTF-8"> 
        <title>Title</title> 
        <!--base 标签设置页面相对路径工作时参照的地址 href 属性就是参数的地址值 --> 
        <base href="http://localhost:8080/07_servlet/a/b/"> 
    </head> 
    <body> 这是a下的b下的c.html页面<br/> 
        <a href="../../index.html">跳回首页</a><br/> 
    </body> 
</html>

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

image-20210325154615778

h)web 中 / 斜杠的不同意义

image-20210325154703928

@面:使用Servlet获取用户提交的查询参数以及表单数据

可以通过请求对象HttpServletRequest的getParameter()方法通过参数名获得参数值。
如果有包含多个值的参数(例如复选框),可以通过请求对象的getParameterValues()方法获得。也可以通过请求对象的getParameterMap()获得一个参数名和参数值的映射(Map)。

HttpServletResponse 类

a)HttpServletResponse类的作用

每次请求进来,Tomcat服务器都会创建一个Response对象传递给Servlet程序去使用。HttpServletRequest 表示请求过来的信息,HttpServletResponse表示所有响应的信息,我们如果需要设置返回给客户端的信息,都可以通过 HttpServletResponse 对象来进行设置。

b)两个输出流的说明

image-20210325155014869

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

c)如何往客户端回传数据

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

d)响应的乱码解决

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

e)请求重定向

请求重定向,是指客户端给服务器发请求,然后服务器告诉客户端说。我给你一些地址,你去新地址访问。叫请求重定向(因为之前的地址可能已经被废弃)。

image-20210325155319509
1)请求重定向的第一种方案: 
// 设置响应状态码 302 ,表示重定向,(已搬迁) 
resp.setStatus(302); 
// 设置响应头,说明新的地址在哪里 
resp.setHeader("Location", "http://localhost:8080"); 

2)请求重定向的第二种方案(推荐使用): 
resp.sendRedirect("http://localhost:8080");

@面:forward与redirect区别

1.从地址栏显示来说
forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器.浏览器根本不知道服务器发送的内容从哪里来的,所以它的地址栏还是原来的地址.
redirect是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址.所以地址栏显示的是新的URL.

2.从数据共享来说
forward:转发页面和转发到的页面可以共享request里面的数据.
redirect:不能共享数据.

3.从运用地方来说
forward:一般用于用户登陆的时候,根据角色转发到相应的模块.
redirect:一般用于用户注销登陆时返回主页面和跳转到其它的网站等.

4.从效率来说
forward:高.
redirect:低.

redirect的状态码是302

八、JSP

8.1 概念及作用

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 {
    
        // 通过响应的回传流回传 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 页面的代码:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> 
<html> 
    <head> 
        <title>Title</title> 
    </head> 
    <body> 
        这是 html 页面数据 
    </body> 
</html>

8.2 jsp 的本质

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

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

8.3 三种语法

1、jsp 头部的 page 指令

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

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

i. language 属性 表示jsp翻译后是什么语言文件。暂时只支持 java。 
ii. contentType 属性 表示jsp返回的数据类型是什么。也是源码中 response.setContentType()参数值 
iii. pageEncoding 属性 表示当前jsp页面文件本身的字符集。 
iv. import 属性 跟 java 源代码中一样。用于导包,导类。 

v. autoFlush 属性 设置当 out 输出流缓冲区满了之后,是否自动刷新冲级区。默认值是 true。 
vi. buffer 属性 设置 out 缓冲区的大小。默认是 8kb
vii. errorPage 属性 设置当 jsp 页面运行时出错,自动跳转去的错误页面路径。
viii. isErrorPage 属性 设置当前 jsp 页面是否是错误信息页面。默认是 false。如果是 true 可以 获取异常信息。 
ix. session 属性 设置访问当前 jsp 页面,是否会创建 HttpSession 对象。默认是 true。 
x. extends 属性 设置 jsp 翻译出来的 java 类默认继承谁。

2、jsp 中的常用脚本

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

声明脚本的格式是: **<%! 声明 java 代码 %>** 
作用:可以给 jsp 翻译出来的 java 类定义**属性、方法、静态代码块、内部类**等。
<%--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; } 
%>
image-20210325171914276

ii. 表达式脚本(常用)

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

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

表达式脚本的特点:

1、所有的表达式脚本都会被翻译到_jspService() 方法中 
2、表达式脚本都会被翻译成为 out.print()输出到页面上 
3、由于表达式脚本翻译的内容都在_jspService() 方法中,所以_jspService()方法中的对象都可以直接使用。 
4、表达式脚本中的表达式**不能以分号结束。
<%=12 %> <br> 
<%=12.12 %> <br> 
<%="我是字符串" %> <br> 
<%=map%> <br> 
<%=request.getParameter("username")%>
image-20210325171939557

iii. 代码脚本

代码脚本的格式是: <% java 语句 %>

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

代码脚本的特点是:

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

3、jsp 中的三种注释

i. html 注释

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

ii. java 注释

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

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

iii. jsp 注释

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

8.4 jsp 九大内置对象

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

image-20210325175932898

@面:JSP有哪些内置对象

JSP有9个内置对象:
- request:封装客户端的请求,其中包含来自GET或POST请求的参数;
- response:封装服务器对客户端的响应;
- pageContext:通过该对象可以获取其他对象;
- session:封装用户会话的对象;
- application:封装服务器运行环境的对象;
- out:输出服务器响应的输出流对象;
- config:Web应用的配置对象;
- page:JSP页面本身(相当于Java程序中的this);
- exception:封装页面抛出异常的对象。

@面:JSP的内置对象以及该对象的使用方法

request表示HttpServletRequest对象。它包含了有关浏览器请求的信息,并且提供了几个用于获取cookie, header, 和session数据的有用的方法。
    
response表示HttpServletResponse对象,并提供了几个用于设置送回浏览器的响应的方法(如cookies,头信息等)
    
out对象是javax.jsp.JspWriter的一个实例,并提供了几个方法使你能用于向浏览器回送输出结果。
    
pageContext表示一个javax.servlet.jsp.PageContext对象。它是用于方便存取各种范围的名字空间、servlet相关的对象的API,并且包装了通用的servlet相关功能的方法。
    
session表示一个请求的javax.servlet.http.HttpSession对象。Session可以存贮用户的状态信息
    
applicaton 表示一个javax.servle.ServletContext对象。这有助于查找有关servlet引擎和servlet环境的信息

config表示一个javax.servlet.ServletConfig对象。该对象用于存取servlet实例的初始化参数。
    
page表示从该页面产生的一个servlet实例
    
exception:封装页面抛出异常的对象。

@面:Request对象的主要方法是

setAttribute(String name,Object):设置名字为name的request的参数值
getAttribute(String name):返回由name指定的属性值
getAttributeNames():返回request对象所有属性的名字集合,结果是一个枚举的实例
getCookies():返回客户端的所有Cookie对象,结果是一个Cookie数组
getCharacterEncoding():返回请求中的字符编码方式
getContentLength():返回请求的Body的长度
getHeader(String name):获得HTTP协议定义的文件头信息
getHeaders(String name):返回指定名字的request Header的所有值,结果是一个枚举的实例
getHeaderNames():返回所以request Header的名字,结果是一个枚举的实例
getInputStream():返回请求的输入流,用于获得请求中的数据
getMethod():获得客户端向服务器端传送数据的方法
getParameter(String name):获得客户端传送给服务器端的有name指定的参数值
getParameterNames():获得客户端传送给服务器端的所有参数的名字,结果是一个枚举的实例
getParameterValues(String name):获得有name指定的参数的所有值
getProtocol():获取客户端向服务器端传送数据所依据的协议名称
getQueryString():获得查询字符串
getRequestURI():获取发出请求字符串的客户端地址
getRemoteAddr():获取客户端的IP地址
getRemoteHost():获取客户端的名字
getSession([Boolean create]):返回和请求相关Session
getServerName():获取服务器的名字
getServletPath():获取客户端所请求的脚本文件的路径
getServerPort():获取服务器的端口号
removeAttribute(String name):删除请求中的一个属性

8.5 jsp 四大域对象

image-20210325180020113

域对象是可以像Map一样存取数据的对象,**四个域对象功能一样,不同的是它们对数据的存取范围**。 
虽然四个域对象都**可以存取数据**。在使用上它们是有优先顺序的。 
四个域在使用的时候,优先顺序分别是,他们**从小到大的范围**的顺序。
**pageContext  >>> request   >>> session  >>> application**
<body> 
    <h1>scope.jsp 页面</h1> 
    <% 
        // 往四个域中都分别保存了数据 
        pageContext.setAttribute("key", "pageContext"); 
        request.setAttribute("key", "request"); 
        session.setAttribute("key", "session"); 
        application.setAttribute("key", "application"); 
    %>
    pageContext 域是否有值:<%=pageContext.getAttribute("key")%> <br> 
    request 域是否有值:<%=request.getAttribute("key")%> <br> 
    session 域是否有值:<%=session.getAttribute("key")%> <br> 
    application 域是否有值:<%=application.getAttribute("key")%> <br> 
    <%	
    	request.getRequestDispatcher("/scope2.jsp").forward(request,response); 
    %> 
</body>
<body> 
    <h1>scope2.jsp 页面</h1> 
    pageContext 域是否有值:<%=pageContext.getAttribute("key")%> <br> 
    request 域是否有值:<%=request.getAttribute("key")%> <br> 
    session 域是否有值:<%=session.getAttribute("key")%> <br> 
    application 域是否有值:<%=application.getAttribute("key")%> <br> 
</body>

@面:out和getWriter区 别

response 中表示响应,我们经常用于设置返回给客户端的内容输出,out 也是给用户做输出使用的。

image-20210325180551364
由于 jsp 翻译之后,底层源代码都是使用 out 来进行输出,所以一般情况下。我们在 jsp 页面中统一使用 out 来进行输出。避免打乱页面输出内容的顺序。 
out.write() 输出字符串没有问题 
out.print() 输出任意数据都没有问题(都转换成为字符串后调用的 write 输出)
深入源码,浅出结论:在 jsp 页面中,**可以统一使用 out.print()来进行输出

8.7 常用标签

a)jsp 静态包含

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

<%@ include file="/include/footer.jsp"%>

b)jsp 动态包含

<%--
    <jsp:include page=""></jsp:include> 这是动态包含 
        page 属性是指定你要包含的jsp页面的路径 
        动态包含也可以像静态包含一样。把被包含的内容执行输出到包含位置 
        动态包含的特点: 
        1、动态包含会把包含的jsp页面也翻译成为java代码 
        2、动态包含底层代码使用如下代码去调用被包含的jsp页面执行输出。 
        JspRuntimeLibrary.include(request, response, "/include/footer.jsp", out, false); 
		3、动态包含,还可以传递参数 
--%> 
<jsp:include page="/include/footer.jsp"> 
    <jsp:param name="username" value="bbj"/> 
    <jsp:param name="password" value="root"/> 
</jsp:include>
image-20210325181133107

c)jsp 标签-转发

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

@面:JSP中的静态包含和动态包含的有哪些区别

静态包含是通过JSP的<%@ include file=""%>指令包含页面,动态包含是通过JSP标准动作<jsp:include page="">包含页面。
静态包含是编译时包含,如果包含的页面不存在则会产生编译错误,而且两个页面的"contentType"属性应保持一致,因为两个页面会合二为一,只产生一个class文件,因此被包含页面发生的变动再包含它的页面更新前不会得到更新。
动态包含是运行时包含,可以向被包含的页面传递参数,包含页面和被包含页面是独立的,会编译出两个class文件,如果被包含的页面不存在,不会产生编译错误,也不影响页面其他部分的执行。

@面:jsp有哪些动作

JSP 共有以下6种基本动作 

jsp:include:在页面被请求的时候引入一个文件。 
jsp:useBean:寻找或者实例化一个JavaBean。
jsp:setProperty:设置JavaBean的属性。 
jsp:getProperty:输出某个JavaBean的属性。 
jsp:forward:把请求转到一个新的页面。
jsp:plugin:根据浏览器类型为Java插件生成OBJECT或EMBED标记。

@面:标签库好处,自定义JSP标签

使用标签库的好处包括以下几个方面:
- 分离JSP页面的内容和逻辑,简化了Web开发;
- 开发者可以创建自定义标签来封装业务逻辑和显示逻辑;
- 标签具有很好的可移植性、可维护性和可重用性;
- 避免了对Scriptlet(小脚本)的使用

自定义标签步骤:
1)编写一个Java类实现实现Tag/BodyTag/IterationTag接口(开发中通常不直接实现这些接口而是继承TagSupport/BodyTagSupport/SimpleTagSupport类,这是对缺省适配模式的应用)
2)重写doStartTag()、doEndTag()等方法,定义标签要完成的功能
3)编写扩展名为tld的标签描述文件对自定义标签进行部署,tld文件通常放在WEB-INF文件夹下或其子目录中
4)在JSP页面中使用taglib指令引用该标签库

8.8 Listener ***

1、Listener ***它是 JavaWeb 的三大组件之一。JavaWeb 的三大组件分别是:Servlet 程序、Filter 过滤器、Listener ***。

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

3、***的作用是,监听某种事物的变化,然后通过回调函数,反馈给客户(程序)去做一些相应的处理。

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

两个方法分别是:

public interface ServletContextListener extends EventListener {
    
    /** * 在 ServletContext 对象创建之后马上调用,做初始化 */ 
    public void contextInitialized(ServletContextEvent sce); 
    /** * 在 ServletContext 对象销毁之后调用 */ 
    public void contextDestroyed(ServletContextEvent sce); 
}

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

使用步骤如下:

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

***实现类:

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 对象被销毁了"); 
    } 
}

web.xml 中的配置:

<!--配置***--> 
<listener> 
    <listener-class>
        com.atguigu.listener.MyServletContextListenerImpl
    </listener-class> 
</listener>

@面:***的理解

JavaWeb开发中的***就是application、session、request三个对象创建、销毁或者往其中添加修改删除属性时【自动执行代码的功能组件】,如下所示:
1)ServletContextListener:对Servlet上下文的创建和销毁进行监听。
2)ServletContextAttributeListener:监听Servlet上下文属性的添加、删除和替换。

3)HttpSessionListener:对Session的创建和销毁进行监听。
    session的销毁有两种情况:
    *session超时(可以在web.xml中通过<session-config>/<session-timeout>标签配置超时时间);
    *通过调用session对象的invalidate()方法使session失效。
4)HttpSessionAttributeListener:对Session对象中属性的添加、删除和替换进行监听。

5)ServletRequestListener:对请求对象的初始化和销毁进行监听。
6)ServletRequestAttributeListener:对请求对象属性的添加、删除和替换进行监听。

@面:JSP和Servlet相同点和不同点

JSP是Servlet技术的扩展,本质上是Servlet的简易方式,更强调应用的外表表达。JSP编译后是”类servlet”。

Servlet和JSP最主要的不同点在于,Servlet的应用逻辑是在Java文件中,并且完全从表示层中的HTML里分离开来。而JSP的情况是Java和HTML可以组合成一个扩展名为.jsp的文件。JSP侧重于视图,Servlet主要用于控制逻辑

九、EL 表达式

9.1 概念

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 字符串

9.2 搜索域数据的顺序

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

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

9.3 EL 表达式输出属性

Bean 的普通属性,数组属性。List 集合属性,map集合属性

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

9.4 运算

语法:${ 运算表达式 }

1)关系运算

2)逻辑运算

3)算数运算

i. empty 运算
**ii. 三元运算**
**iii. “.”点运算 和 [] 中括号运算符

9.5 11 个隐含对象

image-20210325200722873 image-20210325200752473

@面:表达式语言(EL)的隐式对象

EL的隐式对象包括:
pageContext、initParam(访问上下文参数)、param(访问请求参数)、paramValues、header(访问请求头)、headerValues、cookie(访问cookie)、applicationScope(访问application作用域)、sessionScope(访问session作用域)、requestScope(访问request作用域)、pageScope(访问page作用域)。

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

2、pageContext 对象的使用

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

3、EL 表达式其他隐含对象的使用

image-20210325232204653

输出请求参数 username 的值:${ param.username } <br> 
输出请求参数 password 的值:${ param.password } <br> 

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

image-20210325232251241

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

image-20210325232323242

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

image-20210325232352186

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

十、JSTL 标签库

10.1 概念

JSTL 标签库全称是指 JSP Standard Tag Library JSP标准标签库

EL 表达式主要是为了替换 jsp 中的表达式脚本,而**标签库则是为了替换代码脚本**。这样使得整个jsp页面变得更加简洁。

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

image-20210325232717350

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

CORE 标签库 :
<%@ taglib prefix=*"c"* uri=*"http://java.sun.com/jsp/jstl/core"* %>

10.2 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" %>

10.3 core 核心库使用

1、<c:set />(使用很少)

作用:set 标签可以往域中保存数据

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

2、<c:if />

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

3、 < c:choose> < c:when> < c:otherwise>

作用:**多路判断。**跟 switch … case … default 非常接近

<%-- 
    iii.<c:choose> <c:when> <c:otherwise>标签 
        作用:多路判断。跟 switch ... case .... default 非常接近 
        choose 标签开始选择判断 
        when 标签表示每一种判断情况 
            test 属性表示当前这种判断情况的值 
        otherwise 标签表示剩下的情况 
            
        <c:choose> <c:when> <c:otherwise>标签使用时需要注意的点: 
            1、标签里不能使用 html 注释,要使用 jsp 注释 
            2、when 标签的父标签一定要是 choose 标签 
--%> 
<% 
	request.setAttribute("height", 180); 
%>
<c:choose> 
    <%-- 这是 html 注释 --%> 
    <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>

4、<c:forEach />

作用:遍历输出使用。

  1. 遍历 1 到 10,输出
<%--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>
  1. 遍历 Object 数组
<%-- 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>
  1. 遍历 Map 集合
<% 
Map<String,Object> map = new HashMap<String, Object>(); 
map.put("key1", "value1"); 
map.put("key2", "value2"); 
map.put("key3", "value3"); 
request.setAttribute("map", map); 
%>
<c:forEach items="${ requestScope.map }" var="entry"> 
    <h1>${entry.key} = ${entry.value}</h1> 
</c:forEach>
  1. 遍历 List 集合—list 中存放 Student 类,有属性:编号,用户名,密码,年龄, 电话信息
public class Student {
    
    //4.编号,用户名,密码,年龄,电话信息 
    private Integer id; 
    private String username; 
    private String password; 
    private Integer age; 
    private String phone;
}
<%--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); 
%>
<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>

@面:使用过哪些JSTL标签

项目中主要使用了JSTL的核心标签库,包括<c:if>、<c:choose>、<c: when>、<c: otherwise>、<c:forEach>等,主要用于构造循环和分支结构以控制显示逻辑。

虽然JSTL标签库提供了core、sql、fmt、xml等标签库,但是实际开发中建议只使用核心标签库(core),而且最好只使用分支和循环标签并辅以表达式语言(EL),这样才能真正做到数据显示和业务逻辑的分离。

十一、Cookie

11.1 什么是Cookie

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

11.2 创建Cookie

image-20210325235023037
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 创建成功"); 
}

11.3 获取Cookie

服务器获取客户端的 Cookie 只需要一行代码:req.getCookies()

image-20210325235247616
Cookie 的工具类:
public class CookieUtils {
    
    /** * 查找指定名称的 Cookie 对象 */ 
    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"); 
    } 
}

11.4 Cookie 值的修改

// 方案一: 
// 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); 
}

11.5 浏览器查看

image-20210325235935126

11.6 生命控制

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

**setMaxAge()** 
- 正数,表示在指定的秒数后过期 
- 负数,表示浏览器一关,Cookie 就会被删除(默认值是-1) 
- 零,表示马上删除 Cookie
/** * 设置存活 1 个小时的 Cooie */ 
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 */ 
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 */ 
protected void defaultLife(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    Cookie cookie = new Cookie("de***tLife","defaultLife"); 
    cookie.setMaxAge(-1);//设置存活时间 
    resp.addCookie(cookie); 
}

11.7 有效路径 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 发送
protected void testPath(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
   
    Cookie cookie = new Cookie("path1", "path1"); 
    // getContextPath() ===>>>> 得到工程路径 
    cookie.setPath( req.getContextPath() + "/abc" ); // ===>>>> /工程路径/abc 
    resp.addCookie(cookie); 
    resp.getWriter().write("创建了一个带有 Path 路径的 Cookie"); 
}

11.8 练习-免输入用户名登录

image-20210326000744194
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("登录 失败"); 
    } 
}

@面:cookie 和 session 的区别

1.存储位置不同
Cookie 保存在客户端(浏览器)
Session 保存在服务器端

2.存储容量大小不同
cookie 存储的容量较小,单个Cookie保存的数据不能超过 4K
session 存储容量大小没有限制,但是为了服务器性能考虑,一般不能存放太多数据

3.存储有效期不同
cookie 可以长期存储,只要不超过设置的过期时间,可以一直存储
session 在超过一定的操作时间 (通常为30分钟) 后会失效,但是当关闭浏览器时,为了保护用户信息,会自动调用session.invalidate()方法,该方***清除掉session中的信息

4.安全性不同
cookie存储在客户端,所以可以分析存放在本地的cookie并进行cookie欺骗 (CSRF攻击) ,安全性较低
session 存储在服务器上,不存在敏感信息泄漏的风险,安全性较高

5.域支持范围不同
cookie 支持跨域名访问。例如,所有a.com的cookie在a.com下都能用
session 不支持跨域名访问。例如,www.a.com的session在api.a.com下不能用

6.对服务器压力不同
cookie 保存在客户端,不占用服务器资源
session 是保存在服务器端,每个用户都会产生一个session ,session过多的时候会消耗服务器资源,所以大型网站会有专门的session服务器

7.存储的数据类型不同
cookie 中只能保管ASCII字符串,并需要通过编码方式存储为Unicode字符或者二进制数据
Session 可以存任意数据类型,一般情况下我们可以在Session中保持一些常用变量信息,比如说UserId等

@面:cookie 和 session 的关联

image-20210418111455965
用户第一次请求服务器的时候,服务器根据用户提交的相关信息,创建创建对应的Session,请求返回时将此Session的唯一标识信息SessionID返回给浏览器,浏览器接收到服务器返回的SessionID信息后,会将此信息存入到Cookie中,同时Cookie记录此SessionID属于哪个域名。
当用户第二次访问服务器的时候,请求会自动判断此域名下是否存在Cookie信息,如果存在自动将Cookie信息也发送给服务端,服务端会从Cookie中获取SessionID,再根据SessionID查找对应的Session信息,如果没有找到说明用户没有登录或者登录失效,如果找到Session证明用户已经登录可执行后面操作。
根据以上流程可知,SessionID是连接Cookie和Session的一道桥梁,大部分系统也是根据此原理来验证用户登录状态

十二、Session会话

12.1 什么是 Session 会话?

1、Session 就一个接口(HttpSession)。 
2、Session 就是会话。它是**用来维护一个客户端和服务器之间关联的一种技术。** 
3、**每个客户端都有自己的一个 Session 会话。** 
4、Session 会话中,我们经常**用来保存用户登录之后的信息。**

12.2 创建 Session 和获取

request.getSession()

- 第一次调用是:创建 Session 会话 
- 之后调用都是:获取前面创建好的 Session 会话对象。 

isNew(); 判断到底是不是刚创建出来的

- true 表示刚创建 
- false 表示获取之前创建 

**每个会话都有一个身份证号。**也就是 ID 值。而且这个 ID 是唯一的。

getId() 得到 Session 的会话 id 值。

12.3 Session 域数据的存取

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

/** * 获取 Session 域中的数据 */ 
protected void getAttribute(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    Object attribute = req.getSession().getAttribute("key1"); 
    resp.getWriter().write("从 Session 中获取出 key1 的数据是:" + attribute); 
}

12.4 生命周期控制

public void setMaxInactiveInterval(int interval) 
    设置Session的超时时间(以秒为单位),超过指定的时长,Session就会被销毁。
    值为正数的时候,设定 Session 的超时时长。 
    负数表示永不超时(极少使用) 
    
public int getMaxInactiveInterval()
    获取Session的超时时间 
    
public void invalidate() 
    让当前 Session 会话马上超时无效。
    
Session 默认的超时时间长为 30 分钟。
<!--表示当前web工程。创建出来的所有Session默认是20分钟超时时长--> 
<session-config> 
    <session-timeout>20</session-timeout> 
</session-config>

session.setMaxInactiveInterval(int interval)单独设置超时时长。
image-20210326085332116
protected void life3(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    // 先获取 Session 对象 
    HttpSession session = req.getSession(); 
    // 设置当前 Session3 秒后超时 
    session.setMaxInactiveInterval(3); 
    resp.getWriter().write("当前 Session 已经设置为 3 秒后超时"); 
}

12.5 浏览器和Session的关联

Session 技术,底层其实是基于Cookie技术来实现的。

image-20210326085542914

@面:四种会话跟踪技术分别是

会话跟踪:主要**解决HTTP的无状态问题**,即:当用户发出请求时,服务器就会做出响应,客户端与服务器之间的联系是离散的、非连续的。当用户在同一网站的多个页面之间转换时,根本**无法确定是否是同一个客户,**会话跟踪技术就可以解决这个问题。当一个客户在**多个页面间切换时,服务器会保存该用户的信息。**

**1.Cookie:**
可以使用 cookie 存储购物会话的 ID;在后续连接中,取出当前的会话 ID,并**使用这个 ID 从服务器上的查找表中提取出会话的相关信息**。 以这种方式使用 cookie 是一种绝佳的解决方案,也是在处理会话时最常使用的方式。
但是,sevlet 中最好有一种高级的 API 来处理所有这些任务,以及下面这些冗长乏味的任务:从众多的其他cookie中提取出存储会话标识符的 cookie;确定空闲会话什么时候过期,并回收它们;将散列表与每个请求关联起来;生成惟一的会话标识符.

**2.URL重写:**
采用这种方式时,客户程序**在每个URL的尾部添加一些额外数据。**这些数据标识当前的会话,服务器将这个标识符与它存储的用户相关数据关联起来。即使浏览器不支持 cookie 或在用户禁用 cookie 的情况下,这种方案也能够工作。
URL 重写具有 cookie 所具有的同样缺点,也就是说,服务器端程序要做许多简单但是冗长乏味的处理任务。即使有高层的 API 可以处理大部分的细节,仍须十分小心每个引用你的站点的 URL ,以及那些返回给用户的 URL。即使通过间接手段,比如服务器重定向中的 Location 字段,都要添加额外的信息。这种限制意味着,在你的站点上不能有任何静态 HTML 页面。因此,每个页面都必须使用 servlet 或 JSP 动态生成。即使所有的页面都动态生成,如果用户离开了会话并通过书签或链接再次回来,会话的信息也会丢失,因为存储下来的链接含有错误的标识信息。

**3.隐藏的表单域:**
HTML 表单中可以含有如下的条目:< input type="hidden" name="session" value="a1234">
这个条目的意思是:在**提交表单时**,要**将指定的名称和值自动包括在 GET 或 POST 数据中**。这个隐藏域**可以用来存储有关会话的信息。**

但它的主要缺点是:仅当每个页面都是由表单提交而动态生成时,才能使用这种方法。单击常规的超文本链接并不产生表单提交,因此隐藏的表单域不能支持通常的会话跟踪,只能用于一系列特定的操作中,比如在线商店的结账过程。

**4.session:**
 信息保存在服务器端

十三、Filter 过滤器

13.1 什么是过滤器

1、Filter 过滤器它是 JavaEE 的规范。也就是接口 
2、Filter 过滤器它的作用是:**拦截请求,过滤响应。**
拦截请求常见的应用场景有: 1、权限检查 2、日记操作 3、事务管理   …… 等等

@面:过滤器作用以及过滤器的用法

对Web应用来说,过滤器是一个**驻留在服务器端的Web组件**,它可以**截取客户端和服务器之间的请求与响应信息**,并对这些信息进行过滤。

当Web容器接受到一个对资源的请求时,它将判断**是否有过滤器与这个资源相关联**。如果有,那么容器将把请求交给过滤器进行处理。在过滤器中,你可以改变请求的内容,或者重新设置请求的报头信息,然后再将请求发送给目标资源。当目标资源对请求作出响应时候,容器同样会将响应先转发给过滤器,在过滤器中你可以对响应的内容进行转换,然后再将响应发送到客户端。

**常见的过滤器用途**主要包括:对用户请求进行统一认证、对用户的访问请求进行记录和审核、对用户发送的数据进行过滤或替换、转换图象格式、对响应内容进行压缩以减少传输量、对请求或响应进行加解密处理、触发资源访问事件、对XML的输出应用XSLT等。
**和过滤器相关的接口**主要有:Filter、FilterConfig和FilterChain。

13.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; 
    } 
%>

Filter 的工作流程图:

image-20210326090049424

Filter 过滤器的使用步骤:

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

2、实现过滤方法doFilter()

3、到 web.xml 中去配置 Filter 的拦截路径

public class AdminFilter implements Filter {
    
    /** * doFilter 方法,专门用于拦截请求。可以做权限检查 */ 
    @Override 
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; 
        HttpSession session = httpServletRequest.getSession(); 
        Object user = session.getAttribute("user"); 
        // 如果等于 null,说明还没有登录 
        if (user == null) {
    		 
            servletRequest.getRequestDispatcher("/login.jsp")
            				.forward(servletRequest,servletResponse); 
            return; 
        } else {
    
            // 让程序继续往下访问用户的目标资源 
            filterChain.doFilter(servletRequest,servletResponse); 
        } 
    } 
}
<!--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>

13.3 生命周期

Filter的生命周期包含几个方法 
1、构造器方法 
2、init初始化方法 
    第 12 步,在web工程启动的时候执行(Filter已经创建) 
3、doFilter过滤方法 
    第 3 步,每次拦截到请求,就会执行 
4、destroy销毁 
    第 4 步,停止web工程的时候,就会执行(停止web工程,也会销毁Filter过滤器)

13.4 FilterConfig 类

FilterConfig 类见名知义,它是 Filter 过滤器的配置文件类。

Tomcat 每次创建 Filter 的时候,也会同时创建一个 FilterConfig 类,这里包含了 Filter 配置文件的配置信息。

FilterConfig 类的作用是获取filter过滤器的配置内容

1、获取 Filter 的名称 filter-name 的内容 
2、获取在 Filter 中配置的 init-param 初始化参数 
3、获取 ServletContext 对象

13.5 过滤器链

多个过滤器如何一起工作

image-20210326090958307

13.6 拦截路径

--精确匹配 
<url-pattern>/target.jsp</url-pattern> 
以上配置的路径,表示请求地址必须为:http://ip:port/工程路径/target.jsp 

--目录匹配 
<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 过滤器它只关心请求的地址是否匹配,不关心请求的资源是否存在!

十四、JSON

14.1 什么是 JSON

JSON (JavaScript Object Notation) 是一种**轻量级的数据交换格式**。易于人阅读和编写,同时也易于机器解析和生成。JSON 采用完全独立于语言的文本格式,而且很多语言都提供了对 json 的支持,这样就使得 JSON 成为理想的数据交换格式。

json 是一种轻量级的数据交换格式。 轻量级指的是跟 xml 做比较。 **数据交换指的是客户端和服务器之间业务数据的传递格式。**

14.2 JSON 在 JavaScript 中的使用

1、json 的定义

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

2、json 的访问

json 本身是一个对象。

json 中的 key 可以理解为是对象中的一个属性。

json 中的 key 访问就跟访问对象的属性一样: json 对象.key

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

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

14.3 JSON 在java中的使用

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

2、List 和 json 的互转

@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()); 
    System.out.println(list); 
    Person person = list.get(0); 
    System.out.println(person); 
}

3、map 和 json 的互转

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

十五、AJAX 请求

15.1 什么是 AJAX 请求

AJAX 即“Asynchronous Javascript And XML”(异步 JavaScript 和 XML),是指**一种创建交互式网页应用的网页开发技术。**
ajax 是一种浏览器通过 js 异步发起请求,**局部更新页面**的技术。 
Ajax 请求的局部更新,**浏览器地址栏不会发生变化**局部更新不会舍弃原来页面的内容

15.2 原生 AJAX 请求

<html> 
    <head> 
        <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) {
      var jsonObj = JSON.parse(xmlhttprequest.responseText); // 把响应的数据显示在页面上  document.getElementById("div01").innerHTML = "编号:" + jsonObj.id + " , 姓名:" + jsonObj.name; } } // 3、调用 send 方法发送请求  xmlhttprequest.send(); } </script> 
    </head> 
    <body> 
        <button onclick="ajaxRequest()">ajax request</button> 
        <div id="div01"> </div> 
    </body> 
</html>

15.3 jQuery 中的 AJAX 请求

1、$.ajax方法:

url 表示请求的地址 
type 表示请求的类型GET或POST请求 
data 表示发送给服务器的数据
	格式有两种: 
    name=value&name=value 
    {key:value} 
success 请求成功,响应的回调函数 
dataType 响应的数据类型 常用的数据类型有: 
    text 表示纯文本 
    xml 表示xml数据 
    json 表示json对象
$("#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"
    });
});
2、$.get 方法和$.post 方法 

url 请求的url地址 
data 发送的数据 
callback 成功的回调函数 
type 返回的数据类型
// 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");
});
3、$.getJSON 方法 

url 请求的 url 地址 
data 发送给服务器的数据 
callback 成功的回调函数
$("#getJSONBtn").click(function(){
    $.getJSON("http://localhost:8080/16_json_ajax_i18n/ajaxServlet","action=jQueryGetJSON",function (data) {
        $("#msg").html("getJSON 编号:" + data.id + " , 姓名:" + data.name);
    });
});
4、表单序列化 serialize() 
serialize()可以把表单中所有表单项的内容都获取到,并以name=value&name=value的形式进行拼接。
$("#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);
    });
});

十六、文件的上传和下载

16.1 上传介绍

1、要有一个 form 标签,method=post 请求

2、form 标签的 encType 属性值必须为 multipart/form-data 值

3、在 form 标签中使用 input type=file 添加上传的文件

4、编写服务器代码(Servlet 程序)接收,处理上传的数据。

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

16.2 上传HTTP 协议

image-20210326101420149

16.3 commons-fileupload.jar常用 API

commons-fileupload.jar 需要依赖 commons-io.jar 这个包,所以两个包我们都要引入。

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

包中常用的类

ServletFileUpload 类,用于解析上传的数据。 
FileItem 类,表示每一个表单项。 
    
boolean ServletFileUpload.isMultipartContent(HttpServletRequest request); 
	判断当前上传的数据格式是否是多段的格式。 
public List<FileItem> parseRequest(HttpServletRequest request) 
	解析上传的数据 
boolean FileItem.isFormField() 
	判断当前这个表单项,是否是普通的表单项。还是上传的文件类型。 
	true 表示普通类型的表单项 
	false 表示上传的文件类型 
String FileItem.getFieldName() 
	获取表单项的 name 属性值
String FileItem.getString() 
	获取当前表单项的值。 
String FileItem.getName(); 
	获取上传的文件名 
void FileItem.write( file );
	将上传的文件写到 参数 file 所指向抽硬盘位置 。

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

16.5 文件下载

下载的常用 API 说明:

response.getOutputStream();

servletContext.getResourceAsStream();

servletContext.getMimeType();

response.setContentType();

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

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

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

原因是在响应头中,不能包含有中文字符,只能包含 ASCII 码。

@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= 表示指定下载的文件名 
    resp.setHeader("Content-Disposition", "attachment; filename=" + downloadFileName); 
    /** * /斜杠被服务器解析表示地址为 http://ip:prot/工程名/ 映射 到代码的 Web 目录 */ 
    InputStream resourceAsStream = servletContext.getResourceAsStream("/file/" + downloadFileName); 
    // 获取响应的输出流 
    OutputStream outputStream = resp.getOutputStream(); 
    // 3、把下载的文件内容回传给客户端 
    // 读取输入流中全部的数据,复制给输出流,输出给客户端 
    IOUtils.copy(resourceAsStream,outputStream); 
}

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


@面:什么情况下回使用assert

assertion断言在软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制。
在实现中,assertion就是在程序中的一条语句,它对一个boolean表达式进行检查,一个正确程序必须保证这个boolean表达式的值为true;如果该值为false,说明程序已经处于不正确的状态下,系统将给出警告或退出。
一般来说,assertion用于保证程序最基本、关键的正确性。
assertion检查通常在开发和测试时开启,为了提高性能,在软件发布后,assertion检查通常是关闭的。

@面:在链接里不输入项目名称的情况下启动项目

可在taomcat配置虚拟目录。

@面:什么是DAO模式

DAO是一个为数据库或其他持久化机制提供了抽象接口的对象,在不暴露底层持久化方案实现细节的前提下提供了各种数据访问操作。
在实际的开发中,应该将所有对数据源的访问操作进行抽象化后封装在一个公共API中,就是建立一个接口,接口中定义了此应用程序中将会用到的所有事务方法。
在应用程序中,当需要和数据源进行交互的时候则使用这个接口,并且编写一个单独的类来实现这个接口,在逻辑上该类对应一个特定的数据存储。
DAO模式实际上包含了两个模式,一是Data Accessor(数据访问器),二是Data Object(数据对象),前者要解决如何访问数据的问题,而后者要解决的是如何用对象封装数据。

@面:MVC的各个部分都有那些技术来实现

Model代表的是应用的业务逻辑(通过JavaBean,EJB组件实现),View是应用的表示面,用于与用户的交互(由JSP页面产生),Controller是提供应用的处理过程控制(一般是一个Servlet)
通过这种设计模型把应用逻辑,处理过程和显示逻辑分成不同的组件实现,这些组件可以进行交互和重用。
model层实现系统中的业务逻辑,view层用于与用户的交互,controller层是model与view之间沟通的桥梁,可以分派用户的请求并选择恰当的视图以用于显示,同时它也可以解释用户的输入并将它们映射为模型层可执行的操作。