文章目录
- WEB开发
- 一、HTML
- 二、CSS
- 三、JavaScript
- 四、AJAX
- 五、VUE
- 六、ElementVue脚手架
- 七、web
- 八.tomcat
- 九、Servlet
- 十.HTTP协议
- 十一.Request & Response
- 十二.Cookie
- 十三.Session
- 十四.Filter
- 十五.ServletContext
- 十六.Listener
- 十七.git
WEB开发
一、HTML
1.html介绍
- html:超文本标记语言
- 超文本:超越了文本的限制,比普通文本更强大。除了文字信息,还可以定义图片、音频、视频等内容。
- 标记语言:由标签构成的语言
- 网站和网页的区别
- 网站=网页+后端(java+数据库)
- 网页:显示给用户
- HTML运行在浏览器上,HTML标签由浏览器来解析,不需要编译。
- HTML标签都是预定义好的。例如:使用 展示图片
- 在任何地方都可以创建html文件,文件后缀名是.html或者.htm
- W3C标准:
- 结构:HTML
- 表现:CSS
- 行为:JavaScript
- HTML结构标签
标签 | 描述 |
---|---|
HTML | 定义HTML文档 |
head | 定义关于文档的信息 |
title | 定义文档的标题 |
body | 定义文档的主体 |
- HTML基础标签
标签 | 描述 |
---|---|
h1 - h6 | 定义标题,h1最大,h6最小 |
font | 定义文本的字体(face)、字体尺寸(size)、字体颜色(color) |
b | 定义粗体文本 |
i | 定义斜体文本 |
u | 定义文本下划线 |
center | 定义文本居中 |
p | 定义段落 |
br | 定义换行 |
hr | 定义水平线 |
img | 定义图片 |
audio | 定义音频 |
video | 定义视频 |
ol | 定义有序列表 |
ul | 定义无序列表 |
li | 定义列表项 |
table | 定义表格 |
border:规定表格边框的宽度 | |
width:规定表格的宽度 | |
cellspacing:规定单元格之间的空白 | |
tr | 定义行 align:定义表格的内容对齐方式 |
td | 定义单元格 |
rowspan:规定单元格可横跨的行数 | |
colspan:规定单元格可横跨的列数 | |
div | 定义HTML文档中的一个区域部分,经常与CSS一起使用,用来布局网页 |
span | 用于组合行内元素 |
form | 定义表单 |
input | 定义表单项,通过type属性控制输入形式 |
label | 为表单项定义标注 |
select | 定义下拉列表 |
option | 定义下拉列表的列表项 |
textarea | 定义文本域 |
input | type:text、password、radio、checkbox、file、hidden、submit、reset、button |
- form表单
常见属性:
action:表示将收集的数据提交到具体后台服务器的地址
method:提交数据到后台的方式(请求方式)
get:http://www.jd.com?username=zhang&password=123(默认)
不安全;有大小限制;只能传输字符数据;
post:http://www.jd.com
安全;有大小限制;可以传输任意数据
二、CSS
1.CSS介绍
- CSS(Cascading Style Sheet)层叠样式表,是一门语言,用于控制网页表现
2.CSS导入方式
- 内联样式:在标签内部使用style属性,属性值是css属性键值对
<div style="color:red">Hello CSS</div>
- 内部样式:定义
<style type="text/css">
div{
color:red;
}
</style>
- 外部样式:定义link标签,引入外部的css文件
<link rel="stylesheet" href="demo.css">
3.CSS选择器
- 元素选择器
<!--元素名称{color:red;}-->
div{color:red;}
- id选择器
<!--#id属性值{color:red;}-->
#name{color:red;}
<div id="name">hello css</div>
- 类选择器
<!--.class属性值{color:red;}-->
.cls{color:red;}
<div class="cls">helo css</div>
4.CSS属性
https://www.w3school.com.cn/cssref/index.asp
三、JavaScript
1.介绍
- JavaScript是一门跨平台、面向对象的脚本语言,来控制网页行为的,它能使网页可交互;
- JavaScript和Java是完全不同的语言,不论是概念还是设计。但是基础语法类似;
- JavaScript插入HTML页面后,可由所有的现代化浏览器执行。JavaScript语言不需要编译,直接由各大浏览器解析运行。学习JavaScript语言不需要单独安装软件,只需要浏览器即可。
2.浏览器
浏览器分成两部分:渲染引擎和JS引擎
- 渲染引擎:用来解析HTML和CSS,俗称内核,例如chrome浏览器的blink
- JS引擎:也成为JS解释器。用来读取网页中的JS代码,对其处理后运行,例如chrome浏览器的V8
3.js的三种输出方式
- 使用 window.alert() 写入警告框
- 使用 document.write() 写入HTML输出
- 使用 console.log 写入浏览器控制台
4.js定义变量
- 定义变量: let 变量名 = 值;
- 定义常量: const 常量名 = 值;(不能改变)
var i= 10;
let x="haha";
let y = 3.14;
//定义常量,不能改变
const PI=3.14;
5.js数据类型
- number:数字(整数、小数、NaN)
- string:字符、字符串、单双引皆可
- boolean:布尔,true、false
- null:对象为空
- undefined:当声明的变量未初始化时,该变量的默认值是undefined
- 引用数据类型:date、Object
6.js运算符
- 一元运算符:++,–
- 算术运算符:+,-,*,/,%
- 赋值运算符:=,+=,-=
- 关系运算符:>,<,>=,<=,!=, == ,===
==:非严格比较,具有强制类型转换功能,如果两侧的数据类型不一致,转换一样的类型,然后比较数值是否相等;
===:严格比较,如果运算符两侧类型不一致,返回false,如果类型一致,在比较数值是否相等;
- 逻辑运算符:&&、||、!
- 三元运算符:条件表达式?true_value : false_value
在js中有六种假:false;0;空字 符’'、“”;NaN、null、undefined
7.全局函数
- 其他类型转为数字:
- string:将字符串字面值转为数字。如果字面值不是数字,则转为NaN,一般使用parseInt()方法进行转换
- boolean:true转为1,false转为0,一般使用Number()方法进行转换
- 其他类型转为boolean,使用Boolean(value)函数
- number:0和NaN转为false,其他的数字转为true
- string:空字符串转为false,其他字符串转为true
- null:转为false
- undefined:转为false
8.函数定义
- 格式一
function add(a,b){
console.log(a+"---"+b)
//返回
return a+b;
}
//调用
let sum = add(10,20)
console.log(sum)
- 格式二
let add = function(a,b){
return a + b;
}
//调用
let result = add(1,2);
- 注意事项
- 匿名函数还可以作为另一个函数的参数传递
- 在js中没有函数重载概念,如果存在函数名一样的函数,后出现的函数就会覆盖之前的函数名
- 在js中调用无参函数可以传递实参,数据没有丢失会放到js的一个内置数组对象中 arguments;调用有参函数可以不传递值;
9.js数组对象
- 数组定义方式一:
- 采用方式一定义数值时,如果只给一个number类型的值,那么此时数值表示数组长度,数组中的数据都是empty;
- 采用方式一定义数值时,如果只给一个number类型的值,那么此时数值表示数组长度,要求不能给小数,数组长度不能是小数;
- 采用方式一定义数值时,如果只给一个非number类型的值,那么此时数值表示数组的元素;
- js中的数组长度是可变的;
- 数组定义方式二:
- let arr = [10] 表示数组元素为10,长度为1;
//定义
let 变量名 = new Array(元素列表); //方式一
let arr = new Array(1,2,3);
let 变量名 = [元素列表]; //方式二
let arr = [1,2,3];
//访问
arr[索引] = 值;
arr[0] = 1
- 数组对象属性和函数:
- length:返回数组中元素的个数即数组长度
- push():向数组中末尾添加一个或多个元素,返回新的长度
- splice(index,n):从数组中删除元素;index表示从哪个索引删除,n表示删除数据个数
10.js正则对象
- 概念:正则表达式定义了字符串组成的规则
- 定义:
//1.直接量:注意不要加引号
// var reg = /^正则表达式符号$/
//2.创建RegExp对象
var reg = new RegExp("^正则表达式符号$");
- 方法:test(str):判断指定字符串是否符合规则,返回true或false
- 语法:
符合 | 含义 |
---|---|
^ | 表示开始 |
$ | 表示结束 |
[] | 代表某个范围内的单个字符,比如:[0-9]单个数字字符 |
. | 代表任意单个字符,除了换行和行结束符 |
\w | 代表单词字符:字母、数字、下划线(_),相当于[A-Za-z0-9] |
\d | 代表数字字符:相当于[0-9] |
+ | 至少一个 |
* | 零个或多个 |
? | 零个或一个 |
{x} | x个 |
{m,} | 至少m个 |
{m,n} | 至少m个,至多n个 |
11.字符串对象
- 定义
//方式一: let 变量名 = new String(s);
let str = new String("hello");
//方式二:let 变量名 = s;
let str = "hello";
let str = 'hello';
- 属性:
- length:字符串的长度
- 方法:
- charAt() :返回在指定位置的字符
- IndexOf() :检索字符串,查找字符串在指定字符串中存在的索引,如果不存在返回-1;
12.自定义对象
let 对象名称 = {
属性名称1:属性值1,
属性名称2:属性值2,
...
函数名称:function(形参列表){}
...
};
//例:
let person = {
name:"zhangsan",
age:23,
eat:function(){
alert("干饭");
}
};
let person={
//属性名:属性值
name:"liuyan",
address:"hunan",
age:18,
eat:function(){
//TODO:如果在自定义对象中使用当前自定义对象中的其他属性或者函数,格式:
//this.属性名或this.函数名(实参);
//this表示当前对象即person
console.log("ganfanren,ganfanhun"+this.address); //柳岩
//this表示当前对象即person
console.log("person.age"); //18
//调用函数 对象名.函数名(实参);
person.eat();
}
}
13.BOM浏览器对象模型
组成:windows:浏览器窗口对象;Navigator:浏览器对象;Screen:屏幕对象;History:历史记录对象;Location:地址栏对象
- windows:浏览器窗口对象
直接使用window调用window对象中的属性和函数,其中window.可以省略
window.alert(“abc”)
属性和方法 | 解释 |
---|---|
history | 对History对象的只读引用 |
Navigator | 对Navigator对象的只读引用 |
Screen | 对Screen对象的只读引用 |
location | 用于窗口或框架的Location对象 |
alert() | 显示带有一段消息和一个确认按钮的警告框 |
confirm() | 显示带有一段消息以及确认按钮和取消按钮的对话框 |
setInterval() | 按照指定的周期(以毫秒计)来调用函数或计算表达式 |
setTimeout() | 在指定的毫秒数后调用函数或计算表达式,只会执行一次 |
14.DOM对象
(1)Document Object Model文档对象模型
(2)将标记语言的各个组成部分封装为对象:
- img标签 ==> Image对象
- Document:整个文档对象
- Element:元素对象
- Attribute:属性对象
- Text:文本对象
- 所有标签的父对象是Element
(3)JavaScript通过DOM,就能够对HTML进行操作
- 改变HTML元素内容
- 改变HTML元素的样式(CSS)
- 对HTML DOM事件作出反应
- 添加和删除HTML元素
(4)Element:元素对象
- 获取:使用Document对象的方法来获取
- getElementById:根据id属性值获取,返回一个Element对象
- getElementsByTagName:根据标签名获取,返回Element对象数组
- getElementsByName:根据name属性值获取,返回Element对象数组
- getElementsByClassName:根据class属性值获取,返回Element对象数组
(5)事件绑定
- 方式一:通过HTML标签中的事件属性进行绑定
<input type="button" onclick='on()'>
function on(){
alert("我被点了");
}
- 方式二:通过DOM元素属性绑定
<input type="buton" id = "btn">
//只有是标签对象才可以绑定事件,如果是数组不能直接绑定,需要遍历数组取出
//每个标签对象在绑定事件
document.getElementId("btn").onclick = function (){
alert("我被点了");
}
(6)常见事件
事件名 | 说明 |
---|---|
onclick | 鼠标单击事件 |
onblur | 元素失去焦点 |
onfocus | 元素获得焦点 |
onload | 某个页面或图形被完成加载 |
onsubmit | 当表单提交时触发该事件 |
onkeydown | 某个键盘的键被按下 |
onmouseover | 鼠标被移到某元素之上 |
onmouseout | 鼠标从某元素移开 |
四、AJAX
1.es6
es6.ruanyifeng.com
- 变量声明 let
- 常量声明 const
- 模板字符串
let username="Rose";
//es5
console.log('姓名是'+username);
//es6,使用反引号,可以直接打印变量的值,表达式类似于java的el表达式
console.log(`姓名是:${username}`);
- 函数的参数默认值
在js中如果定义的函数有参数,调用的时候可以不传递实参,那么形参变量名就是undefined,为了解决这个问题,es6后引入函数参数默认值。如果调用函数时不传递实参,那么使用默认值,如果传递实参那么使用实参。
//es6前
function show(username){
console.log(username);
}
show(); //undefined
//es6
function show(username='Jack'){ //默认值Jack
console.log(username);
}
show();
- 箭头函数
//es5
let add = function(a,b){
return a+b;
}
//es6
let add2=(a,b) =>{
retturn a+b;
}
//如果函数只有一句话,可以省略大括号和return
var add3=(a,b) => a+b;
//如果只有一个参数 省略小括号
var add4 = a => a+10;
2.Asynchronous JavaScript and XML
(1)异步的JavaScript和XML;
(2)ajax就是一项通过js技术发送异步请求的技术。异步请求,局部更新,不是整个网页更新,可以完成前端和后台服务器的数据交互。
- 说明:
异步:就是不同步。例如我们向后台发送请求,同步的方式是后台必须返回响应数据才可以在浏览器上进行下一步操作,而异步方式可以在不需要等待后台服务器响应数据,之间可以进行其他操作。
- Ajax不是新的编程语言,而是一种使用现有标准的新方法;
- Ajax是与服务器交换数据并更新部分网页的技术,在不重新加载整个页面的情况下。
(3)同步请求存在的问题:
- 阻塞:请求发出后必须得等到响应结束后才能操作页面信息。
- 全部更新:整个页面更新。
(4)异步请求好处:
- 非阻塞:请求发出后不用等到响应结束才能操作页面信息。随时可以操作。
- 局部更新:页面的局部位置更新
3.交互模型
- 传统交互模型:
浏览器客户端向服务器直接发送请求数据,然后后台服务器接收到请求,处理请求数据,期间浏览器客户端只能等待服务器处理数据,并响应数据,最后服务器将响应数据响应给浏览器客户端,浏览器接收到响应之后才可以继续下一步操作。
- AJAX的交互模型:
浏览器内部多了一个ajax引擎,浏览器客户端向服务器发送请求的数据,都是先由浏览器将请求数据交给ajax引擎,注意,ajax引擎位于浏览器内部,是由js编写的一个软件。然后接下来都是由ajax引擎和服务器进行交互,此时用户可以在浏览器上进行其他操作,如果再次向服务器发送请求,那么依然是交给ajax引擎处理,并且服务器响应的数据也是交给ajax引擎处理,由ajax引擎来分配浏览器的操作。
注意:ajax引擎内部具有一个核心对象:XMLHttpResquest。都是由该对象进行异步请求交互数据的。new XMLHttpRequest() 启用ajax引擎。
4.axios
说明网站(https://www.kancloud.cn/yunye/axios/234845)
- 原生ajax请求的代码编写太过繁琐,我们可以使用axios这个库来简化操作!
- 使用步骤
1.引入axios核心js文件
<!--
TODO:引入axios核心js文件
-->
<script src="js/axios-0.18.0.js"></script>
2.使用axios对象调用方法来发起异步请求。
3.使用axios对象调用方法来处理响应的数据。
- axios常用方法
方法名 | 作用 |
---|---|
get(请求的资源路径与请求的参数) | 发起GET方式请求 |
post(请求的资源路径请求的参数) | 发起POST方式请求 |
then(response) | 请求成功后的回调函数,通过response获取响应的数据 |
catch(error) | 请求失败后的回调函数,通过error获取错误信息 |
**回调函数:回过头来调用的函数,回调函数都是我们负责书写,不负责调用,都是底层帮助我们调用。**例: setInterval(function(){},1000);
//备注: then函数的参数response是一个json对象,
//我们重点只需要了解response.data即可
{
// `data` 由服务器提供的响应 (重要!) response.data
data: {},
// `status` 来自服务器响应的 HTTP 状态码 response.status
status: 200,
// `statusText` 来自服务器响应的 HTTP 状态信息
statusText: 'OK',
// `headers` 服务器响应的头
headers: {},
// `config` 是为请求提供的配置信息
config: {}
}
- 代码实现
- get请求
- axios使用说明
- get请求
/*
* # axios的api介绍
* 1. get(参数) 建立和服务器的连接
* get参数 : 是请求地址+请求参数 ---url?key=value&key=value...
* 2. then(fn) 处理服务器响应
* fn(response) : 响应成功的回调函数
* response.data : 响应体数据
*
* 3. catch(fn)
* fn(error) : 响应失败的的回调函数
* error : 错误信息
*
* 4. finally(fn)
* fn : 响应结束的回调函数(无论响应成功与否,都会执行)
* */
整体代码,链式编程:
// /AjaxServlet 表示后台服务器地址 url
// name=zs&age=18 表示向后台提交的数据,get请求数据位于url后面,携带数据格式:url?key=value&key=value
axios.get("/AjaxServlet?name=zs&age=18")
.then(function (response) {
// 回调函数的函数体,处理后台服务器的响应的代码,所有的数据都放到response对象中了,注意response只是一个对象标识符,名字随便定义,我们只负责编写匿名函数体代码
})
.catch(function (error) {
// 回调函数的函数体,如果后台服务器出现异常就在这里处理,所有的错误信息放到error对象中
};)
.finally(function () {
// 回调函数的函数体,必须执行的代码
};);
=================使用es6的箭头函数简================================================
()=>{}
axios.get("/AjaxServlet?name=zs&age=18")
.then(response=>{
// 回调函数的函数体,处理后台服务器的响应的代码,所有的数据都放到response对象中了,注意response只是一个对象标识符,名字随便定义,我们只负责编写匿名函数体代码
})
.catch(error=>{
// 回调函数的函数体,如果后台服务器出现异常就在这里处理,所有的错误信息放到error对象中
};)
.finally(() => {
// 回调函数的函数体,必须执行的代码
};);
代码实现:
步骤:
1.导入axios的js库到当前项目webapp文件夹下
2.创建html页面
3.在html页面中导入axios的js库
4.在html页面向后台发送axios的ajax异步请求
5.创建Servlet,接收请求数据,处理业务逻辑和响应数据
6.在html页面中处理服务器的响应数据
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>使用axios发送异步请求</title>
</head>
<body>
<!-- 不能在文本中书写js代码-->
<script type="text/javascript" src="js/axios-0.18.0.js"></script>
<script type="text/javascript">
//1.使用axios对象调用函数向后台服务器发送ajax异步请求
/*
整体: axios.get().then().catch().finally();
1)get()函数表示两后台服务器发送get请求,格式:
get(url?key=value&key=value...);===axios.get("/axiosDemo01Servlet?username=锁哥&password=1234")
2)then() 处理后台服务器响应的,格式:
then(function(接收响应数据的对象名){
处理响应的代码
});
then(function (resp){
console.log(resp);
})
后台响应数据:
resp={
data: 'axios实现ajax异步get请求,username=锁哥',
status: 200,
statusText: '',
headers: {…},
config: {…},
}
resp.data就可以获取 数据: axios实现ajax异步get请求,username=锁哥
3)catch() :处理异常
catch(function (error) {
console.log(error);
})
let person ={
username:"锁哥",
age:18
}
小结:
1.get函数:建立和服务器的连接,在参数中书写连接服务器的url和请求参数
2.then函数:书写处理响应数据的,在该函数的参数位置书写回调函数
3.catch函数:书写处理响应错误信息数据的,在该函数的参数位置书写回调函数
4.finally函数:无论响应成功还是失败都要执行的代码,在该函数的参数位置书写回调函数
*/
axios.get("http://localhost:8080/axiosDemo01Servlet?username=锁哥&password=1234")
.then(function (response) {
//处理响应数据的回调函数体代码,response表示接受服务器响应数据的对象,该对象中具有重要属性是data
console.log(response);
console.log(response.data);
})
.catch(function (error) {
//书写处理响应错误信息数据的,在该函数的参数位置书写回调函数
console.log(error);
})
.finally(function () {
//无论响应成功还是失败都要执行的代码,在该函数的参数位置书写回调函数
console.log('我是必须执行的...');
});
</script>
</body>
</html>
- axios发送异步的请求步骤:
【1】导入库
<script type="text/javascript" src="js/axios-0.18.0.js"></script>
【2】书写异步请求代码
axios.get("后台服务器地址?参数名=参数值&参数名=参数值")
.then(resp=>{
//后台响应成功执行这里,接收后台的数据:resp.data
})
.catch(error=>{
//后台有异常
})
.finally(()=>{
//必须执行的
});
axios.post("后台服务器地址","参数名=参数值&参数名=参数值")
.then()
.catch()
.finally();
5.JSON
JavaScript对象文本表示形式(JavaScript Object Notation : js对象简写)
json是js对象;json是目前 前后端交互的主要格式之一;
(1)JSON语法:
//json的语法主要有两种:
1. 对象 { }
2. 数组 [ ]
1. 对象类型
{name:value,name:value,....}
2. 数组类型
[
{name:value,name:value},
{name:value,name:value},
{name:value,name:value}
]
3. 复杂对象
{
name:value,
wives:[{name:value},{},{}],
son:{name:value}
}
//注意:
1. 其中name必须是string类型
json在js中,name的双引号可以省略,建议加双引号
2. value必须是以下数据类型之一:
字符串
数字
对象(JSON 对象)
数组
布尔
Null
3. JSON 中的字符串必须用双引号包围。
(单引号不行!主要是涉及到后期在后端java代码中操作)
五、VUE
1.基础入门
导入vue.js
<script src="js/vue.js"></script>
视图
<body>
<!--TODO: vue第一部分:视图-->
<div id="app">
<!--在vue中有个插值表达式,简称插值,可以获取数据模型中的数据并显示到页面中{{数据模型中的冒号左边的标识}}-->
{{msg}}
</div>
</body>
js代码存放数据
<script>
//TODO:vue第二部分:脚本js代码用来存放数据的
new Vue({
//1.作用的视图
//TODO:el是element元素的简写,
//通过视图即html标签的id属性值进行关联,这里的app是上述视图中div
//标签的id属性值
el:"#app",
//2.存放的数据
/*
数据模型:
1)data:{
name:value,
name:value
......
}
*/
data:{
msg:"vue基础入门"
},
methods:{
show:function(){
}
}
});
</script>
2.vue常用指令
(1)vue指令介绍
在vue中指令是作用在视图中的即html标签,可以在视图中增加一些指令来设置html的标签的某些属性和文本。
指令都是以带有v-前缀的特殊属性。
(2)使用vue指令
使用指令时,通常编写在标签的属性上,值可以使用JS表达式。
(3)常见的vue指令
指令 | 作用 |
---|---|
v-html | 把文本解析为HTML代码 |
v-bind | 为HTML标签绑定属性值 |
v-if | 条件性的渲染某元素,判定为true时渲染,否则不渲染 |
v-else | |
v-else-if | |
v-show | 根据条件展示元素,区别在于切换的是display属性的值 |
v-for | 列表渲染,遍历容器的元素或者对象的属性 |
v-on | 为HTML标签绑定事件 |
v-model | 在表单元素上创建双向数据绑定 |
v-html
需求:使用文本插值v-html和插值表达式**{{}}**获取vue中data数据
<标签名 v-html="vue中data的key"></标签名>
说明:文本插值v-html通过data的key获取value显示标签的文本中
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>文本插值</title>
</head>
<body>
<!--视图-->
<div id="div">
<div>{{msg}}</div>
<!--使用文本插值指令:v-html-->
<div v-html="msg"></div>
<div v-text="msg"></div>
</div>
</body>
<script src="js/vue.js"></script>
<script>
//脚本
new Vue({
el:"#div",
data:{
msg:"<h1>Hello Vue</h1>"
}
});
</script>
</html>
v-bind
需求:给html标签绑定属性
完整写法
<标签名 v-bind:属性名="data中key"></标签名>
简写:常用 *****
<标签名 :属性名="data中key"></标签名>
<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml" xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>绑定属性</title>
<style>
.my{
border: 1px solid red;
}
</style>
</head>
<body>
<div id="div">
<!--
插件表达式不能写在属性中
-->
<a href="{{url}}">百度一下</a>
<br>
<!--
v-bind:为 HTML 标签绑定属性值
-->
<a v-bind:href="url">百度一下</a>
<br>
<!--
v-bind 可以省略不写
-->
<a :href="url">百度一下</a>
<br>
<!--
也可以绑定其他属性
给当前div标签绑定一个class属性,属性值通过data中的keycls获取到my,
这里相当于
<div class="my">我是div</div>
-->
<div :class="cls">我是div</div>
</div>
</body>
<script src="js/vue.js"></script>
<script>
new Vue({
el:"#div",
data:{
url:"http://www.baidu.com",
cls:"my"
}
});
</script>
</html>
v-if
需求:判断vue中data的某个变量的值,对3取余,余数为0显示div1,余数为1显示div2,否则显示div3
<标签名 v-if="条件表达式">满足条件显示的内容</标签名>
<标签名 v-else-if="条件表达式">满足条件显示的内容</标签名>
.......
<标签名 v-else>上述条件都不满足执行的内容</标签名>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>条件渲染</title>
</head>
<body>
<div id="div">
<!-- 判断num的值,对3取余
余数为0显示div1
余数为1显示div2
余数为2显示div3 -->
<div v-if="num % 3 == 0">div1</div>
<div v-else-if="num % 3 == 1">div2</div>
<!--注意:如果不满足上述条件,则执行v-else,这里不用书写判断条件-->
<div v-else>div3</div>
</div>
</body>
<script src="js/vue.js"></script>
<script>
new Vue({
el:"#div",
data:{
num:2,
flag:true
}
});
</script>
</html>
v-show
需求:获取Vue中data的布尔类型数据并显示
<标签名 v-show="data中的key">文本</标签名>
说明:
1.如果data中的key的值是true,则显示标签文本内容,如果是false则不显示标签文本内容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>条件渲染</title>
</head>
<body>
<div id="div">
<!--v-show指令-->
<div v-show="flag">div4</div>
<!--
v-if v-show 他们俩虽然都是控制元素是否显示,但是底层的原理不一样
v-if 如果条件为false,页面中根本没有这个元素
v-show如果条件为false,页面中有这个元素只不过它的display属性值为none
-->
</div>
</body>
<script src="js/vue.js"></script>
<script>
new Vue({
el: "#div",
data: {
num: 2,
flag: false
}
});
</script>
</html>
v-show和v-if区别:
1.v-if:如果条件不满足,那么在页面中没有任何标签内容,直接删除
2.v-show:如果条件不满足,是通过设置css样式的属性display,设置属性值为none来隐藏标签,标签还在页面中,只是我们看不见
v-for
遍历数组或者对象
增强for循环:掌握
for(int x : arr){}
<标签名 v-for="x in 数组或者对象名或者集合名">
{{x}} 使用插值表达式获取元素
</标签名>
普通for循环:了解
<标签名 v-for="(x,index) in 数组或者对象名">
元素:{{x}},索引:{{index}}
</标签名>
说明:x和index属于标识符
<li v-for="(元素,索引) in 容器名或者对象名">
{{元素}}
</li>
如果遍历的是对象,那么会将对象中的key作为索引返回。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>列表渲染</title>
</head>
<body>
<div id="div">
<ul>
<!--
类似于增强for循环
1. element是遍历得到的每一个元素(变量名可以自定义)
有一个作用域,它的作用于在当前的这个循环中
2. names 是被遍历的数组或对象
3.我们在标签文本中使用插值表达式根据遍历数组的每个元素的变量element获取对应数据
-->
<!--需求:遍历names数组-->
<li v-for="element in names">
{{element}}
</li>
<!--需求:遍历对象student-->
<!--
1.stu 表示对象中的每组数据 :张三 23
2.student表示遍历的对象名
-->
<li v-for="stu in student">
{{stu}}
</li>
<!--
类似于普通for循环(了解)
-->
<!--需求:遍历names数组-->
<!--
1.element表示数组的元素
2.index表示数组中的索引
-->
<li v-for="(element,index) in names">
元素:{{element}},索引:{{index}}
</li>
<!--需求:遍历对象student-->
<!--
结果:
元素:张三,索引:name
元素:23,索引:age
注意:在vue中使用v-for遍历对象时,使用普通for循环遍历,索引是对象中的key或者变量
-->
<li v-for="(element,index) in student">
元素:{{element}},索引:{{index}}
</li>
</ul>
</div>
</body>
<script src="js/vue.js"></script>
<script>
new Vue({
el:"#div",
data:{
//数组
names:["张三","李四","王五"],
//对象
student:{
name:"张三",
age:23
}
}
});
</script>
</html>
v-on
给视图绑定事件
<标签名 v-on:事件名="调用的是vue中的js函数"></标签名>
简写:掌握
<标签名 @事件名="调用的是vue中的js函数"></标签名>
说明:
1.在vue中绑定的事件名都是将原生js的事件名去掉on:click blur...
<!DOCTYPE html>
<html lang="en" xmlns:v-on="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>事件绑定</title>
</head>
<body>
<div id="div">
<div>{{name}}</div>
<!--
v-on:为 HTML 标签绑定事件
-->
<button onclick="fn();">以前方式_单击_改变div的内容</button>
<!--
1.v-on:click="change();" 给当前标签即视图绑定一个单击事件,在事件的属性值中直接调用vue中的函数change()
-->
<button v-on:click="change();">单击_改变div的内容</button>
<button v-on:dblclick="change();">双击_改变div的内容</button>
<button @click="change();">简写_改变div的内容</button>
</div>
</body>
<script src="js/vue.js"></script>
<script>
let ve = new Vue({
el:"#div",
data:{
name:"黑马程序员"
},
methods:{
change(){
this.name = "传智播客";
}
}
});
//定义原生js函数
function fn() {
//调用vue中的函数change
ve.change();
}
</script>
</html>
v-model
可以实现双向数据绑定:
- vue中的data数据更改,然后视图中的显示内容也会随着改变
- 视图中的内容改变,vue中的data数据也会随着改变
单向数据绑定:
- vue中的data数据更改,然后视图中的显示内容也会随着改变
<!DOCTYPE html>
<html lang="en" xmlns:v-bind="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>表单绑定</title>
</head>
<body>
<div id="div">
<form autocomplete="off">
<!--
单向绑定
-->
<!--
:value="username" 表示给当前input标签绑定value属性,username是vue中data的key
-->
姓名_单向绑定:<input type="text" name="username" :value="username">
<br>
<!--
双向绑定
1.v-model="username":表示给当前input标签进行双向数据绑定,显示到输入框中,
直接根据key即username获取value
-->
姓名_双向绑定:<input type="text" name="username" v-model="username">
<br>
年龄:<input type="number" name="age" v-model="age">
性别:<input type="text" name="gender" v-model="gender">
</form>
{{username}}
<hr>
</div>
</body>
<script src="js/vue.js"></script>
<script>
new Vue({
el:"#div",
data:{
username:"张三",
age:23,
gender:"男"
}
});
</script>
</html>
v-model表单绑定实现双向数据绑定的原理:
MVVM模型(Model,View,ViewModel):是MVC模式的改进版
在前端页面中,JS对象表示Model,页面表示View,两者做到了最大限度的分离。
将Model和View关联起来的就是ViewModel,它是桥梁。
ViewModel负责把Model的数据同步到View显示出来,还负责把View修改的数据同步回Model。
3.vue声明周期
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>生命周期</title>
<script src="js/vue.js"></script>
</head>
<body>
<div id="app">
{{message}}
</div>
</body>
<script>
let vm = new Vue({
el: '#app',
data: {
message: 'Vue的生命周期'
},
//vue对象创建前,所有内容都是未定义类型
beforeCreate: function () {
console.group('------beforeCreate创建前状态------');
console.log("%c%s", "color:red", "el : " + this.$el); //undefined
console.log("%c%s", "color:red", "data : " + this.$data); //undefined
console.log("%c%s", "color:red", "message: " + this.message);//undefined
},
//表示vue对象创建完毕
//我们可以在该生命周期中先准备好数据,然后视图此时还没准备好
created: function () {
console.group('------created创建完毕状态------');
//el的值是undefined说明视图还没有准备好
console.log("%c%s", "color:red", "el : " + this.$el); //undefined
//说明在该生命周期中数据已经准备好了,
console.log("%c%s", "color:red", "data : " + this.$data); //已被初始化
console.log("%c%s", "color:red", "message: " + this.message); //已被初始化
},
//就是vue挂载视图前,还没有将数据挂载到视图中
beforeMount: function () {
console.group('------beforeMount挂载前状态------');
console.log("%c%s", "color:red", "el : " + (this.$el)); //已被初始化
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + this.$data); //已被初始化
console.log("%c%s", "color:red", "message: " + this.message); //已被初始化
},
//就是vue挂载视图后,vue中的data数据已经挂载到视图中了
mounted: function () {
console.group('------mounted 挂载结束状态------');
console.log("%c%s", "color:red", "el : " + this.$el); //已被初始化
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + this.$data); //已被初始化
console.log("%c%s", "color:red", "message: " + this.message); //已被初始化
},
//表示更新前 必须有数据的更新才会执行该生命周期
beforeUpdate: function () {
console.group('beforeUpdate 更新前状态===============》');
let dom = document.getElementById("app").innerHTML;
console.log(dom);
console.log("%c%s", "color:red", "el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + this.$data);
console.log("%c%s", "color:red", "message: " + this.message);
},
//更新完成
updated: function () {
console.group('updated 更新完成状态===============》');
let dom = document.getElementById("app").innerHTML;
console.log(dom);
console.log("%c%s", "color:red", "el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + this.$data);
console.log("%c%s", "color:red", "message: " + this.message);
},
//销毁前
//必须执行销毁方法才可以执行该生命周期
beforeDestroy: function () {
console.group('beforeDestroy 销毁前状态===============》');
console.log("%c%s", "color:red", "el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + this.$data);
console.log("%c%s", "color:red", "message: " + this.message);
},
//销毁后
destroyed: function () {
console.group('destroyed 销毁完成状态===============》');
console.log("%c%s", "color:red", "el : " + this.$el);
console.log(this.$el);
console.log("%c%s", "color:red", "data : " + this.$data);
console.log("%c%s", "color:red", "message: " + this.message);
}
});
// 更新data中message数据值
vm.message = "hehe";
// 销毁Vue对象
vm.$destroy();
vm.message = "hehe";// 销毁后 Vue 实例会解绑所有内容,更新无效
</script>
</html>
六、ElementVue脚手架
1.Element UI
(1)lement介绍
- Element:网站快速成型工具。是饿了么公司前端开发团队提供的一套基于Vue的网站组件库;
- 使用Element前必须要有Vue;
- 组件:组成网页的部件,例如超链接、按钮、图片、表格等;
- Element官网:https://element.eleme.cn/#/zh-CN
(2)快速入门
- 下载element组件库
- 在当前前端项目中引入组件库
- 创建页面,在页面中引入组件库和样式
<!-- 引入样式 -->
<link rel="stylesheet" href="../element-ui/lib/theme-chalk/index.css">
<!-- 引入VUE -->
<script src="../js/vue.js"></script>
<!-- 引入组件库 -->
<script src="../element-ui/lib/index.js"></script>
(3)基础布局
将页面分成最多 24 个部分,自由切换。并且支持响应式布局。
(4)容器布局
注意:<el-container>
:外层容器。当子元素中包含 <el-header>
或 <el-footer>
时,全部子元素会垂直上下排列,否则会水平左右排列。
(5)表单组件
由输入框、下拉列表、单选框、多选框等控件组成,用以收集、校验、提交数据。
在 Form 组件中,每一个表单域由一个 Form-Item 组件构成,表单域中可以放置各种类型的 表单控件,包括 Input、Select、Checkbox、Radio、Switch、DatePicker、TimePicker。
(6)表格组件
用于展示多条结构类似的数据,可对数据进行编辑、删除或其他自定义操作。
(7)导航栏组件
导航菜单默认为垂直模式(就是不写),通过mode
属性可以使导航菜单变更为水平模式。另外,在菜单中通过submenu
组件可以生成二级菜单。Menu 还提供了background-color(菜单的背景色)
、text-color(菜单的文字颜色)
和active-text-color(当前激活菜单的文字颜色)
.
小结:
- 使用步骤:
1.下载Element核心库。
2.引入Element样式文件。
3.引入Vue核心js文件。
4.引入Element核心js文件。
5.借助常用组件编写网页。
- 常用组件:
网页基本组成部分,布局、按钮、表格、表单等等~~~
常用组件不需要记住,只需要在Element官网中复制并学会修改使用即可。
2.Node.js
- 学习
官网:https://nodejs.org/zh-cn/
中文学习网:http://nodejs.cn/learn
- 介绍
1.Node.js是一个基于Chrome V8引擎的JavaScript运行环境。Node.js使用了一个
事件驱动、非阻塞式I/O模型,使其轻量又高效。
2.前端的底层html,css和js这些都不需要编译,由浏览器解释运行(解释型语言)
3.Node.js的包管理器npm,是全球最大的开源库生态系统
- 安装
下载对应你系统的Node.js版本:[https://nodejs.org/en/download/](https://nodejs.org/en/download/)
历史版本:https://nodejs.org/zh-cn/download/releases/
- 测试
测试,在命令提示符下输入命令 `node -v`
会显示当前node的版本
3.nodejs的包管理器npm
(1)使用npm进行初始化
npm init
(2)使用npm在本地即当前模块下安装软件
npm install 安装的技术
(3)全局安装
【1】查看你的电脑存放依赖的全局位置
npm root -g
【2】向全局位置安装依赖
npm install 安装的技术 -g =====> g:global全局的意思
举例:向全局位置安装vue
npm install vue -g
(4)淘宝NPM镜像【建议使用】
【1】下载步骤:
1.更换成淘宝的源
npm config set registry https://registry.npm.taobao.org
2.降低版本
npm install [email protected] -g
【2】验证是否安装成:
【3】使用cnpm安装软件:
cnpm install -g axios
【4】使用npm下载淘宝镜像报错问题:
参考:
4.webpack
【1】什么是webpack
webpack 是一个现代 javascript 应用程序的 静态模块打包器 (module bundler)
待会要学的 vue-cli 脚手架环境, 集成了 webpack, 所以才能对各类文件进行打包处理。
webpack是一个 静态模块 打包器,可以做以下的这些工作:
- 语法转换(主要是浏览器兼容)
- less/sass转换成css
- ES6转换成ES5
- …
- html/css/js 代码压缩合并 (打包);
- webpack可以在开发期间提供一个开发服务器;
【2】Webpack安装
全局安装
cnpm install [email protected] -g
cnpm install [email protected] -g
安装后查看版本号
webpack -v
webpack-cli -v
【3】快速入门
方式一:webpack原始方式
(1)新建文件夹 work3,在创建src文件夹,创建 bar.js
exports.info=function(str){
document.write(str);
}
(2)src下创建logic.js
exports.add=function(a,b){
return a+b;
}
(3)src下创建main.js
var bar= require('./bar');
var logic= require('./logic');
bar.info( 'Hello world!'+ logic.add(100,200));
说明:上述具有多个js文件,那么我们在实际开发中使用的时候会导入过多的js文件,使用起来不方便,那么我们可以将上述多个js文件打包成一个js文件。
(4)创建配置文件webpack.config.js ,该文件与src处于同级目录
var path = require("path"); // 导入 node.js 中专门操作路径的模块,固定写法
//执行webpack命令的时候会读取到module.exports中的内容
module.exports = {
//执行webpack命令的时候,读取入口main.js,由于main.js关联bar.js和logic.js,所以会将这三个js文件合并到一个js文件中
entry: './src/main.js', // 打包入口文件的路径
//输出文件位置
output: {
//__dirname表示当前工程目录
path: path.resolve(__dirname, './dist'), // 输出文件的存放路径
filename: 'bundle.js' // 输出文件的名称
}
};
以上代码的意思是:读取当前目录下src文件夹中的main.js(入口文件)内容,把对应的js文件打包,打包后的文件放入当前目录的dist文件夹下,打包后的js文件名为bundle.js
webpack 的 4.x 版本中默认约定:
- 打包的入口文件为 src -> main.js
- 打包的输出文件为 dist -> index.js
(5)执行编译命令
webpack
执行后查看bundle.js 会发现里面包含了上面两个js文件的内容
(6)创建index.html ,引用bundle.js
<!doctype html>
<html>
<head>
</head>
<body>
<script src="dist/bundle.js"></script>
</body>
</html>`
创建完毕,完整目录如下:
测试调用index.html,会发现有内容输出:Hello world!300
方式二:基于NPM方式
-
新建项目空白目录,并运行
npm init –y
命令,初始化包管理配置文件package.json-y 可以直接跳过配置
-
新建 src 源代码目录,并且把 bar.js 和 logic.js 和 main.js 文件复制到 src目录
-
创建index.html ,引用bundle.js
<!doctype html> <html> <head> </head> <body> <script src="dist/bundle.js"></script> </body> </html>
-
运行
npm install webpack webpack-cli
命令,安装webpack相关的包如果全局安装过webpack 和 webpack-cli工具, 此步骤可跳过
-
在项目根目录中,创建名为webpack.config.js 的 webpack配置文件同上
var path = require("path"); module.exports = { mode: 'development', // mode 用来指定构建模式development、production entry: './src/main.js', output: { path: path.resolve(__dirname, './dist'), filename: 'bundle.js' } };
mode: ‘development’ //mode 用来指定构建模式、production:生产模式(压缩)
-
在 package.json 配置文件中的scripts节点下,新增dev脚本如下:
{ "name": "work4", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "dev": "webpack" //script 节点下的脚本,可以通过 npm run 执行 }, "keywords": [], "author": "", "license": "ISC" }
完整代码结构:
在终端中运行 npm run dev
命令,启动webpack进行项目打包。
注意是在package.json 包下运行该命令
点击index.html文件查看结果, 会发现有内容输出:`Hello world!300
说明:使用npm命令运行的原因是后期使用webpack命令的时候,命令会很多,不仅仅只有webpack几个字母,不好记,而使用:npm run dev命令执行无论webpack命令有多少都会执行。
(1)作用
**将前端的文件进行打包。**可以将打包后的文件发布到前端服务器上;
(2)webpack打包应用
webpack.config.js
var path = require("path"); // 导入 node.js 中专门操作路径的模块,固定写法
//执行webpack命令的时候会读取到module.exports中的内容
module.exports = {
//执行webpack命令的时候,读取入口main.js,由于main.js关联bar.js和logic.js,
//所以会将这三个js文件合并到一个js文件中
entry: './src/main.js', // 打包入口文件的路径
//输出文件位置
output: {
//__dirname表示当前工程目录
path: path.resolve(__dirname, './dist'), // 输出文件的存放路径
filename: 'bundle.js' // 输出文件的名称
}
};
在终端执行webpack命令
(3)基于npm方式打包
记住命令:
npm run dev 启动项目
(4)webpack-dev-server开发服务器
作用:当更改打包的源码的js文件时会自动更新 ====> 资源热更新。
- 介绍:
修改main.js代码如下:
需要每次在修改源码之前需要重新打包:
每次修改代码, 都需要重新打包, 才能看到最新的效果, 且实际工作中, 打包非常费时 (30s - 60s) 之间
为什么费时?
- 构建依赖
- 读取对应的文件, 才能加载
- 用对应的 loader 进行处理
- 将处理完的内容, 写入到 dist 目录
简而言之就是我们希望修改源码完毕之后,不用再每次重新打包,运行的结果直接是修改后的结果。
解决方案:我们可以开启一个开发服务器webpack-dev-server, 在电脑内存中打包, 缓存一些已经打包过的内容, 只重新打包修改的文件 (热更新)
- 使用:
如果模块(work3)下,没有package.json 文件,那么需要在模块下初始化命令:npm init即可
-
修改 package.json -> scripts 中的 dev, dependencies, devDependencies如下:
devDependencies这是开发时依赖, 上线不依赖
{
"name": "work4",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack-dev-server"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"webpack": "^4.41.5",
"webpack-cli": "^3.3.12"
},
"devDependencies": {
"webpack-dev-server": "^3.11.2"
}
}
然后运行 cnpm install 指令,安装webpack,webpack-cli 和 webpack-dev-server
- 修改 webpack.config.js 文件,添加 devServer
var path = require("path");
module.exports = {
mode: 'development', // mode 用来指定构建模式development、production
entry: './src/main.js',
devServer: {
port: 8099,// 服务器占用8099端口
open: true // 自动打开浏览器
},
output: {
path: path.resolve(__dirname, './dist'),
filename: 'bundle.js'
}
};
-
删除之前的dist目录
-
将 index.html 中,script 脚本的引用路径
<!doctype html> <html> <head> </head> <body> <!-- 内存中读取 --> <script src="/bundle.js"></script> </body> </html>
-
运行 npm run dev 命令
-
在浏览器中访问 http://localhost:8099地址,查看自动打包效果
webpack-dev-server 会启动一个实时打包的 http 服务器
webpack-dev-server 打包生成的输出文件,默认放到了项目根目录中,基于内存
(5)小结
vue脚手架之前安装的软件:
- 安装node.js
- 安装淘宝镜像 cnpm
- 安装打包器:webpack
5.vue-cli脚手架(掌握)
(1)常见命令
# 1.启动项目
npm run dev
# 2.安装当前项目所有的依赖
cnpm i
(2)vue脚手架入门
- 安装脚手架到全局位置
cnpm install vue-cli -g
- 使用脚手架命令初始化工程(开发中都是前端工程师做的)
cnpm install vue-cli -g //只需安装一次,可以使用cnpm安装,通过
//Windows使用 ‘vue init’ 的运行效果将会跟 ‘[email protected]’ 相同
// work5 是模块名字
vue init webpack work5
- vue-cli脚手架常见的目录结构
#1.main.js :表示入口文件
#2.App.vue :表示根组件,在脚手架中只要以.vue结尾的都是组件
<template>
书写html标签的,只能书写一个根标签
<div>
</div>
</template>
<script>
书写js代码的
</script>
<style>
书写css样式
</style>
例如:
<template>
<div id="app">
<!-- 脚手架的logo图片 -->
<!-- <img src="./assets/logo.png"> -->
<!-- 组件输出位置,欢迎页面输出的文字是由下面的标签输出的 -->
<!-- <router-view/> -->
<h1>{{msg}}</h1>
</div>
</template>
<script>
export default {
data(){
return {
msg:"vue架手架挺难的"
}
}
}
</script>
<style>
/* #app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
} */
</style>
#3.index.html后期不会修改
- 路由
【1】概念:在vue脚手架中路由表示浏览器地址栏输入的路径和组件的映射关系。
- 脚手架总结
【1】config/index.js
【2】main.js
【3】根组件
【4】路由
注意:前端工程师会在src下面创建一个文件夹:views。存放自己创建的组件即以.vue结尾
6.vue-cli脚手架练习
(1)模拟网易云音乐
【1】在根组件App.vue添加超链接
<template>
<div id="app">
<!-- 脚手架的logo图片 -->
<img src="./assets/logo.png">
<!-- 访问vue ,必须加#-->
<a href="#/">访问vue</a>
<a href="#/friend">访问好友</a>
<!-- 组件输出位置,欢迎页面输出的文字是由下面的标签输出的 -->
<router-view/>
<!-- <h1>{{msg}}</h1> -->
</div>
</template>
<script>
export default {
// data(){
// return {
// msg:"vue架手架挺难的"
// }
// }
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
【2】在路由中配置访问的路径和组件的关系
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
// @表示src目录 ==>src/views/friend.vue
//http://localhost:8080/#/friend===> path: '/friend'===> component: friend===>import friend from===>src/views/friend.vue
import friend from '@/views/friend'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
},
{
path: '/friend',
name: 'haha',
component: friend
}
]
})
【3】创建组件
<template>
<div id="app">
{{username}}
</div>
</template>
<script>
export default {
data(){
return{
username:"柳岩"
}
}
}
</script>
<style>
</style>
(2)将Element-UI组件库添加到vue脚手架中
【1】安装
1.在当前工程下安装element
cnpm i element-ui -S
2.在main.js文件中引入element内容
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
//导入element
import ElementUI from 'element-ui';
// 导入element的css样式
import 'element-ui/lib/theme-chalk/index.css';
// 导入App.vue根组件
import App from './App'
// 导入路由
import router from './router'
// 让vue对象使用ElementUI
Vue.use(ElementUI);
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
【2】代码实现
1.在App.vue根组件中书写超链接
<template>
<div id="app">
<!-- 脚手架的logo图片 -->
<img src="./assets/logo.png">
<!-- 访问vue ,必须加#-->
<a href="#/">访问vue</a>
<a href="#/friend">访问好友</a>
<a href="#/element">访问element</a>
<!-- 组件输出位置,欢迎页面输出的文字是由下面的标签输出的 -->
<router-view/>
<!-- <h1>{{msg}}</h1> -->
</div>
</template>
<script>
export default {
// data(){
// return {
// msg:"vue架手架挺难的"
// }
// }
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
2.在路由中配置路径和组件的关系
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
// @表示src目录 ==>src/views/friend.vue
import friend from '@/views/friend'
import element from '@/views/element'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
component: HelloWorld
},
{
path: '/friend',
name: 'haha',
component: friend
},
{
path: '/element',
name: 'element',
component: element
}
]
})
3.创建组件
<template>
<el-table
:data="tableData"
style="width: 100%">
<el-table-column
prop="date"
label="日期"
width="180">
</el-table-column>
<el-table-column
prop="name"
label="姓名"
width="180">
</el-table-column>
<el-table-column
prop="address"
label="地址">
</el-table-column>
</el-table>
</template>
<script>
export default {
data() {
return {
tableData: [{
date: '2016-05-02',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
}, {
date: '2016-05-04',
name: '王小虎',
address: '上海市普陀区金沙江路 1517 弄'
}, {
date: '2016-05-01',
name: '王小虎',
address: '上海市普陀区金沙江路 1519 弄'
}, {
date: '2016-05-03',
name: '王小虎',
address: '上海市普陀区金沙江路 1516 弄'
}]
}
}
}
</script>
<style>
</style>
7.vue脚手架综合案例
(1)环境搭建
【1】后端环境(day04)
- 执行sql语句
CREATE DATABASE dbvue2;
USE dbvue2;
-- 创建学生表
CREATE TABLE student(
number VARCHAR(10) UNIQUE, -- 学号
NAME VARCHAR(10), -- 姓名
birthday DATE, -- 生日
address VARCHAR(200) -- 地址
);
INSERT INTO student VALUES ('hm001','张三','1995-05-05','北京市昌平区');
INSERT INTO student VALUES ('hm002','李四','1996-06-06','北京市海淀区');
INSERT INTO student VALUES ('hm003','王五','1997-07-07','北京市朝阳区');
INSERT INTO student VALUES ('hm004','赵六','1998-08-08','北京市丰台区');
INSERT INTO student VALUES ('hm005','周七','1999-09-09','北京市顺义区');
INSERT INTO student VALUES ('hm006','孙悟空','2000-01-01','花果山水帘洞');
INSERT INTO student VALUES ('hm007','猪八戒','2001-02-02','高老庄翠兰家');
INSERT INTO student VALUES ('hm008','沙和尚','2002-03-03','流沙河河底');
INSERT INTO student VALUES ('hm009','唐玄奘','2003-04-04','东土大唐');
- 修改连接数据库的参数
- 启动后端项目
【2】前端环境
将work8.zip前端项目放到vscode的工作空间,然后解压
- 安装依赖:cnpm i
- 启动项目 npm run dev
七、web
1.内容介绍
Web(World Wide Web)即全球广域网,也称万维网。它是一种基于超文本和HTTP的、全球性的、动态交互的、跨平台的分布式图像信息系统。是建立在Internet上的一种网络服务,为浏览者在Internet上查找和浏览信息提供了图形化的、易于访问的直观界面,其中的文档及超级链接将Internet上的信息节点组织成一个互为关联的网状结构。
简而言之就是我们平时上网浏览的网页,玩的网页游戏,上网下载资源。对于程序员而言就是前段知识、web服务器知识和数据库知识的结合就是web。
web发展历程:
- web1.0
1994年在中国第一个web网站是中国黄页,由马云。属于静态网页。只能看,不能交互。发布租赁信息。
- web2.0
动态网站。网站数据是时时更新的。数据来自于数据库的。登录网站显示用户名,天气预报,微博头条。
可以实现前后台数据的交互
注意:
网站:前端知识、web服务器知识和数据库知识。包括网页
网页:前端页面html页面
2.软件架构模式
- BS:browser server 浏览器服务器
- 优点:
- 只需要服务器,用户下载浏览器,维护方便
- 减少用户的磁盘空间
- 缺点:
- 给服务器造成压力
- 用户观看体验不友好
- 优点:
- CS:client server 客户端 服务器
- 优点:
- 具有客户端和服务器端,减轻服务器的压力
- 用户观看体验友好
- 缺点:
- 维护成本大
- 版本升级麻烦,占用户磁盘空间
- 优点:
3.B/S和C/S通信模式特点
- 先有请求
- 后有响应
- 请求和响应是成对出现的
4.web资源
- 资源:
计算机中数据文件
-
分类:
-
静态资源:
html、css、js。只能书写静态网站,静态网站的数据永远不会发生改变。
-
动态资源:
使用一些语言(JAVAEE)可以实现数据的变化,例如:java语言。
-
5.URL请求路径
【1】作用:通过资源路径可以访问到具体的服务器。
【2】URL (Uniform Resource Locator) ,统一资源定位符是对互联网上资源位置的一种表示,互联网上的每个文件都有一个唯一的URL。
【3】格式:
协议://服务器的ip地址:服务器的端口号/项目名/资源路径 jdbc:mysql://localhost:3306/数据库名
https://www.jd.com/
说明:
1)协议:是一种规范。类似于我们之前讲解的TCP UDP(传输层),这里讲解的是应用层(http https协议)
2)服务器的ip地址:就是访问的服务器的地址。同一个网段中服务器ip地址是唯一的。
说明:如果我们在自己电脑上进行练习,ip地址书写127.0.0.1或者localhost
3)服务器的端口号:访问服务器端的进程号,属于唯一标识。
4)/项目名/资源路径
【4】浏览器通过url访问服务器的过程
https://www.baidu.com/s?ie=UTF-8&wd=java
6.服务器
【1】服务器,是提供计算服务的设备。由于服务器需要请求响应,并进行处理,因此一般来说,服务器应具备承担服务并且保障服务的能力。
【2】分类:
- 硬件服务器:
服务器的构成包括处理器、硬盘、内存、系统总线等,和通用的计算机架构类似,但是由于需要提供高可靠的服务,因此在处理能力、稳定性、可靠性、安全性、可扩展性、可管理性等方面要求较高。
- 软件服务器:
服务器软件本质上是一个应用程序(由代码编写而成),运行在服务器设备上。能够接收请求并根据请求给客户端响应数据,发布资源(静态和动态)。数据库服务器、邮件服务器(易邮)、网页服务器(tomcat nginx发布网页)等
【3】常见的web服务器
* Tomcat: Apache软件基金开源免费的web服务器,支持JavaEE规范(Servlet/Jsp).
* Jetty:Apache组织开源免费的小型web服务器,支持JavaEE规范.
* JBoss: RedHat红帽公司的开源免费的web服务器,支持JavaEE规范.
* Glass Fish:Sun公司开源免费的web服务器,支持JavaEE规范.
---------------------------------------------------------------------
* WebLogic: Oracle公司收费的web服务器,支持JavaEE规范.
* WebSphere:IBM公司收费的web服务器,支持JavaEE规范.
八.tomcat
1.目录结构
2.启动和关闭以及访问tomcat
-
启动
-
访问
在浏览器地址栏上输入:localhost:8080
- 关闭
3.解决启动tomcat失败的问题
【1】端口号冲突
打开命令行输入:netstat -nao;找到端口号为8080对应的PID
打开任务管理器—>详细信息,找到对应的PID,然后结束对应的任务。
【2】jdk环境变量
打开命令行,输入java -version,查看是否出现版本号,如果没有,请检查java_home是否有问题。
【3】删除环境变量中TOMCAT_HOME
右击此电脑—>属性—>高级系统设置---->环境变量
4.使用tomcat发布web项目
【1】在tomcat的安装目录webapps下发布web资源
(1)在webapps下创建一个文件夹作为项目名
(2)在该文件夹下创建一个静态资源html页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
itcast
</body>
</html>
(3)到bin目录启动tomcat---->双击startup.bat文件
(4)在浏览器中访问
【2】在idea中创建web项目
(1)
(2)
(3)删除一些内容
pom.xml留下的内容
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itheima.sh.heima153</groupId>
<artifactId>day15_web02</artifactId>
<version>1.0-SNAPSHOT</version>
<name>day15_web02</name>
<!--TODO:web工程的打包方式。普通java工程打包方式是jar,也是默认的。不能删除-->
<packaging>war</packaging>
<properties>
<!--TODO:jdk编译版本-->
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
</properties>
<dependencies>
<!--TODO:不要删除-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.1</version>
</plugin>
</plugins>
</build>
</project>
5.使用在idea中创建的web项目发布资源
九、Servlet
之前我们使用浏览器访问的是静态资源即html。web资源除了静态资源还有动态资源,即数据可以实现变化,并且前端浏览器通过动态资源可以实现数据的交互。那么如果想客观客户端访问服务器的动态资源,需要我们在后端中定义类直接或者间接实现Servlet接口。
1.Servlet介绍
- Servlet是一个接口,即规范
- 定义的实现类必须实现接口中的所有抽象方法
- Servlet全称Server Applet服务器端的程序,是sun公司提供一套规范,用来处理客户端请求、响应给浏览器的动态web资源。其实servlet的实质就是java代码,通过java的API动态的向客户端输出内容,并且从客户端接收数据。
- 一个类要想通过浏览器被访问到,那么这个类就必须直接或间接的实现Servlet接口
2.Servlet作用:
1.接收客户端的请求
2.处理业务逻辑
3.将响应数据给浏览器
3.Servlet快速入门
【1】步骤
- 创建web项目
- 导入servlet依赖
<!--导入依赖-->
<dependencies>
<!--导入servlet依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
</dependency>
</dependencies>
- 在创建的web项目中自定义类实现Servlet接口
- 在自定义类中实现Servlet接口中所有的抽象方法
- 在实现Servlet接口的service方法体中书写代码处理业务逻辑
void service(ServletRequest req, ServletResponse res)
- 在web项目的核心配置文件web.xml中配置访问servlet的路径。
- 说明:这样配置是告知tomcat有具体的Servlet类需要被访问。
- 启动tomcat
- 在浏览器中访问servlet类
【2】实现
- 创建maven的web项目
- 导入servlet的依赖
<!--servlet-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<!--编译 测试需要,运行时不需要-->
<scope>provided</scope>
</dependency>
- 在创建的web项目中自定义类实现Servlet接口
- 在自定义类中实现Servlet接口中的所有的抽象方法
- 在实现Servlet接口的service方法体中书写代码处理业务逻辑
package com.itheima.sh.a_demo_01;
import javax.servlet.*;
import java.io.IOException;
/*
2.在创建的web项目中自定义类实现Servlet接口
*/
public class HelloWorldServlet implements Servlet{
//3.在自定义类中实现Servlet接口中的所有的抽象方法
//4.在实现Servlet接口的service方法体中书写代码处理业务逻辑
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("service....");
}
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
- 在web项目的核心配置文件web.xml中配置servlet的路径
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!--
5.在web项目的核心配置文件web.xml中配置访问servlet的路径。
说明:这样配置是告知tomcat有具体的Servlet类需要被访问。
-->
<!--
1.<servlet> 表示将当前Servlet类注册到tomcat中,告知tomcat有一个类要被访问
-->
<servlet>
<!--
表示当前要被访问类的标识,在当前web.xml中要唯一,helloWorldServlet属于标识符
-->
<servlet-name>helloWorldServlet</servlet-name>
<!--
配置要访问 的servlet类,必须是类的全路径:包名.类名。
说明:tomcat底层通过获取这里的类全路径使用反射技术调用当前类的无参构造方法创建对象
-->
<servlet-class>com.itheima.sh.a_demo_01.HelloWorldServlet</servlet-class>
</servlet>
<!--
配置要访问的servlet类的映射路径
-->
<servlet-mapping>
<!--这里要和上面的servlet-name文本值一致,这里找到上面的servlet-name-->
<servlet-name>helloWorldServlet</servlet-name>
<!--浏览器上地址栏上输入的映射路径及访问路径,这里必须加/-->
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
- 启动tomcat
- 在浏览器中访问servlet类
4.Servlet的执行原理
【1】执行流程:
【2】原理
说明:
1.当我们点击run运行的时候,tomcat之所以会启动,是因为程序入口(main方法)在tomcat中
2.tomcat开始运行,会加载web项目里面的配置文件web.xml(xml解析,读取数据)
主要是根据url-pattern 找到对应的servlet-class
3.然后tomcat进入等待状态(永不停止,除非手动关闭)
4.当用户在浏览器中输入地址:http://localhost:8080/hello就会定位到tomcat的访问的项目下面的某个servlet中
5.tomcat会根据 /hello 的servlet的虚拟路径 找到HelloServlet的全限定名
6.tomcat底层通过反射创建HelloServlet的对象,并调用HelloServlet的service方法:
Class clazz = Class.forName("全限定名");
Servlet servlet = clazz.newInstance();//实际上HelloServlet对象,向上转型
servlet.service();
5.Servlet生命周期
【1】生命周期:
指的是一个对象从生(创建)到死(销毁)的一个过程。
【2】生命周期的api:
// 1. servlet对象创建完毕,使用对象调用此方法,初始化方法,
//只有在第一次访问的时候执行一次
public void init(ServletConfig servletConfig);
// 2. 用户访问servlet时,调用此方法 (每次访问都会调用一次)
public void service(ServletRequest servletRequest,
ServletResponse servletResponse);
// 3. servlet对象销毁时,调用此方法
public void destroy();
【3】Servlet生命周期的api执行时机图解
* 创建
1)默认情况下
用户第一次访问时,创建servlet,执行init方法
* 运行(提供服务)
用户每次访问时,都执行service方法
* 销毁
服务器正常关闭时,销毁servlet,执行destroy方法
【4】代码实现
package com.itheima.sh.a_servlet_01;
import javax.servlet.*;
import java.io.IOException;
public class Life01Servlet implements Servlet {
/*
反射调用无参构造方法创建对象
*/
public Life01Servlet() {
System.out.println("无参构造。。。。");
}
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("init。。。。");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("service。。。。");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
System.out.println("destroy。。。。");
}
}
【5】结果
1.何时创建servlet?
第一次访问servlet的时候创建
2.谁创建的?
tomcat服务器创建的
3.创建对象时做了什么?
调用无参构造方法创建对象,然后立刻调用init初始化方法
4.每次访问servlet执行哪个方法
service方法
5.何时销毁servlet?
关闭tomcat服务器,调用destroy方法
6.服务器启动,立刻加载Servlet对象
【1】问题:发现init默认第一次被访问的时候才调用,适合用来初始化项目数据;
如果项目数据很多,加载就需要一定的时间,这样就会给用户的体验不好,因为要等比较久的时间
【2】解决:服务器一启动,就执行init方法
【3】实现:在web.xml核心配置文件中对应的servlet标签按照如下配置:
注意:
1.使用标签进行配置,表示标记容器是否应该在启动的时候加载这个servlet,(实例化并调用其init()方法)
2.它的文本值必须是一个整数,表示servlet应该被载入的顺序
3.如果文本值是负数:默认值是-1 【用户第一次访问时,创建】
4.当值大于等于0时,表示容器在应用启动时就加载并初始化这个servlet;
5.正数的值越小,该servlet的优先级越高,应用启动时就越先加载。
6.当值相同时,容器就会自己选择顺序来加载
<servlet>
<servlet-name>life01Servlet</servlet-name>
<servlet-class>com.itheima.sh.a_servlet_01.Life01Servlet</servlet-class>
<!--
load-on-startup 标签可以让tomcat服务器启动就创建对应的servlet。标签文本值必须是整数:
数字越小,创建servlet的优先级越高
-->
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>life01Servlet</servlet-name>
<url-pattern>/life01</url-pattern>
</servlet-mapping>
总结:
1.load-on-startup标签可以让tomcat服务器启动就创建对应的servlet。标签文本值必须是整数:
数字越小,创建servlet的优先级越高,建议是大于等于0的整数
<load-on-startup>2</load-on-startup>
2.配置load-on-startup标签后servlet生命周期如下:
7.Servlet实现方式
一共有三种:
快捷键:
1.ctrl + alt + u :查看一个类的继承结构图
2.ctrl + h :这个类的简化版继承结构
(1)实现Servlet方式二_自定义类继承GenericServlet
- 编写Servlet实现类,只要重写service方法
【1】描述问题
Servlet中使用频率最高,最重要的方法是service方法(大部分场景)
但是我们每次编写Servlet实现类,都是直接实现Servlet接口,重写5个抽象方法(太冗余)
【2】解决问题
我们可以自定义类继承GenericServlet抽象类,只在子类中重写service即可。
不用重写所有的抽象方法。
【3】步骤
1.自定义类继承GenericServlet类
2.在子类中重写service方法,处理业务逻辑
3.在web.xml中进行映射路径的配置
4.在浏览器客户端访问servlet类
【4】代码实现:
package com.itheima.sh.a_servlet_01;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
/*
1.自定义类继承GenericServlet类
*/
public class Demo01Servlet extends GenericServlet {
// 2.在子类中重写service方法,处理业务逻辑
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("service....");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!--3.在web.xml中进行映射路径的配置-->
<servlet>
<servlet-name>demo01Servlet</servlet-name>
<servlet-class>com.itheima.sh.a_servlet_01.Demo01Servlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>demo01Servlet</servlet-name>
<url-pattern>/demo01</url-pattern>
</servlet-mapping>
</web-app>
小结:方式二的好处是只需要重写service方法,在方法体内部处理业务逻辑即可。
(2)实现Servlet方式三_自定义类继承HttpServlet
根据页面不同的请求方式执行具体的请求方法
【1】问题:
我们在前端的form表单中,method属性,学习过有两种常用的请求方式(get/post)
我们现在的service方法是这样的:
用户发送请求,无论是什么请求方式,都会统一的执行service方法,我们无法很好的区别是哪一种
请求方式
【2】解决问题:
我们可以自定义类继承HttpServlet就可以根据不同的请求做不同的处理:get post
【3】步骤:
1.自定义类继承HttpServlet
2.在子类中根据不同的请求方式重写请求方式的方法:
get请求---重写doGet方法
post请求---重写doPost方法
3.在方法体中书写业务逻辑的代码
4.在web.xml中进行配置
5.浏览器客户端访问servlet
【4】实现
package com.itheima.sh.a_servlet_01;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/*
1.自定义类继承HttpServlet
*/
public class Demo02Servlet extends HttpServlet {
/*
2.在子类中根据不同的请求方式重写请求方式的方法:
get请求---重写doGet方法
post请求---重写doPost方法
*/
// 3.在方法体中书写处理业务逻辑的代码
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("get....");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("post....");
}
}
<!--4.在web.xml中进行配置-->
<servlet>
<servlet-name>demo02Servlet</servlet-name>
<servlet-class>com.itheima.sh.a_servlet_01.Demo02Servlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>demo02Servlet</servlet-name>
<url-pattern>/demo02</url-pattern>
</servlet-mapping>
小结:
1.实现servlet方式三的好处:
- 可以根据不同的请求执行对应的方法
- 处理满足http协议的请求和响应
2.对ServletRequest和HttpServletRequest接口的说明:
这个对象封装了浏览器发送的所有请求数据,兼容大部分协议。HttpServletRequest是子接口,可以匹配http协议下的所有请求。
(3)实现servlet方式三_继承HttpServlet的执行流程
疑问:就是正常浏览器访问tomcat服务器需要访问servlet接口中的service方法,但是方式三在子类中没有重写servlet中的service方法,只重写了doGet和doPost方法,那么底层是如果执行呢?
小结:
当我们访问自定义类的servlet的时候,先访问HttpServlet类实现Servlet接口中的service方法,在service方法体中调用了重载的service方法,在该方法体内部获取请求方式,根据不同的请求方式执行对应的方法。
get请求---doGet()方法
post请求---doPost()方法
注意:查看某个类的成员结构快捷键:alt+7
(4)实现servlet的三种方式总结
1.自定义类实现Servlet接口,重写所有的抽象方法,
不能处理满足http协议的请求和响应
2.自定义类继承GenericService抽象类,该抽象类是Servlet接口的子类,
这种方式只重写service方法即可,但是不能处理满足http协议的请求和响应,
同时不能根据具体的请求方式执行具体的方法
3.自定义类继承HttpServlet抽象类,该抽象类是GenericServlet的子类,
根据不同的请求方式执行对应方法:掌握
get---doGet
post---doPost
可以处理满足http协议的请求和响应
8.编写servlet常见的问题
(1)遇到500错误
表示服务器内部异常
(2)遇到404错误
浏览器客户端访问服务器的资源不存在
注意:报404的根本原因是用户操作不当导致的。
(3)遇到405错误
服务器servlet没有重写doGet或者doPost方法。
package com.itheima.sh.d_servlet_04;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/*
TODO:编写Servlet常见异常
1.服务器内部出现异常===在浏览器中显示的是500:HTTP Status 500 – Internal Server Error
解决方案:找出异常位置修改代码
2.浏览器出现404===就是用户问题,用户操作不当,访问的资源在服务器中不存在,基本都是路径有问题。HTTP Status 404 – Not Found
解决方案:
1)查看你在地址栏上输入的地址路径和服务器中的资源路径是否匹配
2)当前项目配置了虚拟路径,但是你访问的时候没有加虚拟路径
3) 访问的路径在当前项目中存在,但是在当前项目中的target位置不存在,
执行maven生命周期命令===clean清除target,重启启动tomcat即可
3.浏览器出现405===子类Servlet没有重写父类HttpServlet类中的doGet和doPost方法导致调用了父类
HttpServlet中doGet和doPost方法 HTTP Status 405 – Method Not Allowed
解决方案:子类重写HttpServlet的doGet和doPost方法,并且在子类方法体中不能调用父类中的doGet和doPost方法,不能书写:
super.doGet(req, resp);
*/
public class ExceptionDemo01Servlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("哈哈呵呵嘿嘿");
//调用父类HttpServlet类中doGet(req, resp),那么就会报错HTTP Status 405 – Method Not Allowed
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
小结:
1.500错误:服务器异常
2.404错误:找不到资源
3.405错误:
如果我们不重写doGet/doPost方法,那么父类的doGet/doPost方法会执行(继承),给浏览器响应一个错误:状态码405(http1.1)。
9.Servlet映射路径理解
能够配置一个Servlet映射单个和多个url
【1】一个Servlet映射单个url
【2】一个Servlet映射多个url
注解版本:
10.Servlet映射路径配置规范
servlet映射路径一共有四种:
(1)完全路径匹配:
就是访问什么在web.xml中配置什么路径。 /hello /user
package com.itheima.sh.a_servlet_01;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
public class PathOneServlet extends GenericServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("PathOneServlet....");
}
}
<servlet>
<servlet-name>pathOneServlet</servlet-name>
<servlet-class>com.itheima.sh.a_servlet_01.PathOneServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>pathOneServlet</servlet-name>
<!--完全路径匹配-->
<url-pattern>/user/one</url-pattern>
</servlet-mapping>
(2)目录匹配:/user/*
package com.itheima.sh.a_servlet_01;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
public class PathTwoServlet extends GenericServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("PathTwoServlet....");
}
}
<servlet>
<servlet-name>pathTwoServlet</servlet-name>
<servlet-class>com.itheima.sh.a_servlet_01.PathTwoServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>pathTwoServlet</servlet-name>
<!--目录匹配-->
<url-pattern>/user/*</url-pattern>
</servlet-mapping>
(3)后缀名匹配:*.do *.action
注意这里不能书写 / ,访问以.do或者.action结尾的资源路径,后缀名都属于标识符
package com.itheima.sh.a_servlet_01;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
public class PathThrServlet extends GenericServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("PathThrServlet....");
}
}
<servlet>
<servlet-name>pathThrServlet</servlet-name>
<servlet-class>com.itheima.sh.a_servlet_01.PathThrServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>pathThrServlet</servlet-name>
<!--后缀名匹配,前面不能加/-->
<url-pattern>*.do</url-pattern>
</servlet-mapping>
(4)缺省路径: /
如果上述三种路径都不满足就访问缺省路径。
package com.itheima.sh.a_servlet_01;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
public class PathFourServlet extends GenericServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("PathFourServlet....");
}
}
<servlet>
<servlet-name>pathFourServlet</servlet-name>
<servlet-class>com.itheima.sh.a_servlet_01.PathFourServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>pathFourServlet</servlet-name>
<!--缺省路径匹配-->
<url-pattern>/</url-pattern>
</servlet-mapping>
小结:完全路径匹配 > 目录匹配 > 后缀名匹配 > 缺省路径
11.绝对路径
【1】绝对路径有两种写法:
1.带网络三要素:
http://ip地址:端口号/资源路径
2.不带网络三要素:
/资源路径 这里的 / 不能省略,要求访问的资源必须在同一个服务器上
【2】代码实现
html:
<a href="http://127.0.0.1:8080/pathAbso">带网络三要素的绝对路径</a><br>
<a href="/pathAbso">不带网络三要素的绝对路径</a><br>
Servlet:
package com.itheima.sh.b_servlet_02;
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 PathAbso01Servlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("get....");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("post....");
}
}
web.xml
<servlet>
<servlet-name>pathAbso01Servlet</servlet-name>
<servlet-class>com.itheima.sh.a_servlet_01.PathAbso01Servlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>pathAbso01Servlet</servlet-name>
<url-pattern>/pathAbso</url-pattern>
</servlet-mapping>
12.相对路径
【1】相对路径:不是相对当前项目,而是针对当前浏览器地址栏上的url而言。
【2】案例一:
#假设我们在浏览器地址栏访问的页面路径: http://localhost:8080/demo01.html
#而在demo01.html页面想使用相对路径访问servlet:
#http://localhost:8080/pathAbso
说明:
如果在http://localhost:8080/demo01.html
页面中访问 http://localhost:8080/pathAbso 该servlet,
我们通过url观察发现只有最后一级目录不一样,
所以在demo01.html页面中相对的路径的写法是:
./pathAbso 这里的./表示当前路径 可以省略不写即直接写 pathAbso
demo01.html
<a href="./pathAbso">相对路径</a><br>
<a href="pathAbso">相对路径</a><br>
【3】案例二:
# 如果在http://localhost:8080/aaa/demo02.html
# 页面中访问 http://localhost:8080/pathAbso 该servlet
我们通过url观察发现在demo02.html也面中书写访问的servlet即pathAbso
和当前页面的父目录aaa是同等目录,所以我这里先找该页面的父目录,
然后在找该servlet即pathAbso
../pathAbso ../表示上一级目录或者父目录,
找到父目录之后再找servlet即pathAbso
demo02.html
<a href="../pathAbso">相对路径</a><br>
13.Servlet3.0注解开发
【1】问题
说明:之前我们都是使用web.xml进行servlet映射路径的配置。
这样配置的弊端:web.xml中具有非常多个配置信息,显得非常臃肿并且容易出错。
【2】解决问题
使用web.xml配置映射路径的方式属于servlet2.5的技术。从servlet3.0开始引入注解配置访问servlet取代了web.xml配置。
<!-- TODO:想使用注解开发导入的Servlet依赖必须是3.0以上的 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
【3】配置步骤
1.在包上右键---new---servlet(create new Servlet)
2.输入类名
3.在方法体内输入逻辑代码
4.在浏览器地址栏中输入访问的路径
【4】实现
1.在包上右键—new—servlet(create new Servlet)
2.输入类名
3.在方法体内输入逻辑代码
package com.itheima.sh.c_servlet_anno_03;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/annoDemo01Servlet")
public class AnnoDemo01Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("注解开发");
}
}
4.在浏览器地址栏中输入访问的路径
14.修改idea创建注解的servlet模板
(1)File—Settings
(2)找到servlet的模板,按照如下修改
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end
#parse("File Header.java")
@javax.servlet.annotation.WebServlet("/${Entity_Name}")
public class ${Class_Name} extends javax.servlet.http.HttpServlet {
protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException {
doGet(request,response);
}
protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException {
}
}
15.关于servlet3.0注解开发的疑问,配置路径省略了属性urlPatterns
正常我们在WebServlet注解中配置的路径赋值给属性urlPatterns,但是urlPatterns不能省略的,在一个注解中,如果含有多个属性,并且每个属性都有默认值,此时给value赋值可以省略value属性。
注意:在WebServlet注解中value等同于urlPatterns属性。
十.HTTP协议
1.HTTP协议介绍
【1】概念
全称:超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议。
简而言之:浏览器和服务器数据交换固定的格式。
【2】版本
HTTP协议版本如下:
- http1.0(1996):每次请求都有一个新的连接(开销大,比较慢)
- http1.1(1999):长连接,多个请求共用一个连接(开销小一些,比较快)传输的数据都是文本(比较慢)
【3】分类
- 请求报文协议(浏览器发送给服务器的数据):1)请求行 2)请求头 3)请求体
- 响应报文协议(服务器发送给浏览器的数据):1)响应行 2)响应头 3)响应体
【4】特点
- 先有请求
- 再有响应
- 一个请求对应一个响应
2.浏览器抓包观察请求报文协议
【1】步骤
1.创建html页面
2.在html页面书写html代码
3.创建servlet
4.在servlet中书写java代码
5.启动服务器
6.打开浏览器,在浏览器访问页面,然后按f12 点击网络 network
7.抓包结果分析
【2】实现
1.创建html页面
2.在html页面书写html代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>GET请求</h2>
<form action="/getServlet" method="get">
用户名:<input type="text" name="username" value="zhangsan" /><br>
密码:<input type="text" name="pwd" value="123" /><br>
<input type="submit" value="提交"/>
</form>
<h2>POST请求</h2>
<form action="/postServlet" method="post">
用户名:<input type="text" name="username" value="zhangsan"/><br>
密码:<input type="text" name="pwd" value="123"/><br>
<input type="submit" value="提交"/>
</form>
</body>
</html>
3.创建servlet
4.在servlet中书写java代码
package com.itheima.sh.d_http_04;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/getServlet")
public class GetServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("get....");
}
}
package com.itheima.sh.d_http_04;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/postServlet")
public class PostServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("post....");
}
}
5.启动服务器
6.打开浏览器,在浏览器访问页面,然后按f12 点击网络
7.抓包结果分析
- 请求报文协议的get请求和post请求区别:
1.GET请求:
1)请求行直接传递请求参数.将请求参数追加在URL后面,不安全。
例如:form.html?username=jack&username=1234
补充:但是get请求可以作为商品的分享。
2)URL长度限制(不同浏览器限制大小不一致),GET请求方式的数据大小,
不可以传输数据量较大或者非文本数据。例如图片或者视频。
3)请求体里面没有内容。
2.POST请求:
1)请求参数以请求体形式发送给服务器,数据传输安全。
2)请求数据可以为非文本数据,可以传输数据量较大的数据。
3)只有表单设置为method=”post”才是post请求.
或者axios.post()也属于post请求
其他的都是get请求。常见GET请求:地址栏直接访问、
<a href=””>、<img src=””> location.href="" 、axios.get()等
十一.Request & Response
1.Request和Response的概述
# 重点
1.service方法的两个参数request和response是由tomcat创建的
void service(ServletRequest varl,ServletResponse var2)
2.request 表示请求数据,tomcat将浏览器发送过来的请求数据解析并封装到
request对象中,servlet开发者可以通过request对象获得请求数据
3.response 表示响应数据,服务器发送给浏览器的数据
servlet开发者可以通过response对象设置响应数据
==Request是请求对象,Response是响应对象。==这两个对象在我们使用Servlet的时候有看到:
此时,我们就需要思考一个问题request和response这两个参数的作用是什么?
- request:获取请求数据
- 浏览器会发送HTTP请求到后台服务器[Tomcat]
- HTTP的请求中会包含很多请求数据[请求行+请求头+请求体]
- 后台服务器[Tomcat]会对HTTP请求中的数据进行解析,并把解析结果存入到一个对象中
- 所存入的对象即为request对象,所以我们可以从request对象中获取请求的相关参数
- 获取到数据后就可以继续后续的业务,不如获取用户名和密码就可以实现登录操作的相关业务
- response:设置响应数据
- 业务处理完后,后台就需要给前端返回业务处理的结果即响应数据
- 把响应数据封装到response对象中
- 后台服务器[Tomcat]会解析response对象,按照[响应行+响应头+响应体]格式拼接结果
- 浏览器最终解析结果,把内容展示在浏览器给用户浏览
案例:
@WebServlet("/demo3")
public class ServletDemo3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//使用request对象 获取请求数据
String name = request.getParameter("name");//url?name=zhangsan
//使用response对象 设置响应数据
response.setHeader("content-type","text/html;charset=utf-8");
response.getWriter().write("<h1>"+name+",欢迎您!</h1>");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("Post...");
}
}
启动成功后就可以通过浏览器来访问,并且根据传入参数的不同就可以在页面上展示不同的内容:
2.Request对象
(1)Request继承体系
- 当我们的Servlet类实现的是Servlet接口的时候,service方法中的参数是ServletRequest和ServletResponse
- 当我们的Servlet类继承的是HttpServlet类的时候,doGet和doPost方法中的参数就变成HttpServletRequest和HttpServletResponse
那么?
- ServletRequest和HttpServletRequest的关系是什么?
- request对象是由谁来创建的?
- request提供了哪些API,这些API从哪里查?
首先,我们先看下Request的继承体系:
ServletRequest request = new RequestFacade();
从上图中可以看出,ServletRquest和HttpServletRequest都是java提供的,所以我们可以打开JavaEE提供的API文档[参考资料中的JavaEE7-api.chm],打开后可以看到:
所以ServletRequest和HttpServletRequest是继承关系,并且两个都是接口,接口是无法创建对象,这个时候就引发; 下面这个问题:
这个时候,我们就需要用到Request继承体系中的RequestFacade:
- 该类实现了HttpServletRequest接口,也间接实现了ServletRequest接口。
- Servlet类中的service方法、doGet方法或者是doPost方法最终都是由Web服务器[Tomcat]来调用的,所以Tomcat提供了方法参数接口的具体实现类,并完成了对象的创建
- 要想了解RequestFacade中都提供了哪些方法,我们可以直接查看JavaEE的API文档中关于ServletRequest和HttpServletRequest的接口文档,因为RequestFacade实现了其接口就需要重写接口中的方法
对于上述结论,要想验证,可以编写一个Servlet,在方法中把request对象打印下,就能看到最终的对象是不是RequestFacade,
@WebServlet("/demo2")
public class ServletDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println(request);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
启动服务器,运行访问http://localhost:8080/request-demo/demo2
,得到运行结果:
(2)Request获取请求数据
HTTP请求数据总共分为三部分内容,分别是请求行、请求头、请求体,对于这三部分内容的数据,分别该如何获取,首先我们先来学习请求行数据如何获取?
【1】获取请求行数据
请求行包含三块内容,分别是请求资源路径
、请求方式
、HTTP协议及版本
对于这个三部分内容,request对象都提供了对于的API方法来获取,具体如下:
- 获取请求方式:GET
String getMethod()
- 获取虚拟目录(项目访问路径):/request-demo
String getContextPath()
- 获取URL(统一资源定位符):
http://localhost:8080/request-demo/req1
StringBuffer getRequestURL()
- 获取URI(统一资源标识符):
/request-demo/req1
String getRequestURI()
- 获取请求参数(GET方式):
username=zhangsan&password=123
String getQueryString()
案例:
/**
* request 获取请求数据
*/
@WebServlet("/req1")
public class RequestDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// String getMethod():获取请求方式: GET
String method = req.getMethod();
System.out.println(method);//GET
// String getContextPath():获取虚拟目录(项目访问路径):/request-demo
String contextPath = req.getContextPath();
System.out.println(contextPath);
// StringBuffer getRequestURL(): 获取URL(统一资源定位符):http://localhost:8080/request-demo/req1
StringBuffer url = req.getRequestURL();
System.out.println(url.toString());
// String getRequestURI():获取URI(统一资源标识符): /request-demo/req1
String uri = req.getRequestURI();
System.out.println(uri);
// String getQueryString():获取请求参数(GET方式): username=zhangsan
String queryString = req.getQueryString();
System.out.println(queryString);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
启动服务器,访问http://localhost:8080/request-demo/req1?username=zhangsan&passwrod=123
,获取的结果如下:
【2】获取请求头数据
对于请求头的数据,格式为 key:value如下:
所以根据请求头名称获取对应值的方法为:
String getHeader(String name)
参数name书写的是请求头冒号左边的内容例如:User-Agent
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/96.0.4664.110 Safari/537.36
接下来,在代码中如果想要获取客户端浏览器的版本信息,则可以使用
/**
* request 获取请求数据
*/
@WebServlet("/req1")
public class RequestDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取请求头: user-agent: 浏览器的版本信息
String agent = req.getHeader("user-agent");
System.out.println(agent);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
重新启动服务器后,http://localhost:8080/request-demo/req1?username=zhangsan&passwrod=123
,获取的结果如下:
【3】获取请求体数据
浏览器在发送GET请求的时候是没有请求体的,所以需要把请求方式变为POST,请求体中的数据格式如下:
对于请求体中的数据,Request对象提供了如下两种方式来获取其中的数据,分别是:
- 获取字节输入流,如果前端发送的是字节数据,比如传递的是文件数据,则使用该方法
ServletInputStream getInputStream()
该方法可以获取字节和字符数据
- 获取字符输入流,如果前端发送的是纯文本数据,则使用该方法
BufferedReader getReader()
接下来,大家需要思考,要想获取到请求体的内容该如何实现?
具体实现的步骤如下:
1.准备一个页面,在页面中添加form表单,用来发送post请求
2.在Servlet的doPost方法中获取请求体数据
3.在doPost方法中使用request的getReader()或者getInputStream()来获取
4.访问测试
- 在项目的webapp目录下添加一个html页面,名称为:
req.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--
action:form表单提交的请求地址
method:请求方式,指定为post
-->
<form action="/request-demo/req1" method="post">
<input type="text" name="username">
<input type="password" name="password">
<input type="submit">
</form>
</body>
</html>
- 在Servlet的doPost方法中获取数据
/**
* request 获取请求数据
*/
@WebServlet("/req1")
public class RequestDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//在此处获取请求体中的数据
}
}
- 调用getReader()或者getInputStream()方法,因为目前前端传递的是纯文本数据,所以我们采用getReader()方法来获取
/**
* request 获取请求数据
*/
@WebServlet("/req1")
public class RequestDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取post 请求体:请求参数
//1. 获取字符输入流
BufferedReader br = req.getReader();
//2. 读取数据
String line = br.readLine();
System.out.println(line);
}
}
注意
BufferedReader流是通过request对象来获取的,当请求完成后request对象就会被销毁,request对象被销毁后,BufferedReader流就会自动关闭,所以此处就不需要手动关闭流了。
4.启动服务器,通过浏览器访问http://localhost:8080/request-demo/req.html
点击提交
按钮后,就可以在控制台看到前端所发送的请求数据
【4】获取请求参数的通用方式:
1.什么是请求参数?
为了能更好的回答上述两个问题,我们拿用户登录的例子来说明
1.1 想要登录网址,需要进入登录页面
1.2 在登录页面输入用户名和密码
1.3 将用户名和密码提交到后台
1.4 后台校验用户名和密码是否正确
1.5 如果正确,则正常登录,如果不正确,则提示用户名或密码错误
上述例子中,用户名和密码其实就是我们所说的请求参数。
get请求:请求参数位于url后面。
post请求:请求参数位于请求体中。
2.什么是请求数据?
请求数据则是包含请求行、请求头和请求体的所有数据
3.请求参数和请求数据的关系是什么?
3.1 请求参数是请求数据中的部分内容
3.2 如果是GET请求,请求参数在请求行中
3.3 如果是POST请求,请求参数一般在请求体中
对于请求参数的获取,常用的有以下两种:
- GET方式:
String getQueryString()
- POST方式:
BufferedReader getReader();
有了上述的知识储备,我们来实现一个案例需求:
(1)发送一个GET请求并携带用户名,后台接收后打印到控制台
(2)发送一个POST请求并携带用户名,后台接收后打印到控制台
此处大家需要注意的是GET请求和POST请求接收参数的方式不一样,具体实现的代码如下:
@WebServlet("/req1")
public class RequestDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String result = req.getQueryString();
System.out.println(result);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
BufferedReader br = req.getReader();
String result = br.readLine();
System.out.println(result);
}
}
GET请求和POST请求获取请求参数的方式不一样,在获取请求参数这块该如何实现呢?
要想实现,我们就需要思考:
GET请求方式和POST请求方式区别主要在于获取请求参数的方式不一样,是否可以提供一种统一获取请求参数的方式,从而统一doGet和doPost方法内的代码?
解决方案一:
@WebServlet("/req1")
public class RequestDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取请求方式
String method = req.getMethod();
//获取请求参数
String params = "";
if("GET".equals(method)){
params = req.getQueryString();
}else if("POST".equals(method)){
BufferedReader reader = req.getReader();
params = reader.readLine();
}
//将请求参数进行打印控制台
System.out.println(params);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
}
}
使用request的getMethod()来获取请求方式,根据请求方式的不同分别获取请求参数值,这样就可以解决上述问题,但是以后每个Servlet都需要这样写代码,实现起来比较麻烦,这种方案我们不采用
解决方案二:
request对象已经将上述获取请求参数的方法进行了封装,并且request提供的方法实现的功能更强大,以后只需要调用request提供的方法即可,在request的方法中都实现了哪些操作?
(1)根据不同的请求方式获取请求参数,获取的内容如下:
(2)把获取到的内容进行分割,内容如下:
(3)把分割后端数据,存入到一个Map集合
注意:因为参数的值可能是一个,也可能有多个,所以Map的值的类型为String数组。
基于上述理论,request对象为我们提供了如下方法:
- 获取所有参数Map集合
Map<String,String[]> getParameterMap()
- 根据名称获取参数值(数组)
String[] getParameterValues(String name)
参数name是前端提交请求参数的等号左边的key(name)==>username=zhangsan===>
想获取zhangsan===>getParameterValues("username");
- 根据名称获取参数值(单个值)
String getParameter(String name)
注意:如果参数的name有多个值谁在前面先获取谁
接下来,我们通过案例来吧上述的三个方法进行实例演示:
1.修改req.html页面,添加爱好选项,爱好可以同时选多个
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/request-demo/req2" method="get">
<input type="text" name="username"><br>
<input type="password" name="password"><br>
<input type="checkbox" name="hobby" value="1"> 游泳
<input type="checkbox" name="hobby" value="2"> 爬山 <br>
<input type="submit">
</form>
</body>
</html>
2.在Servlet代码中获取页面传递GET请求的参数值
2.1获取GET方式的所有请求参数
/**
* request 通用方式获取请求参数
*/
@WebServlet("/req2")
public class RequestDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//GET请求逻辑
System.out.println("get....");
//1. 获取所有参数的Map集合
Map<String, String[]> map = req.getParameterMap();
for (String key : map.keySet()) {
// username:zhangsan lisi
System.out.print(key+":");
//获取值
String[] values = map.get(key);
for (String value : values) {
System.out.print(value + " ");
}
System.out.println();
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
获取的结果为:
2.2获取GET请求参数中的爱好,结果是数组值
/**
* request 通用方式获取请求参数
*/
@WebServlet("/req2")
public class RequestDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//GET请求逻辑
//...
System.out.println("------------");
String[] hobbies = req.getParameterValues("hobby");
for (String hobby : hobbies) {
System.out.println(hobby);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
获取的结果为:
2.3获取GET请求参数中的用户名和密码,结果是单个值
/**
* request 通用方式获取请求参数
*/
@WebServlet("/req2")
public class RequestDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//GET请求逻辑
//...
String username = req.getParameter("username");
String password = req.getParameter("password");
System.out.println(username);
System.out.println(password);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
获取的结果为:
3.在Servlet代码中获取页面传递POST请求的参数值
3.1将req.html页面form表单的提交方式改成post
3.2将doGet方法中的内容复制到doPost方法中即可
(3)解决post请求乱码问题
html页面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/httpServletRequestDemo04Servlet" method="post">
<input type="text" name="username"><br>
<input type="submit">
</form>
</body>
</html>
【1】从tomcat8开始以后,对于get请求乱码,tomcat已经解决。对于post请求中文乱码没有解决,需要我们自己处理。
【2】post请求乱码产生的原因和解决思路
说明:
1)页面使用的编码表是UTF-8编码,tomcat使用的是默认编码表ISO-8859-1进行解码,编码和解码使用的编码表不一致导致乱码。
2)解决思路:先按照ISO-8859-1编码,在按照UTF-8进行重新解码
【3】解决方案
解决方案有三种:
- 方案一
使用URLEncoder类进行编码:static String encode (String s,String enc)
参数:
s:编码的字符串
enc:使用编码表
使用URLDecoder进行解码:static Sring decode(String s,String enc)
参数:
s:解码的字符串
enc:使用编码表
- 方案二
使用String类中的方法进行编码:byte[] getBytes(String charsetName)
参数表示指定的编码表,返回值表示编码后的字
使用String类中的构造方法进行解码:String(byte[] bytes,String charsetName)
参数:
bytes:字节数组
charsetName:表示指定的编码表
返回值:解码后的字符串
- 方案三
如果是get请求,tomcat8底层已经帮助我们解决完了,我们只需要解决post乱码即可,
但是上述两种方式对于post请求可以解决乱码,对于get请求本身获取到的已经是正确的数据,
处理后又乱码了。
我们的想法是:get请求不用我们自己书写代码处理乱码,
只需要我们书写代码处理post乱码。
我们接下来学习第三种解决方案:
只解决来自于请求体数据的乱码。而get请求体没有数据,post请求体含有数据,
所以我们可以理解为第三种处理方案
只是用来解决post乱码的。使用的api是ServletRequest接口中的:
void setCharacterEncoding(String env)
参数:指定的编码表
注意:该方式的代码必须书写在获取请求数据之前
【4】代码实现
package com.itheima.sh.web;
import com.itheima.sh.pojo.User;
import com.itheima.sh.service.UserServcie;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLDecoder;
import java.net.URLEncoder;
@WebServlet("/httpServletRequestDemo04Servlet")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取浏览器的请求数据
// String username = request.getParameter("username");
/*
解决post乱码问题有三种方式:
【1】方式一
使用URLEncoder类进行编码:static String encode(String s, String enc)
参数:
s:编码的字符串
enc:使用编码表
使用URLDecoder进行解码:static String decode(String s, String enc)
参数:
s:解码的字符串
enc:使用编码表
*/
//1)编码 : 使用URLEncoder类进行编码:static String encode(String s, String enc)
// String encodeUsername = URLEncoder.encode(username, "ISO-8859-1");
// //2)解码:使用URLDecoder进行解码:static String decode(String s, String enc)
// username = URLDecoder.decode(encodeUsername, "UTF-8");
/*
解决post乱码问题有三种方式:
【2】方式二:
使用String类中的方法进行编码: byte[] getBytes(String charsetName)
参数表示指定的编码表,返回值表示编码后的字节数组
使用String类中的构造方法进行解码:String(byte[] bytes, String charsetName)
参数:
bytes:字节数组
charsetName:表示指定的编码表
返回值:解码后的字符串
*/
//1)编码 : 使用String类中的方法进行编码: byte[] getBytes(String charsetName)
// byte[] bytes = username.getBytes("ISO-8859-1");
// //2)解码:使用String类中的构造方法进行解码:String(byte[] bytes, String charsetName)
// username = new String(bytes, "UTF-8");
//username = new String(username.getBytes("ISO-8859-1"), "UTF-8");
/*
解决post乱码问题有三种方式:
【3】方式三:
如果是get请求,tomcat8底层已经帮助我们解决完了,我们只需要解决post乱码即可,但是上述
两种方式对于post请求可以解决乱码,对于get请求本身获取到的已经是正确的数据,处理
后又乱码了。
我们的想法是:get请求不用我们自己书写代码处理乱码,只需要我们书写代码处理post乱码。
我们接下来学习第三种解决方案:
只解决来自于请求体数据的乱码。而get请求体没有数据,post请求体含有数据,所以我们可以理解为第三种处理方案只是用来解决
post乱码的。使用的api是ServletRequest接口中的:
void setCharacterEncoding(String env)
参数:指定的编码表
注意:该方式的代码必须书写在获取请求数据之前
*/
request.setCharacterEncoding("utf-8");//告知tomcat使用UTF-8解码页面请求数据
// 1.获取浏览器的请求数据
String username = request.getParameter("username");
System.out.println("username = " + username);
}
}
(4)Request请求转发
前后端分离后使用较少,面试考
1.请求转发(forward):一种在服务器内部的资源跳转方法。
(1)浏览器发送请求给服务器,服务器中对应的资源A接收到请求
(2)资源A处理完请求后将请求发给资源B
(3)资源B处理完后将结果响应给浏览器
(4)请求从资源A到资源B的过程就叫请求转发
2.请求转发的实现方式:
req.getRequestDispatcher("资源B路径").forward(req,resp);
说明:
1)获取转发器:RequestDispatcher dispatcher = req.getRequestDispatcher("资源B路径");
2)转发:RequestDispatcher 表示转发器
该接口中有一个方法:forward(request,response)
具体如何来使用,我们先来看下需求:
针对上述需求,具体的实现步骤为:
1.创建一个RequestDemo5类,接收/req5的请求,在doGet方法中打印demo5
2.创建一个RequestDemo6类,接收/req6的请求,在doGet方法中打印demo6
3.在RequestDemo5的方法中使用
req.getRequestDispatcher("/req6").forward(req,resp)进行请求转发
4.启动测试
(1)创建RequestDemo5类
/**
* 请求转发
*/
@WebServlet("/req5")
public class RequestDemo5 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("demo5...");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
(2)创建RequestDemo6类
/**
* 请求转发
*/
@WebServlet("/req6")
public class RequestDemo6 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("demo6...");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
(3)在RequestDemo5的doGet方法中进行请求转发
/**
* 请求转发
*/
@WebServlet("/req5")
public class RequestDemo5 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("demo5...");
//请求转发
request.getRequestDispatcher("/req6").forward(request,response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
(4)启动测试
访问http://localhost:8080/request-demo/req5
,就可以在控制台看到如下内容:
说明请求已经转发到了/req6
3.请求转发资源间共享数据:使用Request域对象
此处主要解决的问题是把请求从/req5转发到/req6的时候,如何传递给/req6
需要使用request对象提供的三个方法:
- 存储数据到request域[范围,数据是存储在request对象]中
void setAttribute(String name,Object o);
- 根据key获取值
Object getAttribute(String name);
- 根据key删除该键值对
void removeAttribute(String name);
接着上个需求来:
1.在RequestDemo5的doGet方法中转发请求之前,将数据存入request域对象中
2.在RequestDemo6的doGet方法从request域对象中获取数据,并将数据打印到控制台
3.启动访问测试
(1)修改RequestDemo5中的方法
@WebServlet("/req5")
public class RequestDemo5 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("demo5...");
//存储数据
request.setAttribute("msg","hello");
//请求转发
request.getRequestDispatcher("/req6").forward(request,response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
(2)修改RequestDemo6中的方法
/**
* 请求转发
*/
@WebServlet("/req6")
public class RequestDemo6 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("demo6...");
//获取数据
Object msg = request.getAttribute("msg");
System.out.println(msg);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
(3)启动测试
访问http://localhost:8080/request-demo/req5
,就可以在控制台看到如下内容:
此时就可以实现在转发多个资源之间共享数据。
4.请求转发的特点
-
浏览器地址栏路径不发生变化
虽然后台从
/req5
转发到/req6
,但是浏览器的地址一直是/req5
,未发生变化
-
只能转发到当前服务器的内部资源
不能从一个服务器通过转发访问另一台服务器
-
一次请求,可以在转发资源间使用request共享数据
虽然后台从/req5转发到/req6,但是这个只有一次请求
-
问题:request.getParameter()
request.getParameter(String name)和request.getAttribute(String name);
区别
1.request.getParameter(String name):
获取来自于浏览器的数据 <input type="text" name="username" value="锁哥"/>
request.getParameter("username"); 获取的是锁哥
2.request.getAttribute(String name)
获取的是服务器中的代码:request.setAttibute(String name,Object obj);
的数据
request.setAttribute("msg","黑马程序员");
String msg = (String) request.getAttribute("msg");
(5)request的生命周期
1.何时创建?
浏览器第一次访问tomcat服务器的时候
2.谁创建?
tomcat
3.创建对象做什么?
浏览器第一次访问tomcat服务器的是,tomcat创建request和response对象,传递给servlet中的service方法,然后我们可以在servlet中使用request对象调用方法获取请求数据(请求行 头 体),然后处理业务逻辑,处理完毕,然后tomcat将响应数据给浏览器,浏览器接收到响应之后,tomcat立刻销毁request和response对象。
3.HTTP响应详解
(1)使用抓包查看响应报文协议内容
http响应报文协议包括:
1.响应行
2.响应头
3.响应体
响应数据:是服务器响应给浏览器
【1】步骤
1.创建html页面
2.创建servlet
【2】实现
1.创建html页面
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h2>get请求</h2>
<form action="/getServlet" method="get">
用户名:<input type="text" name="username" value="suoge" /> <br/>
密码:<input type="text" name="password" value="1234" /> <br/>
<input type="submit" value="get提交" />
</form>
<h2>post请求</h2>
<form action="/postServlet" method="post">
用户名:<input type="text" name="username" value="suoge" /> <br/>
密码:<input type="text" name="password" value="1234" /> <br/>
<input type="submit" value="post提交" />
</form>
</body>
</html>
2.创建servlet
package com.itheima.sh.a_http_01;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/getServlet")
public class GetServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//响应给浏览器数据
response.getWriter().print("get....");
}
}
package com.itheima.sh.a_http_01;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/postServlet")
public class PostServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//响应给浏览器数据
response.getWriter().print("post....");
}
}
【3】抓包结果
小结:
1.由于浏览器的原因,浏览器会把请求行和响应行信息放在一起;
2.get和post请求的响应没有区别
(2)HTTP响应报文协议介绍
【1】响应行格式:协议/版本 状态码
如:HTTP/1.1 200;
状态码 | 状态码描述 | 说明 |
---|---|---|
200 | OK | 请求已成功,请求所希望的响应头或数据体将随此响应返回。出现此状态码是表示正常状态。 |
302 | Move temporarily | 重定向,请求的资源临时从不同的地址响应请求 |
304 | Not Modified | 从浏览器缓存中读取数据,不从服务器重新获取数据。例如,用户第一次从浏览器访问服务器端图片资源,以后再访问该图片资源的时候就不会在从服务器上加载而直接到浏览器缓存中加载,这样效率更高。 |
404 | Not Found | 请求资源不存在。通常是用户路径编写错误,也可能是服务器资源已删除。 |
403 | Forbidden | 服务器已经理解请求,但是拒绝执行它 |
405 | Method Not Allowed | 请求行中指定的请求方法不能被用于请求相应的资源 |
500 | Internal Server Error | 服务器内部错误,通常程序抛异常 |
【2】响应头
响应头也是用的键值对key:value,服务器基于响应头通知浏览器的行为。
常见的响应头:
响应头Key | 响应头value |
---|---|
location | 指定响应的路径,需要与状态码302配合使用,完成重定向 |
content-Type | 响应正文的类型(MIME类型,属于服务器里面的一种类型,例如文件在window系统有自己的类型,.txt .doc .jpg。文件在服务器中也有自己的类型),同时还可以解决乱码问题。例如:text/html;charset=UTF-8 |
content-disposition | 通过浏览器以附件形式解析正文,例如:attachment;filename= xx.zip |
refresh | 页面刷新,例如:3;url=www.itcast.cn //三秒刷新页面到www.itcast.cn |
常见的MIME类型:就是文件在tomcat服务器中的文件类型:
windows tomcat(MIME类型)
超文本标记语言文本 .html text/html ***
xml文档 .xml text/xml
XHTML文档 .xhtml application/xhtml+xml
普通文本 .txt text/plain ***
PDF文档 .pdf application/pdf
Microsoft Word文件 .word application/msword
PNG图像 .png image/png **
GIF图形 .gif image/gif
JPEG图形 .jpeg,.jpg image/jpeg **
......
【3】响应体
服务器发送给浏览器的数据。当前浏览器向服务器请求的资源是hello.html,所以服务器给浏览器响应的数据是一个html页面。
请求资源路径:localhost:8080/hello.html
响应结果:
如果请求是servlet,那么浏览器的响应体接收到的是servlet响应的数据:
4.Response对象
- Request:使用request对象来获取请求数据
- Response:使用response对象来设置响应数据
Reponse的继承体系和Request的继承体系也非常相似:
HttpServletResponse response = new ResponseFacade();多态
(1)Response设置响应数据功能介绍
HTTP响应数据总共分为三部分内容,分别是响应行、响应头、响应体,对于这三部分内容的数据,respone对象都提供了哪些方法来进行设置?
- 响应行
对于响应行,比较常用的就是设置响应状态码:
void setStatus(int sc);
- 响应头
设置响应头键值对:
void setHeader(String name,String value);
响应头:name的值
location 指定响应的路径
content-type:告诉浏览器文件格式,告诉浏览器不要解析html文件(text/plain),解决中文乱码问题 ************
refresh 定时刷新
content-disposition 以附件形式展示图片等资源
3.响应体
对于响应体,是通过字符、字节输出流的方式往浏览器写,
获取字符输出流:
PrintWriter getWriter();
获取字节输出流
ServletOutputStream getOutputStream();
介绍完这些方法后,后面我们会通过案例把这些方法都用一用,首先先来完成下重定向的功能开发。
(2)Responses请求重定向
- Response重定向(redirect):一种资源跳转方式(服务器外部的)。
(1)浏览器发送请求给服务器,服务器中对应的资源A接收到请求
(2)资源A现在无法处理该请求,就会给浏览器响应一个302的状态码+location的一个访问资源B的路径
(3)浏览器接收到响应状态码为302就会重新发送请求到location对应的访问地址去访问资源B
(4)资源B接收到请求后进行处理并最终给浏览器响应结果,这整个过程就叫重定向
- 重定向的实现方式:
resp.setStatus(302);设置响应状态码是302
resp.setHeader("location","资源B的访问路径");
具体如何来使用,我们先来看下需求:
1.创建一个ResponseDemo1类,接收/resp1的请求,在doGet方法中打印
resp1....
2.创建一个ResponseDemo2类,接收/resp2的请求,在doGet方法中打印
resp2....
3.在ResponseDemo1的方法中使用
response.setStatus(302);
response.setHeader(“Location”,“/request-demo/resp2”) 来给前端响应结果数据
4.启动测试
(1)创建ResponseDemo1类
@WebServlet("/resp1")
public class ResponseDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("resp1....");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
(2)创建ResponseDemo2类
@WebServlet("/resp2")
public class ResponseDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("resp2....");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
(3)在ResponseDemo1的doGet方法中给前端响应数据
@WebServlet("/resp1")
public class ResponseDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("resp1....");
//重定向
//1.设置响应状态码 302
response.setStatus(302);
//2. 设置响应头 Location
response.setHeader("Location","/request-demo/resp2");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
(4)启动测试
访问http://localhost:8080/request-demo/resp1
,就可以在控制台看到如下内容:
说明/resp1
和/resp2
都被访问到了。到这重定向就已经完成了。
虽然功能已经实现,但是从设置重定向的两行代码来看,会发现除了重定向的地址不一样,其他的内容都是一模一样,所以resposne对象给我们提供了简化的编写方式为:
resposne.sendRedirect("/request-demo/resp2")
所以第3步中的代码就可以简化为:
@WebServlet("/resp1")
public class ResponseDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("resp1....");
//重定向
resposne.sendRedirect("/request-demo/resp2");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
-
重定向的特点:
-
浏览器地址栏路径发生变化:当进行重定向访问的时候,由于是由浏览器发送的两次请求,所以地址会发生变化
-
可以重定向到任何位置的资源(服务内部、外部均可)
因为第一次响应结果中包含了浏览器下次要跳转的路径,所以这个路径可以是任意位置资源。
-
两次请求,不能在多个资源使用request共享数据
因为浏览器发送了两次请求,是两个不同的request对象,就无法通过request对象进行共享数据
-
以后到底用哪个,还是需要根据具体的业务来决定。
# 如果需要在资源之间传递共享request数据,使用请求转发, 否则就用重定向
(3)路径问题
问题1:转发的时候路径上没有加/request-demo
而重定向加了,那么到底什么时候需要加,什么时候不需要加呢?
其实判断的依据很简单,只需要记住下面的规则即可:
- 浏览器使用:需要加虚拟目录(项目访问路径)
- 服务端使用:不需要加虚拟目录
对于转发来说,因为是在服务端进行的,所以不需要加虚拟目录
对于重定向来说,路径最终是由浏览器来发送请求,就需要添加虚拟目录。
掌握了这个规则,接下来就通过一些练习来强化下知识的学习:
<a href='路劲'>
<form action='路径'>
- req.getRequestDispatcher(“路径”)
- resp.sendRedirect(“路径”)
答案:
1.超链接,从浏览器发送,需要加
2.表单,从浏览器发送,需要加
3.重定向,是由浏览器进行跳转,需要加。
4.转发,是从服务器内部跳转,不需要加
(4)Response响应字符数据
要想将字符数据写回到浏览器,我们需要两个步骤:
- 通过Response对象获取字符输出流: PrintWriter writer = resp.getWriter(); 由于获取打印字符流是根据response对象获取的,所以向浏览器打印输出
- 通过字符输出流写数据: writer.write(“aaa”);
接下来,我们实现通过些案例把响应字符数据给实际应用下:
- 返回一个简单的字符串
aaa
/**
* 响应字符数据:设置字符数据的响应体
*/
@WebServlet("/resp3")
public class ResponseDemo3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
//1. 获取字符输出流
PrintWriter writer = response.getWriter();
writer.write("aaa");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
- 返回一串html字符串,并且能被浏览器解析
PrintWriter writer = response.getWriter();
//content-type,告诉浏览器返回的数据类型是HTML类型数据,这样浏览器才会解析HTML标签
response.setHeader("content-type","text/html");
writer.write("<h1>aaa</h1>");
==注意:==一次请求响应结束后,response对象就会被销毁掉,所以不要手动关闭流。
- 返回一个中文的字符串
你好
,需要注意设置响应数据的编码为utf-8
//设置响应的数据格式及数据的编码
response.setContentType("text/html;charset=utf-8");
writer.write("你好");
(5)Response响应字节数据
要想将字节数据写回到浏览器,我们需要两个步骤:
-
通过Response对象获取字节输出流:
ServletOutputStream outputStream = new resp.getOutputStream();
-
通过字节输出流写数据:outputStream.write(字节数据);
接下来,我们实现通过这些案例把响应字节数据给实际应用:
1.返回一个图片文件到浏览器
/**
* 响应字节数据:设置字节数据的响应体
*/
@WebServlet("/resp4")
public class ResponseDemo4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1. 读取文件
FileInputStream fis = new FileInputStream("D:\\abc\\柳岩.jpg");
//2. 获取response字节输出流
ServletOutputStream os = response.getOutputStream();
//3. 完成流的copy
byte[] buff = new byte[1024];
int len = 0;
while ((len = fis.read(buff))!= -1){
os.write(buff,0,len);
}
fis.close();
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
上述代码中,对于流的copy的代码还是比较复杂的,所以我们可以使用别人提供好的方法来简化代码的开发,具体步骤是:
(1)pom.xml添加一列
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
(2)调用工具类方法
//fis:输入流
//os:输出流
IOUtils.copy(fis,os);
优化后的代码
/**
* 响应字节数据:设置字节数据的响应体
*/
@WebServlet("/resp4")
public class ResponseDemo4 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1. 读取文件
FileInputStream fis = new FileInputStream("d://a.jpg");
//2. 获取response字节输出流
ServletOutputStream os = response.getOutputStream();
//3. 完成流的copy
IOUtils.copy(fis,os);
fis.close();
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
十二.Cookie
1.基本使用
(1)概念
Cookie:客户端会话技术,将数据保存到客户端,以后每次请求都携带Cookie数据进行访问。
(2)Cookie工作流程
- 服务端提供了两个Servlet,分别是ServletA和ServletB
- 浏览器发送HTTP请求1给服务端,服务端ServletA接收请求并进行业务处理
- 服务端ServletA在处理的过程中可以创建一个Cookie对象并将name=zs的数据存入Cookie
- 服务端ServletA在响应数据的时候,会把Cookie对象响应给浏览器
- 浏览器接收到响应数据,会把Cookie对象中的数据存储在浏览器内存中,此时浏览器和服务端就建立了一次会话
- 在同一次会话中浏览器再次发送HTTP请求2给服务端ServletB,浏览器会携带Cookie对象中的所有数据
- ServletB接收到请求和数据后,就可以获取到存储在Cookie对象中的数据,这样同一个会话中的多次请求之间就实现了数据共享
- **注意:**cookie是创建在服务器端,存在浏览器端
- 好处:减轻服务器压力,但是不安全
(3)Cookie的基本使用
对于Cookie的使用,我们更关注的应该是后台代码如何操作Cookie,对于Cookie的操作主要分两大类,分别是发送Cookie和获取Cookie,对于上面这两块内容,分别该如何实现呢?
3.1发送Cookie
- 创建Cookie对象,并设置数据
Cookie(String name,String value)
Cookie cookie = new Cookie("key","value");
- 发送Cookie到客户端:使用response对象
response.addCookie(cookie);
介绍完发送Cookie对应的步骤后,接下来通过一个案例来完成Cookie的发送,具体实验步骤为:
需求:在Servlet中生成Cookie对象并存入数据,然后将数据发送给浏览器
1.创建Maven项目,项目名称为cookie-demo,并在pom.xml添加依赖
2.编写Servlet类,名称为AServlet
3.在AServlet中创建Cookie对象,存入数据,发送给前端
4.启动测试,在浏览器查看Cookie对象中的值
(1)创建Maven项目cookie-demo,并在pom.xml添加依赖
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!--servlet-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
(2)编写Servlet类,名称为AServlet
@WebServlet("/aServlet")
public class AServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
(3)在Servlet中创建Cookie对象,存入数据,发送给前端
@WebServlet("/aServlet")
public class AServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//发送Cookie
//1. 创建Cookie对象
Cookie cookie = new Cookie("username","zs");
//2. 发送Cookie,response
response.addCookie(cookie);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
(4)启动测试,在浏览器查看Cookie对象中的值
访问http://localhost:8080/cookie-demo/aServlet
chrome浏览器查看Cookie的值,有两种方式,分布式:
方式一:
方式二:选中打开开发者工具或者 使用快捷键F12或者 Ctrl+Shift+I
3.2获取Cookie
- 获取客户端携带的所有Cookie,使用request对象
Cookie[] cookies = request.getCookies();
- 遍历数组,获取每一个Cookie对象:for
- 使用Cookie对象方法获取数据Cookie(String name,String value)
cookie.getName(); 获取Cookie类构造方法的第一个参数 name
cookie.getValue(); 获取Cookie类构造方法的第二个参数 value
介绍完获取Cookie对应的步骤后,接下来在通过一个案例来完成Cookie的获取,具体实现步骤为:
需求:在Servlet中获取前一个案例存入在Cookie对象中的数据
1.编写一个新Servlet类,名称为BServlet
2.在BServlet中使用request对象获取Cookie数组,遍历数组,
从数据中获取指定名称对应的值
3.启动测试,在控制台打印出获取的值
(1)编写一个新Servlet类,名称为BServlet
@WebServlet("/bServlet")
public class BServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
(2)在BServlet中使用request对象获取Cookie数组,遍历数组,从数据中获取指定名称对应的值
@WebServlet("/bServlet")
public class BServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取Cookie
//1. 获取Cookie数组
Cookie[] cookies = request.getCookies();
//2. 遍历数组
for (Cookie cookie : cookies) {
//3. 获取数据
String name = cookie.getName();
if("username".equals(name)){
String value = cookie.getValue();
System.out.println(name+":"+value);
break;
}
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
(3)启动测试,在控制台打印出获取的值
访问http://localhost:8080/cookie-demo/bServlet
在IDEA控制台就能看到输出的结果
2.Cookie的原理分析
对于Cookie的实现原理是基于HTTP协议的,其中涉及到HTTP协议中的两个头信息:
- 响应头:set-cookie
- 请求头:cookie
-
前面的案例中已经能够实现,AServlet给前端发送Cookie,BServlet从request中获取Cookie的功能
-
对于AServlet响应数据的时候,Tomcat服务器都是基于HTTP协议来响应数据
-
当Tomcat发现后端要返回的是一个Cookie对象之后,Tomcat就会在响应头中添加一行数据 Set-Cookie:username=zs
-
浏览器获取到响应结果后,从响应头中就可以获取到Set-Cookie对应值
usernames=zs,并将数据存储在浏览器的内存中
-
浏览器再次发送请求给BServlet的时候,浏览器会自动在请求头中添加Cookie:usernames=zs发送给服务端BServlet
-
Request对象会把请求头中cookie对应的值封装成一个个Cookie对象,最终形成一个数组
-
BServlet通过Request对象获取到Cookie[]后,就可以从中获取自己需要的数据
接下来,使用刚才的案例,把上述结论验证下:
(1)访问AServlet对应的地址http://localhost:8080/cookie-demo/aServlet
使用Chrom浏览器打开开发者工具(F12或Crtl+Shift+I)进行查看响应头中的数据
(2)访问BServlet对应的地址`http://localhost:8080/cookie-demo/bServlet
使用Chrom浏览器打开开发者工具(F12或Crtl+Shift+I)进行查看请求头中的数据
3.Cookie的使用细节
(1)Cookie的存活时间
cookie 有两种:
1.会话级别的cookie:会话结束,那么cookie消失
2.持久化级别的cookie:当前会话结束,cookie并没有消失,而是保存,
下次访问同一个服务器,cookie数据依然存在
问题:在访问AServlet和BServlet的中间把关闭浏览器,重启浏览器后访问BServlet能否获取到Cookie中的数据?
(1)浏览器发送请求给AServlet,AServlet会响应一个存有username=zs的Cookie对象给浏览器
(2)浏览器接收到响应数据将cookie存入到浏览器内存中
(3)当浏览器再次发送请求BServlet,BServlet就可以使用Request对象获取到Cookie数据
(4)在发送请求到BServlet之前,如果把浏览器关闭在打开进行访问,BServlet能否获取到Cookie数据?
针对上面这个问题,通过演示,会发现,BServlet中无法在获取到Cookie数据,这是为什么呢?
- 默认情况下,Cookie存储在浏览器内存中,当浏览器关闭,内存释放,则Cookie被销毁
这个结论就印证了上面的演示效果,但是如果使用这种默认情况下的Cookie,有些需求就无法实现,比如:
上面这个网站的登录页面上有一个记住我
的功能,这个功能大家都比较熟悉
- 第一次输入用户名和密码并勾选
记住我
然后进行登录 - 下次再登陆的时候,用户名和密码就会被自动填充,不需要再重新输入登录
- 比如
记住我
这个功能需要记住用户名和密码一个星期,那么使用默认情况下的Cookie就会出现问题 - 因为默认情况,浏览器一关,Cookie就会从浏览器内存中删除,对于
记住我
功能就无法实现
所以我们现在就遇到一个难题是如何将Cookie持久化存储?
Cookie其实已经为我们提供好了对应的API来完成这件事,这个API就是setMaxAge,
- 设置Cookie存活时间
setMaxAge(int seconds)
参数值为:
1.正数:将Cookie写入浏览器所在电脑的硬盘,持久化存储。到时间自动删除
2.负数:默认值,Cookie在当前浏览器内存中,当浏览器关闭,则Cookie被销毁
3.零:删除对应Cookie
接下来,咱们就在AServlet中设置Cookie的存活时间。
@WebServlet("/aServlet")
public class AServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//发送Cookie
//1. 创建Cookie对象
Cookie cookie = new Cookie("username","zs");
//设置存活时间 ,1周 7天
cookie.setMaxAge(60*60*24*7); //易阅读,需程序计算
//cookie.setMaxAge(604800); //不易阅读(可以使用注解弥补),程序少进行一次计算
//2. 发送Cookie,response
response.addCookie(cookie);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
修改完代码后,启动测试,访问http://localhost:8080/cookie-demo/aServlet
- 访问一个AServlet后,把浏览器关闭重启后,再去访问
http://localhost:8080/cookie-demo/bServet
,能在控制台打印出username:zs
,说明Cookie没有随着浏览器关闭而被销毁 - 通过浏览器查看Cookie的内容,会发现Cookie的相关信息
(2)关于cookie中存储特殊字符问题
如果直接向cookie中存储特殊字符,例如空格,分号( ; ) , 逗号(,)等特殊字符。那么就会出现问题。在向cookie中存储特殊字符之前必须要先进行编码处理,然后从cookie中取出之后再进行解码处理
【1】向cookie中存储特殊字符问题演示
package com.itheima.sh.b_cookie_02;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/specialCookie01Servlet")
public class SpecialCookie01Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*
向cookie中存储特殊字符问题演示
*/
//1.创建Cookie类的对象
Cookie cookie = new Cookie("msg", "12 34");
//2.将cookie存储到浏览器端
response.addCookie(cookie);
}
}
【2】解决向cookie中存储特殊字符的问题
方案:在向cookie中存储特殊字符前进行编码,然后取出之后需要解码。
【2】解决向cookie中存储特殊字符的问题
方案:在向cookie中存储特殊字符前进行编码,然后取出之后需要解码。
package com.itheima.sh.b_cookie_02;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
@WebServlet("/specialCookie01Servlet")
public class SpecialCookie01Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*
向cookie中存储特殊字符问题演示
*/
//1.创建Cookie类的对象
// Cookie cookie = new Cookie("msg", "12 34");报错
String str = "12 34";
//编码
String encode = URLEncoder.encode(str, "utf-8");
Cookie cookie = new Cookie("msg", encode);
//2.将cookie存储到浏览器端
response.addCookie(cookie);
}
}
package com.itheima.sh.b_cookie_02;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLDecoder;
@WebServlet("/specialCookie02Servlet")
public class SpecialCookie02Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取浏览器的cookie
Cookie[] cookies = request.getCookies();
//2.遍历cookie数组
for (Cookie cookie : cookies) {
//3.取出cookie的name
String cookieName = cookie.getName();
//4.判断cookieName的值是否是msg
if("msg".equals(cookieName)){
//5.取出value
String value = cookie.getValue();
//6.解码并输出
String decode = URLDecoder.decode(value, "utf-8");
System.out.println(decode);
}
}
}
}
内容小结
1.cookie中不能直接存储特殊字符:空格 分号等,如果存储必须先编码在存储:
String encode = URLEncoder.encode(str,"utf-8");
2.在获取的时候需要解码:
String decode = URLDecoder.decode(value,"utf-8");
十三.Session
1.基本使用
【1】概念
Session:服务端会话跟踪技术:将数据保存到服务端
- Session是存储在服务端而Cookie是存储在客户端
- 存储在客户端的数据容易被窃取和截获,存在很多不安全因素
- 存储在服务端的数据相比于客户端来说就更安全
【2】Session的工作流程
- 在服务端的AServlet获取一个Session对象,把数据存入其中。
- 在服务端的BServlet获取到相同的Session对象,从中取出数据。
- 就可以实现一次会话中多次请求之间的数据共享了
- 现在最大的问题是如何保证AServlet和BServlet使用的是同一个Session对象(在原理分析会讲解?)
【3】Session的基本使用
在JavaEE中提供了HttpSession接口,来实现一次会话的多次请求之间数据共享功能。
具体的使用步骤为:
- 获取Session对象,使用的是request对象
//如果是第一次执行那么就是创建session对象,如果不是第一次执行就是获取session
HttpSession session = request.getSession();
///参数是true用法和无参用法一样的,
//如果参数是false表示如果存在session就获取session,不存在则返回null;
HttpSession getSession(boolean create) ;
-
Session对象提供的功能:session也是域对象,使用范围比request大,只要在一次会话过程中多次请求都可以共享session中的数据。
-
存储数据到session域中
void setAttribute(String name,Object o)
-
根据key,获取值
Object getAttribute(String name)
-
根据key,删除该键值对
void removeAttribute(String name)
-
介绍完Session相关的API后,接下来通过一个案例来完成对Session的使用,具体实现步骤为:
需求:在一个Servlet中往Session中存入数据,在另一个Servlet中获取Session中
获取数据
1.创建名为SessionDemo1的Servlet类
2.创建名为SessionDemo2的Servlet类
3.在SessionDemo1的方法中:获取Session对象、存储数据
4.在SessionDemo2的方法中:获取Session对象、获取数据
5.启动测试
(1)创建名为SessionDemo1的Servlet类
@WebServlet("/demo1")
public class SessionDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
(2)创建名为SessionDemo2的Servlet类
@WebServlet("/demo2")
public class SessionDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
(3)SessionDemo1:获取Session对象、存储数据
@WebServlet("/demo1")
public class SessionDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//存储到Session中
//1. 获取Session对象
HttpSession session = request.getSession();
//2. 存储数据
session.setAttribute("username","zs");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
(4)SessionDemo2:获取Session对象、获取数据
@WebServlet("/demo2")
public class SessionDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取数据,从session中
//1. 获取Session对象
HttpSession session = request.getSession();
//2. 获取数据
Object username = session.getAttribute("username");
System.out.println(username);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
(5)启动测试
- 先访问
http://localhost:8080/cookie-demo/demo1
,将数据存入Session - 在访问
http://localhost:8080/cookie-demo/demo2
,从Session中获取数据 - 查看控制台
2.Session的原理分析
内容:
- 用户第一次访问的时候,tomcat会创建对应的session容器,每个容器具有唯一的标识,JSESSIONID,然后tomcat底层创建会话界别的cookie存储唯一标识JSESSIONID存储到浏览器端。
- 用户再次访问,tomcat中取出session并从cookie中取出之前保存的唯一标识JSESSIONID进行比较查找自己的session容器。
- 注意:Session是基于Cookie来实现的
- session和浏览器没有任何关系,存在服务器端的。
3.Session的使用细节
【1】浏览器关闭后,session持久化方案
tomcat在创建cookie的时候属于会话级别的cookie,关闭浏览器,cookie消失,下次打开浏览器不会携带之前的cookie,即cookie中的JSESSIONID到tomcat服务器中,那么这样会造成tomcat服务器中会有很多个不能使用的session容器。(session依然还在,只是找不到了)。严重的话会造成服务宕机。
package com.itheima.sh.e_session_05;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet("/sessionPersis01Servlet")
public class SessionPersis01Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取session
HttpSession session = request.getSession();
System.out.println(session.getId());
}
}
持久化session步骤:
- 创建session
- 获取session的JSESSIONID的值
- 创建Cookie,Cookie(“JSESSIONID”,值)
- 使用cookie对象调用方法setMaxAge()进行cookie的持久化,存活时间建议30min
- 将cookie响应给浏览器
package com.itheima.sh.e_session_05;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
@WebServlet("/sessionPersis01Servlet")
public class SessionPersis01Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.创建session
HttpSession session = request.getSession();
//2.获取session的JSESSIOID的值
String sessionId = session.getId();
System.out.println(sessionId);
//3.创建Cookie ,Cookie("JSESSIOID",值)
Cookie cookie = new Cookie("JSESSIONID", sessionId);
//4.使用cookie对象调用方法setMaxAge()进行cookie的持久化,存活时间建议30min
cookie.setMaxAge(60*30);
//5.将cookie响应给浏览器
response.addCookie(cookie);
}
}
【2】Session钝化和活化
由于钝化和活化的原理是序列化和反序列化,所以要求存储在session容器中的对象所属类必须实现序列化接口Serializable
演示:
- 代码:
package com.itheima.sh.b_session_02;
import java.io.Serializable;
public class Product implements Serializable {
//成员变量
private String pname;
private double price;
public Product(String pname, double price) {
this.pname = pname;
this.price = price;
}
public String getPname() {
return pname;
}
public void setPname(String pname) {
this.pname = pname;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "Product{" +
"pname='" + pname + '\'' +
", price=" + price +
'}';
}
}
package com.itheima.sh.f_session_06;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet("/setSessionServlet")
public class SetSessionServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
//1.创建session
HttpSession session = request.getSession();
//2.获取session的id
String sessionId = session.getId();
//3.创建商品对象
Product p = new Product("笔记本", 9999);
//4.将商品对象存储到session中
session.setAttribute("p",p);
//5.响应数据
response.getWriter().print("setSessionServlet.....当前JSESSIONID="+sessionId);
}
}
package com.itheima.sh.f_session_06;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet("/getSessionServlet")
public class GetSessionServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
//1.获取session
HttpSession session = request.getSession();
//2.获取session的id
String sessionId = session.getId();
//3.从session中取出商品
Product p = (Product) session.getAttribute("p");
//4.响应数据
response.getWriter().print("getSessionServlet.....当前JSESSIONID="+sessionId+",p="+p.toString());
}
}
- 打war包
将上述生成的war包复制到tomcat的webapps目录下:
到tomcat的bin目录下启动tomcat
注意:这儿启动一定将idea中的tomcat关闭
在浏览器中访问servlet
正常关闭tomcat:bin目录下面的shutdown.bat
在如下目录生成session的钝化文件:
正常启动tomcat
钝化文件就会被加载到内存,文件自动消失
【3】Session销毁
两种方式:
- 默认情况下,无操作,30分钟自动销毁
对于这个失效时间,是可以通过配置进行修改的
在项目的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_3_1.xsd"
version="3.1">
<session-config>
<session-timeout>10</session-timeout>
</session-config>
</web-app>
如果没有配置,默认是30分钟,默认值是在Tomcat的web.xml配置文件中写死的
- 调用Session对象的invalidate()进行销毁
在SessionDemo2类中添加session销毁的方法
@WebServlet("/demo2")
public class SessionDemo2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取数据,从session中
//1. 获取Session对象
HttpSession session = request.getSession();
System.out.println(session);
// 销毁
session.invalidate();
//2. 获取数据
Object username = session.getAttribute("username");
System.out.println(username);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
启动访问测试,先访问demo1将数据存入到session,再次访问demo2从session中获取数据
Cookie和Session小结
- 相同:都是完成一次会话内多次请求间数据共享的。
- 区别:
- 存储位置:Cookie是将数据存储在客户端,Session将数据存储在服务端
- 安全性:Cookie不安全,Session安全
- 数据大小:Cookie最大3KB,Session无大小限制
- 存储时间:Cookie可以通过setMaxAge()长期存储,Session默认30分钟
- 服务器性能:Cookie不占服务器资源,Session占用服务器资源
十四.Filter
1.概述
Filter表示过滤器属于javax.servlet.Filter接口类型,过滤器可以对访问的静态和动态资源进行过滤,既可以过滤请求,也可以 过滤响应。
2.入门案例
1.定义类实现javax.servlet.Filter接口
2.在实现类中实现该接口中的所有抽象方法
3.在doFilter方法中书写过滤资源的代码
4.在web.xml中配置过滤器
5.访问
package com.itheima.sh.a_filter_01;
import javax.servlet.*;
import java.io.IOException;
//1.自定义过滤器类实现过滤器接口Filter
public class MyFilter implements Filter {
//2.在自定义类中实现过滤器接口Filter中的所有抽象方法
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//不要调用父的方法否则报错
// Filter.super.init(filterConfig);
}
//3.在doFilter方法体中书写拦截资源的代码
//每次访问被过滤的资源都要执行该方法
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("doFilter。。。。");
//放行 可以访问被过滤的资源
chain.doFilter(request, response);
}
@Override
public void destroy() {
// Filter.super.destroy();
}
}
<?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">
<!-- 4.配置过滤器 -->
<filter>
<filter-name>MyFilter</filter-name>
<filter-class>com.itheima.sh.a_filter_01.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>MyFilter</filter-name>
<!--配置的是当前过滤器过滤的资源路径,表示MyFilter过滤器过滤demo01.html的请求响应-->
<url-pattern>/demo01.html</url-pattern>
</filter-mapping>
</web-app>
3.关于入门案例的补充
1.Filter接口中的doFilter方法的第三个参数
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("doFilter。。。。");
//放行 可以访问被过滤的资源
chain.doFilter(request, response);
}
FilterChain是一个接口
抽象方法:
void doFilter(ServletRequest request, ServletResponse response)
注意:如果可以访问过滤资源:则书写:过滤器链对象.doFilter();
如果不可以访问过滤资源:则不书写:过滤器链对象.doFilter();
4.注解开发
作用:简化代码开发
作用:简化代码开发
package com.itheima.sh.a_filter_01;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
//1.自定义过滤器类实现过滤器接口Filter
/*
TODO:
1.String[] urlPatterns() default {}; 相当于 <url-pattern>/demo01.html</url-pattern>
2.String[] value() default {}; 等同于 urlPatterns属性,诞生value属性的目的简化开发就是省略value属性
*/
//@WebFilter(urlPatterns = {"/demo01.html"})
//@WebFilter(urlPatterns = "/demo01.html")
//@WebFilter(value = "/demo01.html")
@WebFilter("/demo01.html")
public class MyFilter implements Filter {
//2.在自定义类中实现过滤器接口Filter中的所有抽象方法
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//不要调用父的方法否则报错
// Filter.super.init(filterConfig);
}
//3.在doFilter方法体中书写拦截资源的代码
//每次访问被过滤的资源都要执行该方法
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("doFilter。。。。");
//放行 可以访问被过滤的资源
chain.doFilter(request, response);
}
@Override
public void destroy() {
// Filter.super.destroy();
}
}
5.在idea中配置filter的模板
package com.itheima.sh.a_filter_01;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebFilter("/demo01.html")
public class My2Filter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
//your code....
System.out.println("注解开发简单");
chain.doFilter(request, response);
}
@Override
public void init(FilterConfig config) throws ServletException {
}
}
6.Filter的生命周期 掌握
Filter过滤器的生命周期
1.当启动tomcat服务器的时候tomcat调用过滤器类的无参构造方法创建过滤器类的对象
2.然后使用过滤器类的对象调用init方法进行初始化
3.每次访问被过滤的资源都要执行doFilter方法 ******** 实际开发中我们在doFilter方法处理被过滤的资源逻辑
4.关闭tomcat服务器之前,使用过滤器对象调用destroy方法
package com.itheima.sh.a_filter_01;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/*
TODO:Filter过滤器的生命周期
1.当启动tomcat服务器的时候tomcat调用过滤器类的无参构造方法创建过滤器类的对象
2.然后使用过滤器类的对象调用init方法进行初始化
3.每次访问被过滤的资源都要执行doFilter方法 ******** 实际开发中我们在doFilter方法处理被过滤的资源逻辑
4.关闭tomcat服务器之前,使用过滤器对象调用destroy方法
*/
@WebFilter("/demo02.html")
public class LifeDemo01Filter implements Filter {
//无参构造方法
public LifeDemo01Filter() {
System.out.println("无参构造方法。。。。。");
}
@Override
public void destroy() {
System.out.println("destroy。。。。。");
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
//your code....
System.out.println("doFilter。。。。。");
chain.doFilter(request, response);
}
@Override
public void init(FilterConfig config) throws ServletException {
System.out.println("init。。。。。");
}
}
7.拦截路径(重点)
过滤器的过滤资源路径
1.精确匹配 /demo03.html 这里必须加/
2.目录匹配: /xx/* 只要浏览器访问的路径以xx开始就会执行当前过滤器
3.后缀名匹配:.xx 只要浏览器访问的路径以xx结尾就会执行当前过滤器,注意:这里不能加/
4.拦截所有: / 只要浏览器访问当前项目就会执行当前过滤器 *************
注意:在过滤器中,如果多个过滤器过滤器同一个资源,那么要执行所有的满足条件的过滤器
8.过滤器链 掌握
就是多个过滤器过滤器同一个资源形成的一条链子。
小结:
1.前提条件:多个过滤器在同一包下过滤器统一资源才有如下规律(注解开发)
执行顺序按照过滤器类的字母升序执行。
2.如果是xml配置方式,多个过滤器过滤统一资源按照在xml中出现的顺序执行
9.Filter案例
(1)解决全站乱码问题
以前我们都是在每个servlet中书写处理请求和响应的乱码diam,这样代码冗余。
以后我们将处理请求和响应的乱码的代码书写在过滤器中即可。
以后我们将处理请求和响应的乱码的代码书写在过滤器中即可。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/demo01Servlet" method="post">
<input type="text" name="username"><br>
<input type="submit">
</form>
</body>
</html>
package com.itheima.sh.c_filter_test_03;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
// /* 表示拦截所有资源
@WebFilter("/*")
public class EncodingFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
//your code....
//处理请求乱码
request.setCharacterEncoding("utf-8");
//处理响应乱码
response.setContentType("text/html;charset=utf-8");
chain.doFilter(request, response);
}
@Override
public void init(FilterConfig config) throws ServletException {
}
}
package com.itheima.sh.c_filter_test_03;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/demo01Servlet")
public class Demo01Servlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取请求参数
String username = request.getParameter("username");
System.out.println("username = " + username);
//2.响应给浏览器
response.getWriter().print("黑马程序员");
}
}
(2)过滤器案例–登录权限校验
hack.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>黑客。。</h1>
<a href="javascript:;">下载黑客资源</a>
</body>
</html>
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<form action="/loginServlet" method="post">
用户名:<input type="text" name="name"><br>
密码:<input type="password" name="password"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
LoginServlet
package com.itheima.sh.c_filter_test_03;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取请求参数
String name = request.getParameter("name");
String password = request.getParameter("password");
//2.创建User对象
User user = new User();
//3.封装数据
user.setName(name);
user.setPassword(password);
//TODO:实际开发中将user对象传递到业务层到数据库查询数据
//4.这里假设输入的用户名和密码就是正确的,将user对象放到session
request.getSession().setAttribute("u",user);
//5.跳转到hack.html页面
response.sendRedirect("/hack.html");
}
}
过滤器LoginFilter
package com.itheima.sh.c_filter_test_03;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
// 当前过滤器过滤的是/hack.html
@WebFilter("/hack.html")
public class LoginFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
//your code....
//1.从session中获User对象 request.getSession().setAttribute("u",user);
User u = (User) request.getSession().getAttribute("u");
//2.判断u是否等于null
if(u == null){
//3.说明没有登录,跳转到登录页面
response.sendRedirect("/login.html");
}else{
//4.说明登录了,放行可以访问被过滤的资源hack.html页面
chain.doFilter(request, response);
}
}
@Override
public void init(FilterConfig config) throws ServletException {
}
}
十五.ServletContext
ServletContext表示上下文对象,属于接口,代表整个web项目,可以使用方法获取当前web项目的所有文件的MIME类型
之前学习能够共享数据的对象:
- request:只能在一次请求一次响应中进行数据的共享—》请求转发
- session:只能在一次会话过程中,可以有多次请求和响应
- servletContext:只要项目存在就可以共享数据,多次会话,多次请求和响应都可以共享数据,操作整个项目的配置文件
范围大小:ServletContext > session >request
(1)ServletContext对象介绍
当tomcat服务器启动的时候,会为每个web项目创建一个唯一的ServletContext对象,该对象代表当前整个web应用项目。该对象不仅封装了当前web应用的所有信息,而且实现了多个servlet的数据共享。在ServletContext中可以存放共享数据,ServletContext对象是真正的一个全局对象,凡是web容器中的Servlet都可以访问。
在每个项目中可以有多个Servlet程序,每个Servlet程序都是独立的。当前这个项目的配置信息,就必须使用描述这个项目的ServletContext对象获取。
方法名 | 描述 |
---|---|
setAttribute(String name,Object object) | 向ServletContext中存数据 |
getAttribute(String name) | 从ServletContext中取数据 |
removeAttribute(name) | 从ServletContext中移除数据 |
String getRealPath(String path) | 返回资源文件在服务器文件系统上的真实路径(文件的绝对路径) |
getMimeType(fileName) | 获取服务器中文件类型.txt text/plain .html text/html |
ServletContext对象,tomcat为每一个web项目单独创建一个上下文对象。有如下功能:
1.可以在多个servlet之间共享数据
存放:setAttribute()
获得:getAttribute()
删除:removeAttribute()
2.可以获得当前WEB项目中的指定资源(文件)
String path = getRealPath( String string);
- 需求1:如何获取上下文servletContext对象。
使用HttpServlet类的父类 GenericServlet 中的方法:getServletContext();
代码如下:
/*
获取上下文ServletContext对象:
使用HttpServlet类的父类 GenericServlet 中的方法:getServletContext();
*/
@WebServlet("/servletContextDemoServlet")
public class ServletContextDemoServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取ServletContext类的对象
ServletContext servletContext = getServletContext();
}
}
- 需求2:如何获取服务器中每个文件的路径。例如,在当前项目下的web文件夹下放一个1.jpg的图片,获取其真实路径(绝对路径)。
@WebServlet("/servletContextDemoServlet")
public class ServletContextDemoServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext servletContext = getServletContext();
//如何获取服务器中每个文件的路径。
// 例如,在当前项目下的web文件夹下放一个1.jpg的图片,获取其真实路径(绝对路径)。
String realPath = servletContext.getRealPath("/1.jpg");
/*
输出结果:
realPath = F:\ideawork\jiuyeban2\heima60\heima03\out\artifacts\web_war_exploded\1.jpg
说明:我们的项目在发布的时候会被打成一个war包,这个war包下的class文件会被放在tomcat下被运行。
所以这里获取的真实路径是这个war包下的1.jpg所在的路径。
*/
System.out.println("realPath = " + realPath);
}
}
说明:
获取结果:
realPath = F:\ideawork\jiuyeban2\heima60\heima03\out\artifacts\web_war_exploded\1.jpg
说明:我们的项目在发布的时候会被打成一个war包,这个war包下的class文件会被放在tomcat下被运行。
所以这里获取的真实路径是这个war包下的1.jpg所在的路径。
- 需求3:获取当前项目下的1.jpg在服务器中的文件类型。
/*
需求3:获取当前项目下的1.jpg在服务器中的文件类型。
getMimeType("1.jpg");这里书写获取文件类型的文件名和后缀名即可
*/
String mimeType = servletContext.getMimeType("1.jpg");
System.out.println("mimeType = " + mimeType);//mimeType = image/jpeg
注意:对于方法 getMimeType(“1.jpg”);这里书写获取文件类型的文件名和后缀名即可
总结:
1.ServletContext表示上下文对象,代表整个web项目,tomcat一启动就会创建该接口对象,关闭tomcat就会消失。
2.ServletContext属于对象,多次会话,多次请求限制,三个域对象范围:ServletContext > HttpSession > HttpServletRequest
十六.Listener
1.说明
javaweb中的监听器是监听ServletContext、HttpSession、HttpServletRequest三个对象创建和销毁的,同时监听是哪个对象中数据的变化,就是监听属性的变化:setAttribute、removeAttribute。
- ServletContext 是在tomcat启动创建,关闭tomcat销毁
- HttpSession 是在浏览器第一次访问执行request.getSession()创建,销毁时间:(1)30min (2)执行invalidate()
- HttpServletRequest是浏览器第一次访问创建,浏览器接收到服务器的响应就销毁
2.监听器的接口分类
事件源 | 监听器接口 | 时机 |
---|---|---|
ServletContext | ServletContextListener | 上下文域创建和销毁 |
ServletContext | ServletContextAttributeListener | 上下文域属性增删改的操作 |
**HttpSession ** | HttpSessionListener | 会话域创建和销毁 |
**HttpSession ** | HttpSessionAttributeListener | 会话域属性增删改的操作 |
HttpServletRequest | ServletRequestListener | 请求域创建和销毁 |
HttpServletRequest | ServletRequestAttributeListener | 请求域属性增删改的操作 |
3.快速入门,简单案例
步骤:
1. 创建一个普通类,实现ServletContextListenner
2. 重写方法
监听ServletContext创建
监听ServletContext销毁
3. 配置
web.xml
注解 掌握
① xml版本
package com.itheima04;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
/*
设置监听器的人: 开发者
监听器 : MyListener
监听器目标 : ServletContext 对象
监听器的工作:
1). 当ServletContext 对象创建的时候就会执行contextInitialized方法
ServletContext是tomcat启动时就会创建 (早于Filter和Servlet)
2). 当ServletContext 对象销毁的时候就会执行contextDestroyed方法
ServletContext是tomcat关闭时销毁 (晚于Filter和Servlet)
这两个方法是事件驱动
*/
public class MyListener implements ServletContextListener {
//tomcat一启动,此方法就会运行
//运用场景: spring底层封装了一个ServletContextListener加载配置文件
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("contextInitialized");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("contextDestroyed");
}
}
<listener>
<listener-class>com.itheima08.MyServletContextListener</listener-class>
</listener>
② 注解版本
@WebListener
public class MyListener implements ServletContextListener {
//tomcat一启动,此方法就会运行
//运用场景: spring底层封装了一个ServletContextListener加载配置文件
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("contextInitialized");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("contextDestroyed");
}
}
4.模拟spring框架
需求:可以在项目启动时读取配置文件。获取配置文件的名字。
步骤:
1.在web.xml进行配置文件的配置
2.创建自定义监听器类监听ServletContext的创建和销毁
3.在自定义监听器类中实现监听器的方法
4.实现的方法体中读取web.xml文件中关于当前项目配置文件的信息
5.输出结果
package com.itheima.sh.h_listener_08;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
//2.创建自定义监听器类监听ServletContext的创建和销毁
//别忘记注册监听器
@WebListener
public class SpringContextListener implements ServletContextListener{
//3.在自定义监听器类中实现监听器的抽象方法
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
//4.实现的方法体中读取web.xml文件中关于当前项目配置文件的信息
//4.1根据事件类对象servletContextEvent调用方法获取ServletContext上下文对象
ServletContext servletContext = servletContextEvent.getServletContext();
//4.2使用上下文对象调用方法获取web.xml配置文件中的数据
/*
<context-param>
<param-name>jdbc</param-name>
<param-value>jdbc.properties</param-value>
</context-param>
*/
String str = servletContext.getInitParameter("jdbc");
System.out.println("str = " + str);
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("释放资源....销毁。。。。");
}
}
<!--配置当前项目的配置文件信息-->
<!--1.在web.xml进行配置文件的配置-->
<context-param>
<param-name>jdbc</param-name>
<param-value>jdbc.properties</param-value>
</context-param>
十七.git
1.git概述
- 工作区:
平时存放项目代码的地方;
一个文件夹通过git init设置成一个git可以管理的文件夹时,这个文件夹里的内容(除去.git文件夹)就是工作区。
- 仓库区(或版本库)
安全存放数据的位置,这里面有提交到所有版本的数据。其中HEAD指向指向最新放入仓库的版本。工作区有一个隐藏目录 .git,它不算工作区,而是Git的版本库
-
暂存区(stage或index)
用来暂时存放工作区中修改的内容,可以理解为一个中转站。.
- 位置:一般存放在 .git 目录下的 index 文件(.git/index)中,所有我们把暂存区有时也叫做索引(index)。
- 只是一个文件
- 包含在版本库中
- 为什么需要暂存区?
- 如果没有暂存区,想要提交文件就需要一个个修改,然后提交,比较麻烦,但是有了暂存区就可以一次性将所需要的文件从暂存区直接修改后提交。
- 如果没有暂存区,修改的文件只可以立刻保存到版本库中,但是这样很容易对别人的工作造成影响
-
Head:指向最新放入仓库的版本
-
master:
主分支,当 git init 后,并不会立刻产生分支,而是添加了一个文件,并git add,git commit 后,才会看到master分支,是本地仓库一部分。
-
objects:
是git对象库,用来存储各种创建的对象以及内容。
-
远程仓库:
托管代码的服务器,常用github、gitee、gitlab
2.git作用
- 代码共享
每位开发者可以使用Git将自己的代码上传到云服务器上,同时使用Git还可以将其他人上传的代码下载到自己的电脑上。
- 回溯版本
- 追踪信息
【面试题】
Git与SVN的区别:
- Git是分布式的,SVN不是
- Git把内容按元数据(修饰数据的数据)方式存储,而SVN是按文件
- Git分支和SVN的分支不同
- Git没有一个全局的版本号,而SVN有
- Git的内容完整性优于SVN
3.git下载与安装
下载地址:https://git-scm.com/download
安装过程非常简单,各种下一步,各种Next。
点击Finish完成安装,验证安装,找一个桌面空白处,右键出现下列窗口
点击后,出现Git的控制台,在控制台输入git,可以看到相关的帮助信息
4.git本地操作
- 初始化工作区
命令:git init 初始化
在要被初始化工作区的目录右键,选择Git Bash Here
在文件夹中机会出现一个隐藏文件.git如图
当我们在learn-Git文件夹中添加文件的时候,那么这个文件就会被Git所管理
- 查看状态
我们在learn-git目录中创建一个readme.txt文件,并使用vim命令进入编辑模式添加内容:第一行代码。可以通过命令来查看它的状态
命令:git status 查看状态
红色代表当前没有提交到缓存区
5.git本地操作-add与commit
- 工作区提交到暂存区
命令:git add readme.txt
这是完成了将文件由工作区提交暂存区
我们通过git status来查看状态
发现这时文件变成绿色,可以提交到本地仓库
- 暂存取提交到本地仓库
命令:git commit -m '第一次提交'
说明:-m 后面跟随的是为你提交的备注,m是单词message信息的首字母
提交信息格式:增删改查第几次提交
注意:如果第一次提交需要填写如下内容:
命令:git config --global user.email '[email protected]'
说明:指定邮箱
命令:git config --global user.name 'suoge'
说明:指定操作者
- 扩展
添加多个文件 git add [file1] [file2] ...
添加指定目录到暂存区,包括子目录 git add [dir]
添加当前目录下的所有文件到暂存区,不包括被删除的文件 git add .
add 时,一个个文件加比较麻烦,
可以用下面的命令将所有变动的文件同步至暂存区(新增、修改、删除)
git add -A
下面的命令是将所有修改和删除的文件同步至暂存区,不包括新增文件
git add -u
6.git本地操作-差异比较
使用vim命令,对readme.txt文件进行编辑,添加我是第二行代码,使用:wq退出
- 工作区、暂存区比较
命令:git diff readme.txt
- 工作区、本地库比较
命令:git diff HEAD readme.txt
- 暂存区、本地库比较
命令:git diff --cached readme.txt
这里缓存区和本地库没有不同所以没有内容
7.git本地操作-版本回退
当我们从暂存区提交到本地仓库时,发现当前的提交的版本有问题,希望回退到指定版本如何操作呢?
使用vim命令编辑readme.txt,添加“我是第三行代码”
命令:git add readme.txt 提交到暂存区
命令:git commit -m ‘第三次提交’ 提交到本地仓库
我们可以通过git提供的查看日志命令来查看提交的日志
命令:git log 查看当前提交日志
可以发现,目前为止,我们已经在本地仓库中提交了3次,也就是说有3个不同版本。其中,最近的这个版本有一个标示:HEAD-> master 这就是标记当前分支的当前版本所在位置,如果没有显示当前所在位置可以使用下面命令查看:
命令:git log --decorate 查看当前提交日志,且显示当前分支的当前版本所在位置
在log中,每一个版本的前面,都有一长串随即数字: b44a10787c2b2bcc7ceb9c39cf06309065518d4b ,这是每次提交的commit id ,这是通过SHA1算法得到的值,Git通过这个唯一的id来区分每次提交
- 回退到之前版本
命令:git reset --hard HEAD^
回归到上一个版本,Git通过HEAD来判断当前所在的版本位置。
那么上一个版本,就用HEAD^标示,上上一个版本就是HEAD^^,
当然往上100个版本写100个^比较容易数不过来,所以写成HEAD~100。
这时我们可以在查看一下日志 git log
发现只有两个版本,我们在查看一下文件内容
- 回退到指定版本
命令: git reflog 查看所有操作
找到之后可以使用如下命令进行回退到指定版本
命令:git reset --hard 版本号 回退到指定版本
我们回到第三次提交,这时我们在查看文件
8.git本地操作-修改撤销
当我们工作区内容想要提交到缓存区时【add】,突然发现有问题,想要撤销该如何处理?
当我们已提交到缓存区的内容,发现出现了bug,这时又应该如何处理哪?
以上操作我们可以使用GIT提供的撤销命令来完成
- 工作区撤销修改
编辑readme.txt添加“我是第四行”
在你提交缓存区前,你突然发现这个修改是有问题的,你打算恢复到原来的样子。怎么办?
使用git status 命令查看当前状态
命令:git checkout 文件名称
撤销工作区修改
我们撤销后,在查看文件中内容,发现工作区内容已经撤销,并查看状态,发现状态很干净
- 暂存区撤销修改
使用 vim 命令 编辑readme.txt添加“我是第五行”
使用git add提交文件至暂存区
撤销到工作区
命令:git reset HEAD readme.txt 撤销到工作区
工作区撤销 git checkout readme.txt
我们在查看文件,发现已经恢复到最初始样子
9.分支操作
分支就是多长提交串起来的一条线
问题背景:要开发一个新功能,又不想打扰目前分支(main)的功能开发,于是
可以看到main分支的开发并不会停止,可以进行进行C4这个功能的开发,而new分支可以开发新功能c5,同样不会影响main
new 分支的功能开发测试没有问题了,main 也想要 new 的代码怎么办?
可以使用merge命令将new分支的变动合并至main分支,此时的C6中既包含了C4以来的修改,也包括了C5的修改,这时如果觉得C5已经没用了,可以将它删除。
(1)分支的创建与切换
- 创建分支
分支创建示意图:
命令: git branch 分支名 创建dev分支 git branch dev
注意:分支名字随便书写,名字最好达到见名知意,张三---》zhangsan
- 分支切换
命令: git checkout dev 切换dev分支
编辑readme.txt,添加“我是dev提交的代码”
从工作区提交到缓存区执行:git add readme.txt
执行:从缓存区提交到本地区:git commit -m ‘dev分支提交’
查看文件内容
切换到master分支,并查看文件内容
注意:如果没有将dev分支合并到主分支上,那么在主分支即master上面是无法查看到dev分支提交的内容
(2)分支合并与删除
- 分支合并
命令:git merge 分支名 合并dev分支 git merge dev
注意:当前我们是在master分支
合并后,我们发现master分支上的readme.txt文件内容已经改变
- 分支删除
合并完分支之后,如果不再使用dev分支,则可以删除此分支,先查看当前分支:
命令 git branch //查看分支情况
当前有两个分支dev与master,我们当前是在master分支上,如何删除dev分支
命令 git branch -d 分支名
我们使用git branch查看,发现dev分支已经被删除
10.git远程仓库介绍与码云仓库的注册创建
远程仓库是公网或外网中的一个仓库,主要用于存储个人或团队的提交记录与提交日志,团队合作开发也是靠远程仓库实现的。
常用的远程仓库
- GitHub(https://github.com)
- Gitee(https://gitee.com)
- Gitlab(https://about.gitlab.com)
GitHub是一个面向开源及私有软件项目的托管平台,说白了就是给开发者提供了一个远程仓库。现在的GitHub除了承载了代码托管平台的功能,更大的功能是组建了一个同行交流平台,大量的码农在上面做技术及非技术的交流,活跃用户还是很多的,可惜这个主站在国外,速度一般。
gitee和GitHub是一样的东西,是开源中国(OSChina)推出的基于Git的代码托管平台,又叫码云。
- 注册登录
访问地址:https://gitee.com/
- 创建仓库
填写项目相关的信息
仓库创建完毕,可以看到,如果我们要与远程仓库同步,这里支持多种通信协议,当我们选中一种协议后,后面会出现对应的远程仓库地址。
11.Git远程仓库操作
(1)关联
现在readme.txt已经推送到我们自己的本地仓库,在推送到码云仓库前,我们需要先建立本地仓库与远程仓库的关系
命令:git remote add origin 远程仓库地址 关联远程仓库
(2)拉取
【注意】在推送代码前必须先拉取代码,否则无法推送本地仓库代码到码云仓库
命令:git pull origin master --allow-unrelated-histories
首次拉取需要添加:--allow-unrelated-histories
命令:git pull 后续拉取
(3)推送
本地仓库推送到码云仓库
命令: git push -u origin master 首次推送
命令: git push 后续推送
码云仓库有推送的信息
(4)克隆
新建文件夹learn-Git-B,新建的文件中右键
点击 Git bash Here
命令: git clone 远程仓库
这时候就拉取出远程仓库的内容
- git clone(克隆) 与 git pull(拉取) 区别
#1.相同点:都是从远程服务器拉取代码到本地
#2.不同点:
git clone(克隆) :是在本地没有版本库的时候,
从远程服务器克隆整个版本库到本地,是一个本地从无到有的过程。
git pull(拉取) :在本地有版本库的情况下,从远程库获取最新commit 数据
(如果有的话),并merge(合并)到本地。
12.IDEA中使用Git
- 集成
在idea中的file菜单中选中settings
弹出settings后在搜索中输入"git",选择Git,指定你的安装的git.exe目录
校验git是否集成完成,点击test,弹出校验窗口,点击git Executed successed 成功则表示集成完成
- 创建过程
创建一个普通的java工程git-project项目,结构如下:
- 基本操作-初始化工作区
点击VCS --> Create Git Repository
选择管理的文件夹,这里我现在的为gitProject文件夹
点击左下角,Git菜单,此时day0901_git下所有的文件都变成棕色,说明我们的工作区添加完成了
- 忽略文件类型
从version control中我们可以看到有一部分文件,我们是不需要提交到本地仓库中去的
那我们怎么做呢?可以拷贝"资料"中.gitignore文件,到gitProject的根目录:
这个时候你会发现,多余的不需要提交的文件类型被忽略了。如果有新的要忽视的文件类型,你可以在.gitignore中添加
13.IDEA中使用Git-基本操作
- 工作区提交 暂存区 add
选中gitProject项目,右键
可以看到Git中的文件颜色由棕色变成的绿色
- 暂存区提交本地库 commit
点击右下角Version control面板中,选中你要提交的文件,这里我都需要提交,使用全部选中
点击鼠标右键
选中commit:
点击Commit
再次确定,点击Commit
- 工作区与本地仓库比较
在Version Control中选中HelloWorld.java右键:
点击左下角Git—>log,就可以查看提交记录
- 制造问题
选择Demo01.java,提交刚刚修改的内容到本地仓库中:
填写备注,然后点击commit:
在左下角Git中查看log
在Demo01.java中添加
提交到本地仓库
在左下角Git中查看log
可以看出此时:我们一共提交3次,下面我们来进行版本的回退
- 本地仓库回退撤销
在右下方Git点击log,此时我们可以看到3个提交的版本
现在我们在本地仓库中回退到第二次提交,选择第二次提交的标记,右键
选择Hard
- 工作区撤销
当我们在工作区编辑代码时候,希望撤销未提交本地仓库的代码时候,在Git中右键
弹出如下窗口
点击Rollback,代码则撤销
说明:针对上述工作区的代码撤消,有同学会有疑问,我直接删除不就完了吗,为什么这么麻烦,其实我们在实际开发中代码会很多,那么当你书写了很多不同地方的代码,以至于你都忘记哪些是新编写的代码了,那么删除是一件很痛苦的事情,并且容易误删,所以使用工作区撤销更加方便。
14.IDEA中使用Git远程仓库
GitLab是一个用于 仓库管理系统的开源项目。使用Git作为代码的管理工具,并在此基础上搭建起来的Web服务。可通过Web界面进行访问公开的或者私人项目。他拥有GitHub和Gitee类似的功能,能够浏览源代码,管理缺陷和注释。可以管理团队对仓库的访问,它非常易于浏览提交过的版本并提供一个文件历史库。团队成员可以利用内置的简单聊天程序(Wall)进行交流。它还提供一个代码片段收集功能可以轻松实现代码复用。
官网
https://about.gitlab.com
登录之后的页面效果: