javaweb
XML
作用:spring 中的 ico 配置文件,beans.xml mybatis XXXMapper.xml tomcat server.xml web.xml maven pom.xml
XML 语法
一个 XML 文件分为如下几部分内容
1、文档声明
2、元素
3、属性
4、注释
5、CDATA 区 (原样输出)
元素
1、元素语法要求
- 每个XML文档必须有且只有一个根元素。
- 根元素是一个完全包括文档中其他所有元素的元素。
- 根元素的起始标记要放在所有其他元素的起始标记之前。
- 根元素的结束标记要放在所有其他元素的结束标记之后。
2、XML 元素命名规则
- 区分大小写,例如,
<P>和<p>
是两个不同的标记。 - 不能以数字开头。
- 不能包含空格。
- 名称中间不能包含冒号(:)。
- 如果标签单词需要间隔,建议使用下划线 比如 <book_title>hello</book_title>
属性
- 属性介绍
属性值用双引号(")或单引号(')分隔(如果属性值中有',用"分隔;有",用'分隔
一个元素可以有多个属性,它的基本格式为:<元素名 属性名="属性值">
特定的属性名称在同一个元素标记中只能出现一次
属性值不能包括& 字符
CDATA 节!
老韩说明: 有些内容不想让解析引擎执行,而是当作原始内容处理(即当做普通文本),可
以使用 CDATA 包括起来,CDATA 节中的所有字符都会被当作简单文本,而不是 XML 标记。
- 语法:
<![CDATA[
这里可以把你输入的字符原样显示,不会解析 xml
]]>
可以输入任意字符(除]]>外)
不能嵌套
转义字符
转义字符 | 符号 |
---|---|
< | < |
> | > |
&; | & |
" | " |
' | ' |
DOM4j
DOM4J 介绍
- Dom4j 是一个简单、灵活的开放源代码的库(用于解析/处理 XML 文件)。Dom4j 是由早期
开发 JDOM 的人分离出来而后独立开发的。
- 与 JDOM 不同的是,dom4j 使用接口和抽象基类,虽然 Dom4j 的 API 相对要复杂一些,
但它提供了比 JDOM 更好的灵活性。
- Dom4j 是一个非常优秀的 Java XML API,具有性能优异、功能强大和极易使用的特点。
现在很多软件采用的 Dom4j。
\4. 使用 Dom4j 开发,需下载 dom4j 相应的 jar 文件
DOM4j 中,获得 Document 对象的方式有三种
开发 dom4j 要导入 dom4j 的包
1、读取 XML 文件,获得 document 对象
SAXReader reader = new SAXReader(); //创建一个解析器
Document document = reader.read(new File("src/input.xml"));//XML Document
2、解析 XML 形式的文本,得到 document 对象
String text = "<members></members>";
Document document = DocumentHelper.parseText(text);
3、主动创建 document 对象.
Document document = DocumentHelper.createDocument(); //创建根节点
Element root = document.addElement("members");
DOM4j 应用实例
使用 DOM4J 对 students.xml 文件进行增删改查
重点讲解查询(遍历和指定查询)
遍历
@Test
public void listStus() throws DocumentException {
//得到一个解析器
SAXReader reader = new SAXReader();
//老师的代码技巧->debug 看看 document 对象的属性
//分析了 document 对象的底层结构
Document document = reader.read(new File("src/students.xml"));
//1. 得到 rootElement, 你是 OOP
Element rootElement = document.getRootElement();
//2. 得到 rootElement 的 student Elements
List<Element> students = rootElement.elements("student");
//System.out.println(student.size());//2
for (Element student : students) {//element 就是 Student 元素/节点
//获取 Student 元素 的 name Element
Element name = student.element("name");
Element age = student.element("age");
Element resume = student.element("resume");
Element gender = student.element("gender");
System.out.println("学生信息= " + name.getText() + " " + age.getText() +
" " + resume.getText() + " " + gender.getText());
}
}
前端技术栈
1 课程内容介绍
1.1.1 ES6
1.1.1.1 学习目标: 掌握 ES6 语法新特性
1.1.1.2 一句话: 全称 ECMAScript 6.0 ,是 JavaScript 的下一个版本标准, 增加了新的语法特性
1.1.2 NodeJS
1.1.2.1 学习目标: 掌握 Node.js 的 npm 功能就 OK
1.1.2.2 一句话: NPM 是随同 NodeJS 一起安装的包管理工具, 类似 Maven
1.1.3 Vue
1.1.3.1 学习目标: 1. 能看懂Vue代码 2. 能用 Vue 开发框架完成和后端通信 3. 能开发基本的 Vue代码
1.1.3.2 一句话: Vue 是一个前端框架,类似 Java 后端的 Spring MVC 这样的框架
1.1.4 模块化编程
1.1.4.1 Javascript 代码越来越庞大,Javascript 引入模块化编程,开发者只需要实现核心的业务逻辑,
其他都可以加载别人已经写好的模块韩顺平 Java 工程师
1.1.5 Promise
1.1.5.1 一句话: Promise 是异步编程的一种解决方案, 可以解决传统 Ajax 回调函数嵌套问题
1.1.5.2 Promise 也是 ES6 的新特性,因为比较重要,老师单独拉出来讲
1.1.6 Axios
1.1.6.1 axios 通常和 Vue 一起使用,实现 Ajax 操作
1.1.6.2 Axios 是一个基于 promise 的 HTTP 库
前后端技术栈比较

ES6 新特性
ES6 基本介绍
ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准, 2015 年 6 月发布。
ES6 设计目标:达到 JavaScript 语言可以用来编写复杂的大型程序,成为企业级开发语言
ECMAScript 和 JavaScript 的关系:ECMAScript 是 JavaScript 的规范/规则JavaScript 是ECMAScript 的一种实现
let 声明变量
注意事项和使用细节
let 声明的变量有严格局部作用域
let 只能声明一次, var 可以声明多次
let 不存在变量提升, var 存在变量提升
const 声明常量/只读变量
注意事项和使用细节
常量在定义时,需要赋值
常量赋值后不能修改
解构赋值
解构赋值是对赋值运算符的扩展
是一种针对数组或者对象进行模式匹配,然后对其中的变量进行赋值
主要有两种形式: 数组解构和对象解构
数组解构
<script type="text/javascript">
let arr = [1, 2, 3];
//如果要看某个变量的类型
console.log("arr=>", arr);
//数组解构[取出元素]
//1.传统
let x = arr[0], y = arr[1], z = arr[2];
console.log(x, y, z);
//2. ES6 风格
let [a, b, c] = arr;
console.log("==================================");
console.log(a, b, c);
let [num1, num2, num3] = [100, 200, 300];
console.log(num1, num2, num3);
</script>
对象解构
<script type="text/javascript">
//对象解构
/**
* let monster = {name: '牛魔王', age: 800} * 细节说明
* 1. 对象解构时,let{name, age} 中的 name 和 age 名称需要和对象属性名保持一致
* 2. 也可解构对象部分属性,比如 let{age} 这样就只取出 age 属性
* 3. 还有其它的形式,比如方法上使用对象解构
*/
//monster 是一个对象
let monster = {name: '牛魔王', age: 800};
//传统方式取出属性-解构 对象名.属性名
console.log(monster.name, " ", monster.age);
//ES6 对象解构
//老韩解读
//1. 把 monster 对象的属性, 赋值给{name,age}
//2. {name,age} 的取名 name 和 age 要和 monster 对象的属性名保持一致
//3. 使用{} 包括, 不要使用[]
//4. {name,age} 顺序是无所谓
let {name, age} = monster;
console.log("========================");
console.log("name=", name, " age=", age);
//下面这个写法也可以
//let {name, age} = {name: '牛魔王', age: 800};
//还有其它的形式,比如方法上使用对象解构
//如果这样使用,仍然要保证名称和对象的属性名一致
function f1({name, age}) {
console.log("f1-name=", name, " ", "f1-age=", age)
}
f1(monster);
</script>
模板字符串
1 模板字符串使用反引号 ` 将字符串包裹
2 可作为普通字符串
3 可用来定义多行字符串,即可以将换行字符串原生输出
4 字符串插入变量和表达式, 使用 ${}
5 字符串中调用函数
<script type="text/javascript">
//1、字符串,换行会原生输出
let str1 = `for(int i = 0;i < 10;i++){
System.out.println("i="+i);
}`;
console.log("str1=", str1);
//2. 字符串插入变量和表达式。变量名写在 ${} 中,${} 中可以放入 JavaScript 表达式。
let name = "hspedu 教育";
//老师解读
//(1) 当解析 ${name}, 就找最近的 name 遍历, 进行替换
//(2) 然后可以得到最后解析的字符串
let str2 = `教育名称=${name}`;
let str3 = `1+2=${1 + 2}`;
let n1 = 80;
let n2 = 20;
let str4 = `${n1}+${n2}=${n1 + n2}`;
console.log("str2=", str2);
console.log("str3=", str3);
console.log("str4=", str4);
//3. 字符串中调用函数
function sayHi(name) {
return "hi " + name;
}
let name2 = "tom";
let str5 = `sayHi() 返回的结果是=${sayHi('jack')}`;
let str6 = `sayHi() 返回的结果是=${sayHi(name2)}`;
console.log("str5=", str5);
console.log("str6=", str6);
</script>
对象相关新特性
声明对象简写
<script type="text/javascript">
const age = 800;
const name = "牛魔王";
// 传统 声明/定义对象
let monster = {name: name, age: age};
// ES6 声明/定义对象
//老师解读
//1. {name, age} : 表示对象的属性名是 name 和 age
//2. 属性 name 的值是从变量/常量 name 来的
let monster02 = {name, age};
console.log("monster02=>", monster02);
</script>
对象方法简写
<script type="text/javascript">
// 传统-对象方法定义
let monster = {
name: "红孩儿", age: 100, sayHi: function () {
console.log("信息: name=", this.name, " age=", this.age);
}
}
monster.f10 = function () {
console.log("哈哈");
};
monster.sayHi();
monster.f10();
// ES6-对象方法定义
let monster2 = {
name: "红孩儿~", age: 900, sayHi() {
console.log("信息: name=", this.name, " age=", this.age);
},
f1() {
console.log("f1()");
}
}
monster2.sayHi();
monster2.f1();
</script>
对象拓展运算符
<script type="text/javascript">
let cat = {name: "小花猫", age: 2};
// let cat2 = cat;
// cat2.name = "大花猫";
// console.log("cat=>", cat);
// console.log("cat2=>", cat2);//
// 拷贝对象(深拷贝)
let cat2 = {...cat};
cat2.name = "中花猫";
console.log("cat=>", cat);//小花猫
console.log("cat2=>", cat2);//中花猫
// 合并对象[深拷贝]
let monster = {name: "白骨精", age: 100};
let car = {brand: "奔驰", price: 800000};
let monster_car = {...monster, ...car}
monster_car.name = "狐狸精";
console.log("monster=>", monster);
console.log("monster_car=>", monster_car);
</script>
箭头函数
箭头函数提供更加简洁的函数书写方式。
基本语法是:(参数列表) =>
箭头函数没有参数或有多个参数,要用 () 括起来,箭头函数只有一个参数, 可以省略()
箭头函数函数体有多行语句,用 {} 包裹起来,表示代码块
函数体只有一行语句,并且需要返回结果时,可以省略 {} , 结果会自动返回
箭头函数多用于匿名函数的定义
应用一
<script type="text/javascript">
// 传统定义函数
var f1 = function (n) {
return n * 2
}
console.log("传统= " + f1(2))
// ES6 , 箭头函数使用
let f2 = (n) => {
return n * 2;
}
console.log("f2() 结果= ", f2(100));//200
//上面的 es6 函数写法,还可以简化
let f3 = n => n * 3;
console.log("f3() 结果=", f3(100));//300
//函数也可以传给一个变量=> 看看 java 基础匿名内部类
function hi(f4) {
console.log(f4(900));
}
hi((n) => {
return n + 100
});
hi((n) => {
return n - 100
});
</script>
应用二
<script type="text/javascript">
// 传统
var f1 = function (n, m) {
var res = 0
for (var i = n; i <= m; i++) {
res += i
}
return res
}
console.log("传统= " + f1(1, 10))
// ES6 , 箭头函数使用
let f2 = (n, m) => {
var res = 0
for (var i = n; i <= m; i++) {
res += i
}
return res
};
console.log(f2(1, 10));
</script>
应用三 箭头函数+对象解构
<script type="text/javascript">
const monster = {
name: "红孩儿", age: 1000, skill: ['红缨枪', '三位真火']
}
//要求:在方法形参取出 monster 对象的 skill 属性
//传统方式
function f1(monster) {
console.log("skill=", monster.skill);
}
f1(monster);
//箭头函数
let f2 = ({skill}) => {
console.log("skill=", skill);
}
//老韩解读
//1. f2 传入对象 monster
//2. f2 形参是 {skill} , 所以 es6 的对象解构特性, 会把 monster 对象的 skill 属性
赋给
// skill
//3. 对象解构的前提就是 {skill}的 skill 和 monster 的 skill 属性是一致
f2(monster);
//箭头函数+解构, 注意有{}, skill 名称需要和对象属性名一致.
let f3 = ({age, name, skill}) => {
console.log("skill=", skill, " age=", age, " name=", name);
}
f3(monster);
</script>
注意事项和使用细节
1 箭头函数+对象解构, 注意参数是({属性名})
2 ({属性名}) 是由{} 包括的,属性名需要和对象属性名一致,使用到了对象解构
Promise
Promise 基本介绍
传统的 Ajax 异步调用在需要多个操作的时候,会导致多个回调函数嵌套,导致代码不够直观,就是常说的 Callback Hell
为了解决上述的问题,Promise 对象应运而生,在 EMCAScript 2015 当中已经成为标准
Promise 是异步编程的一种解决方案。
从语法上说,Promise 是一个对象,从它可以获取异步操作的消息
代码实现
1. 创建 D:\idea_java_projects\es6\promise\data\monster.json
{
"id": 1,
"name": "黑山老妖"
}
2. 创建 D:\idea_java_projects\es6\promise\data\monster_detail_1.json
{
"address": "阴曹地府-黑山",
"skill": "翻江倒海功",
"age": 800
}
3. 创建 D:\idea_java_projects\es6\promise\ajax.html, 先使用 ajax 传统方式完成, 问题分
析(出现回调函数嵌套)
<!--引入 jquery-->
<script type="text/javascript" src="script/jquery-3.6.0.min.js"></script>
<script type="text/javascript">
//jquery 发出 ajax 的方式-回顾
$.ajax({
url: "data/monster.json", success(resultData) {//如果请求成功了,回调处理函数 success
console.log("第 1 次 ajax 请求 monster 基本信息=", resultData);
//发出第二次 ajax 请求
$.ajax({
url: `data/monster_detail_${resultData.id}.json`,
//下面是 es6 对象的方法简写形式
success(resultData) {
console.log("第 2 次 ajax 请求 monster 详细信息=", resultData);
//$.ajax => callback hell
//$.ajax
//$.ajax
}, error(err) { //出错的回调函数
console.log("出现异常=", err);
}
})
}, error(err) {
console.log("出现异常=", err);
}
})
</script>
4. 修改 D:\idea_java_projects\es6\promise\promise.html, 使用 promise 方式完成
<script type="text/javascript" src="script/jquery-3.6.0.min.js"></script>
<script type="text/javascript">
//先请求到 monster.json
//老韩解读
//1. 创建 Promise 对象
//2. 构造函数传入一个箭头函数
//3. (resolve, reject) 参数列表 resolve: 如果请求成功, 调用 resolve 函数
//4. 如果请求失败, 调用 reject 函数
//5. 箭头函数体, 仍然是通过 jquery 发出 ajax
let p = new Promise((resolve, reject) => {
//发出 ajax
$.ajax({
url: "data/monster.json", success(resultData) {//成功的回调函数
console.log("promise 发 出 的 第 1 次 ajax monster 基 本 信 息 =",
resultData
)
;
resolve(resultData);
}, error(err) {
//console.log("promise 1 发出的异步请求异常=", err);
reject(err);
}
})
})
//这里我们可以继续编写请求成功后的业务
p.then((resultData) => {
//这里我们可以继续发出请求
//console.log("p.then 得到 resultData", resultData);
return new Promise((resolve, reject) => {
$.ajax({
url: `data/monster_detail_${resultData.id}.json`, success(resultData) { //第 2 次 ajax 请求成功,回调函数
console.log(" 第 2 次 ajax 请 求 monster 的 详 细 信 息 =", resultData);
//继续进行下一次的请求
resolve(resultData);
}, error(err) { //第 2 次 ajax 请求失败,回调函数
//console.log("promise2 发出的异步请求异常=", err);
reject(err);
}
})
})
}).then((resultData) => {
console.log("p.then().then(), resultData", resultData)
//即可以在这里发出第 3 次 ajax 请求=》 获取该妖怪的女友
return new Promise((resolve, reject) => {
$.ajax({
url: `data/monster_gf_${resultData.gfid}.json`, success(resultData) { //第 3 次 ajax 请求成功,回调函数
console.log("第 3 次 ajax 请求 monster 女友的详细信息=", resultData);
//继续进行下一次的请求
//resolve(resultData);
}, error(err) { //第 2 次 ajax 请求失败,回调函数
//console.log("promise2 发出的异步请求异常=", err);
//reject(err);
}
})
})
}).catch((err) => { //这里可以对多次 ajax 请求的异常进行处理
console.log("promise 异步请求异常=", err);
})
</script>
5. 修改 D:\idea_java_projects\es6\promise\promise2.html, 使用 promise 代码优化/重排
方式完成
<script type="text/javascript" src="script/jquery-3.6.0.min.js"></script>
<script type="text/javascript">
/**
* 这里我们将重复的代码,抽出来,编写一个方法 get *
* @param url ajax 请求的资源
* @param data ajax 请求携带的数据
* @returns {Promise<unknown>} */
function get(url, data) {
return new Promise((resolve, reject) => {
$.ajax({
url: url,
data: data,
success(resultData) {
resolve(resultData);
},
error(err) {
reject(err);
}
}
)
})
}
//需求: 完成
//1. 先获取 monster.json
//2. 获取 monster_detail_1.json
//2. 获取 monster_gf_2.json
get("data/monster.json").then((resultData) => {
//第 1 次 ajax 请求成功后的处理代码
console.log("第 1 次 ajax 请求返回数据=", resultData);
return get(`data/monster_detail_${resultData.id}.json`);
}).then((resultData) => {
//第 2 次 ajax 请求成功后的处理代码
console.log("第 2 次 ajax 请求返回数据=", resultData);
return get(`data/monster_gf_${resultData.gfid}.json`);
}).then((resultData) => {
//第 3 次 ajax 请求成功后的处理代码
console.log("第 3 次 ajax 请求返回数据=", resultData);
//继续..
}).catch((err) => {
console.log("promise 请求异常=", err);
})
</script>
模块化编程
基本介绍
1 传统非模块化开发有如下的缺点:(1)命名冲突 (2)文件依赖[代码演示]
2 Javascript 代码越来越庞大,Javascript 引入模块化编程,开发者只需要实现核心的业务逻辑,其他都可以加载别人已经写好的模块
3 Javascript 使用"模 块"(module)的概念来实现模块化编程, 解决非模块化编程问题
4 模块化 也是 ES6 的新特性
ES5 模块编程
- 每个 js 文件就是一个模块,有自己的作用域。在文件中定义的变量、函数、类/对象,
都是私有的,对其他 js 文件不可见
- CommonJS 使用 module.exports={} / exports={} 导出 模块 , 使用 let/const 名称 =
require("xx.js") 导入模块
//function.js
//定义对象,变量,常量, 函数
const sum = function (a, b) {
return parseInt(a) + parseInt(b);
}
const sub = function (a, b) {
return parseInt(a) - parseInt(b);
}
let name = "韩顺平教育";
const PI = 3.14;
const monster = {
name: "牛魔王", age: 500, hi() {
console.log("hi 你好 牛魔王");
}
}
//导出
/*
老韩解读
1. module.exports 导出模块
2. 把你需要导出的数据, 写入到 {}中即可
3. 可以全部导出,也可以部分导出
4. 理解:相当于把我们导出的数据,当做一个对象
5. 如果属性名和函数/变量/对象..名字相同,可以简写
6. 有些前端, 简写 module.exports={} 成 exports={}
*/
exports = {
sum, sub, name, PI
}
// module.exports = {
// sum,
// sub,
// name,
// PI
// }
// module.exports = {
// sum: sum,
// sub: sub,
// myname: name,
// PI: PI
// }
//use.js
//导入
//老韩解读
//1. 在 es5 中, 我们通过 require 就包 对应.js 中的
//数据/对象,引入
//2. 我们使用的时候,通过 m.属性 就可以使用
//3. 如果我们导入时,不需要所有的,可以导入部分数据
const m = require("./function.js");
const {sub} = require("./function.js");
//使用
//老韩说明: 只要这里 idea 可以识别变量/函数/对象, 说明 OK
console.log(m.sub("100","200"));
console.log(m.sum(10,90));
console.log(m.name)
console.log(m.PI);
console.log(sub(19,8));
ES6 模块编程
1、ES6 使用 (1)export {名称/对象/函数/变量/常量} (2) export 定义 = (3) export default {}
导出模块
2、使用 import {} from "xx.js" / import 名称 form "xx.js" 导入模块
第一种
//common.js
//定义对象,变量,常量, 函数
const sum = function (a, b) {
return parseInt(a) + parseInt(b);
}
const sub = function (a, b) {
return parseInt(a) - parseInt(b);
}
let name = "韩顺平教育";
const PI = 3.14;
const monster = {
name: "牛魔王", age: 500, hi() {
console.log("hi 你好 牛魔王");
}
}
//es6 的导出模块/数据
/**
* 老师解读
* 1. export 就是导出模块/数据
* 2. 可以全部导出,也可以部分导出
*/
export {
sum, sub,
name, monster
}
//use_common.js
//导入
/**
* 老韩解读
* 1. 我可以{} 来接收导出的数据
* 2. 可以全部接收,也可以选择的接收
* 3. 细节: 这时要求导入的名称和导出的名称一致
*/
import {sub, sum, name} from "./common";
import {monster} from "./common";
//使用
console.log(sumx(10, 90));
console.log(name);
console.log(monster.hi())
第二种
//common2.js
//定义对象,变量,常量, 函数
//定义 sum 函数时,就直接导出
//老韩说明:如果在定义时,导出的数据, 在导入时,要保持名字一致
export const sum = function (a, b) {
return parseInt(a) + parseInt(b);
}
const sub = function (a, b) {
return parseInt(a) - parseInt(b);
}
let name = "韩顺平教育";
const PI = 3.14;
const monster = {
name: "牛魔王", age: 500, hi() {
console.log("hi 你好 牛魔王");
}
}
//use_common2.js
//可以导入模块/数据
import {sum} from "./common2";
//没有导出的数据, 是不可以导入
//import {sub} from "./common2";
console.log(sum(10, 30));
第三种
//common3.js
//定义对象,变量,常量, 函数
//演示默认导出
//如果是默认导出, 导入的时候,使用的语法
//可以这里理解, 类似把 {} 当做一个对象导出
export default {
sum(a, b) {
return parseInt(a) + parseInt(b);
}, sub(a, b) {
return parseInt(a) - parseInt(b);
}
}
//use_common3.js
//导入默认导出模块/数据
//好处是 m 名称是可以自己指定的.
//因为 m 名字, 程序员可以自己指定,因此我们就可以解决名称冲突问题
import m from "./common3";
//使用 m.xx
console.log(m.sub(80, 90));
Vue2
Vue (读音 /vjuː/,类似于 view) 是一个前端框架, 易于构建用户界面
Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或项目整合
支持和其它类库结合使用
开发复杂的单页应用非常方便
Vue 是 Vue.js 的简称
MVVM-一图胜千言
- M∶即 Model,模型,包括数据和一些基本操作
- V∶即View,视图,页面渲染结果
- VM∶即 View-Model,模型与视图间的双向操作(无需开发人员干涉)
- 在 MVVM之前,开发人员从后端获取需要的数据模型,然后要通过 DOM 操作 Model渲染到View 中。而后当用户操作视图,我们还需要通过 DOM获取 View 中的数据,然后同步到Model 中。
- 而 MVVM中的VM 要做的事情就是把DOM 操作完全封装起来,开发人员不用再关心Model 和View 之间是如何互相影响的韩顺平 Java 工程师
- 只要我们 Model 发生了改变,View上自然就会表现出来
- 当用户修改了View,Model 中的数据也会跟着改变。。
- 结果:把开发人员从繁琐的 DOM操作中解放出来,把关注点放在如何操作 Model上, 大大提高开发效率
快速入门
官网文档:https://cn.vuejs.org/v2/guide/index.html
下载: https://cn.vuejs.org/v2/guide/installation.html
<body>
<!--老师解读
1. div 元素不是必须的,也可以是其它元素,比如 span,但是约定都是将 vue 实例挂载到 div
2. 因为 div 更加适合做布局
3. id 不是必须为 app , 是程序员指定,一般我们就使用 app
-->
<div id="app">
<!--老师解读
1. {{message}} : 插值表达式
2. message 就是从 model 的 data 数据池来设置
3. 当我们的代码执行时,会到 data{} 数据池中去匹配数据, 如果匹配上, 就进行替换
, 如果没有匹配上, 就是输出空
-->
<h1>欢迎你{{message}}-{{name}}</h1>
</div>
<!--引入 vue.js-->
<script src="vue.js"></script>
<script>
//创建 Vue 对象
/**
韩顺平 Java 工程师
* 老韩解读
* 1. 创建 Vue 对象实例
* 2. 我们在控制台输出 vm 对象,看看该对象的结构!(data/listeners) *
*/
let vm = new Vue({
el: "#app", //创建的 vue 实例挂载到 id=app 的 div
data: { //data{} 表示数据池(model 的有了数据), 有很多数据 ,以 k-v 形式设置(根据业务需要来设置)
message: "Hello-Vue!",
name: "韩顺平教育"
}
})
console.log("vm=>", vm);
console.log(vm._data.message);
console.log(vm._data.name);
console.log(vm.name);
console.log(vm.message);
</script>
</body>
注意事项和使用细节
注意代码顺序,要求 div 在前,script 在后,否则无法绑定数据
从案例可以体会声明式渲染:Vue.js 采用简洁的模板语法来声明式地将数据渲染进DOM 的系统, 做到数据和显示分离
Vue 没有繁琐的 DOM 操作,如果使用 jQuery,我们需要先找到 div 节点,获取到 DOM对象,然后进行节点操作, 显然 Vue 更加简洁
数据单向渲染
1 v-bind 指令可以完成基本数据渲染/绑定
2 v-bind 简写形式就是一个冒号(:)
<body>
<div id="app">
<h1>{{message}}</h1>
<!--老师解读
1. 使用插值表达式引用 data 数据池数据是在标签体内
2. 如果是在标签/元素 的属性上去引用 data 数据池数据时,不能使用插值表达式
3. 需要使用 v-bind, 因为 v-bind 是 vue 来解析, 默认报红,但是不影响解析
4. 如果不希望看到报红, 直接 alt+enter 引入 xmlns:v-bind
-->
<!--<img src="{{img_src}}">-->
<img v-bind:src="img_src" v-bind:width="img_width">
<img :src="img_src" :width="img_width">
</div>
<script src="vue.js"></script>
<script>
let vm = new Vue({
el: "#app", //创建的 vue 实例挂载到 id=app 的 div
data: { //data{} 表示数据池(model 的有了数据), 有很多数据 ,以 k-v 形式设置(根据业务需要来设置)
message: "hello, 耗子精",
img_src: "1.jpg",
img_width: "200px"
}
})
console.log("vm=>", vm);
</script>
</body>
数据双向绑定
v-model 可以完成双向数据绑定
<body>
<div id="app">
<h1>{{message}}</h1>
<!--老韩解读
1. v-bind 是数据单向渲染: data 数据池绑定的数据变化,会影响 view
2. v-model="hobby.val" 是数据的双向渲染, (1)data 数据池绑定的数据变化,会影响 view 【底层的机制是 Data Bindings】
(2)view 关联的的元素值变化, 会影响到 data 数据池的数据【底层机制是 Dom
Listeners】
-->
<input type="text" v-model="hobby.val"><br/><br/>
<input type="text" :value="hobby.val"><br/><br/>
<p>你输入的爱好是: {{hobby.val}}</p>
</div>
<script src="vue.js"></script>
<script>
let vm = new Vue({
el: "#app", //创建的 vue 实例挂载到 id=app 的 div
data: { //data{} 表示数据池(model 的有了数据), 有很多数据 ,以 k-v 形式设置(根据业务需要来设置)
message: "hi, 输入你的爱好",
hobby: {
val: "购物"
}
}
})
console.log("vm=>", vm);
</script>
</body>
事件绑定
1使用 v-on 进行事件处理,比如: v-on:click 表示处理鼠标点击事件
2 事件调用的方法定义在 vue 对象声明的 methods 节点中
3 v-on:事件名 可以绑定指定事件
<body>
<!--视图-->
<div id="app">
<h1>{{message}}</h1>
<!--老韩解读
1. v-on:click 表示我们要给 button 元素绑定一个 click 的事件
2. sayHi() 表示绑定的方法, 在方法池 methods{} 定义的
3. 底层仍然是 dom 处理
4. 如果方法不需要传递参数,可以省略()
5. v-on:click 可以简写@, 但是需要浏览器支持
-->
<button v-on:click="sayHi()">点击输出</button>
<button v-on:click="sayOk()">点击输出</button>
<button v-on:click="sayHi">点击输出</button>
<button @click="sayOk">点击输出</button>
</div>
<!--引入我们的 vue.js-->
<script src="vue.js"></script>
<!--创建一个 vue 实例,并挂载到 id=app 的 div-->
<script>
//老师说明:这里创建的 Vue 实例, 你可以不去接收, 也可以接收
//方便我们调试信息
let vm = new Vue({
el: "#app", //创建的 vue 实例挂载到 id=app 的 div, el 就是 element 的简写
data: { //data{} 表示数据池(model 中的数据), 有很多数据 ,以 k-v 形式设置(根据业务需要来设置)
message: "Vue 事件处理的案例",
name: "韩顺平教育"
},
//老师解读:
// 1. 是一个 methods 属性, 对应的值是对象{}
// 2. 在{} 中, 可以写很多的方法, 你可以这里理解是一个方法池
// 3. 这里需要小伙伴有 js 的基础=>java web 第 4 章
methods: {
sayHi() {
console.log("hi, 银角大王~");
},sayOk() {
console.log("ok, 金角大王~");
}
}
})
</script>
注意事项和使用细节
1 如果方法没有参数,可以省略()[需要浏览器支持]
2 v-on 指令的简写形式 @ [需要浏览器支持]
修饰符
修饰符 (Modifiers) 是以(.)指明的后缀,指出某个指令以特殊方式绑定。
例如,.prevent 修饰符告诉 v-on 指令对于触发的事件调用 event.preventDefault()即阻
止事件原本的默认行为
- 事件修饰符
.stop 阻止事件继续传播
.prevent 阻止标签默认行为
.capture 使用事件捕获模式,即元素自身触发的事件先在此处处理,然后才交由内部元素进
行处理
.self 只当在 event.target 是当前元素自身时触发处理函数
.once 事件将只会触发一次
.passive 告诉浏览器你不想阻止事件的默认行为
- 键盘事件的修饰符
比如: 项目经常需要监听一些键盘事件来触发程序的执行,而 Vue 中允许在监听的时候添
加关键修饰符
<input v-on:keyup.13="submit">
- v-model 的修饰符
比如: 自动过滤用户输入的首尾空格
<input v-model.trim="msg">
<body>
<div id="app">
<!-- 老韩解读
1. 修饰符用于指出一个指令应该以特殊方式绑定。
2. v-on:submit.prevent 的.prevent 修饰符表示阻止表单提交的默认行为
3. 执行 程序员指定的方法
-->
<form action="http://www.baidu.com" v-on:submit.prevent="onMySubmit">
妖怪名: <input type="text" v-model="monster.name"><br/><br/>
<button type="submit">注册</button>
</form>
<p>服务返回的数据是{{count}}</p>
<h1>修饰符扩展案例</h1>
<button v-on:click.once="onMySubmit">点击一次</button>
<br/>
<input type="text" v-on:keyup.enter="onMySubmit">
<input type="text" v-on:keyup.down="onMySubmit">
<input type="text" v-model.trim="count">
</div>
<script src="vue.js"></script>
<script>
let vm = new Vue({
el: '#app', data: {//数据池
monster: {//monster 数据(对象)的属性, 可以动态生成
}, count: 0
}, methods: {//方法池
onMySubmit() {
//console.log("我们自己的表单提交处理...");
//"", null, undefined 都是 false
if (this.monster.name) {
console.log("提交表单 name=", this.monster.name);
//这里,程序员就可以根据自己的业务发出 ajax 请求到后端
//得到数据后,在进行数据更新
this.count = 666;
} else {
console.log("请输入名字..");
}
}
}
})
</script>
</body>
条件渲染/控制: v-if v-show
<body>
<div id="app">
<!--这里小伙伴还可以看到 checkbox 的 checked 属性的值-->
<input type="checkbox" v-model="sel">是否同意条款[v-if 实现]
<!-- 老师解读 v-if/v-else 会根据 返回的值,来决定是否动态创建对应的子组件 <h1>
-->
<h1 v-if="sel">你同意条款</h1>
<h1 v-else>你不同意条款</h1>
</div>
<script src="vue.js"></script>
<script>
//为了调试方便, 使用 vm 接收 Vue 实例
let vm = new Vue({
el: '#app', data: {//data 数据池
sel: false
}
})
</script>
</body>
<body>
<div id="app">
<!--这里小伙伴还可以看到 checkbox 的 checked 属性的值-->
<input type="checkbox" v-model="sel">是否同意条款[v-show 实现]
<h1 v-show="sel">你同意条款</h1>
<h1 v-show="!sel">你不同意条款</h1>
</div>
<script src="vue.js"></script>
<script>
//为了调试方便, 使用 vm 接收 Vue 实例
let vm = new Vue({
el: '#app', data: {//data 数据池
sel: false
}
})
</script>
</body>
v-if VS v-show
1 v-if 会确保在切换过程中,条件块内的事件监听器和子组件销毁和重建
2 v-show 机制相对简单, 不管初始条件是什么,元素总是会被渲染,并且只是对 CSS 进行切换
3 使用建议:如果要频繁地切换,建议使用 v-show ;如果运行时条件很少改变,使用 v-if 较好
列表渲染: v-for
<body>
<div id="app">
<!-- 基本语法:
<li v-for="变量 in 数字">{{ 变量 }}</li>-->
<h1>简单的列表渲染</h1>
<ul>
<li v-for="i in 3">{{i}}</li>
</ul>
<!-- 基本语法:
韩顺平 Java 工程师
<li v-for="(变量, 索引) in 值">{{ 变量 }} - {{ 索引 }}</li>
-->
<h1>简单的列表渲染-带索引</h1>
<ul>
<li v-for="(i,index) in 3">{{i}}-{{index}}</li>
</ul>
<h1>遍历数据列表</h1>
<!-- 语法:
<tr v-for="对象 in 对象数组">
<td>{{对象的属性}}</td>
</tr>
-->
<table width="400px" border="1px">
<tr v-for="(monster,index) in monsters">
<td>{{index}}</td>
<td>{{monster.id}}</td>
<td>{{monster.name}}</td>
<td>{{monster.age}}</td>
</tr>
</table>
</div>
<script src="vue.js"></script>
<script>
new Vue({
el: '#app', data: { //数据池
monsters: [
{id: 1, name: '牛魔王', age: 800}, {id: 2, name: '黑山老妖', age: 900}, {id: 3, name: '红孩儿', age: 200}
]
}
})
</script>
</body>
组件化编程
- 组件(Component) 是 Vue.js 最强大的功能之一(可以提高复用性[1.界面2.业务处理])
- 组件也是一个Vue实例,也包括∶ data、methods、生命周期函数等
- 组件渲染需要 html模板,所以增加了template 属性,值就是 HTML 模板
- 对于全局组件,任何vue 实例都可以直接在 HTML 中通过组件名称来使用组件
- data 是一个函数,不再是一个对象, 这样每次引用组件都是独立的对象/数据
普通方式
<body>
<div id="app">
<!--非组件化方式-普通方式-->
<button v-on:click="click1()">点击次数= {{count}} 次【非组件化方式】</button><br/><br/>
<!--需求是,有多个按钮,都要进行点击统计
老师解读
1. 其实三个按钮界面其实一样, 但是目前我们都重新写了一次, 复用性低
2. 点击各个按钮的业务都是对次数+1, 因此业务处理类似,但是也都重新写了一个方法, 复
用性低
3. 解决===> 组件化编程
-->
<button v-on:click="click2()">点击次数= {{count2}} 次【非组件化方式】</button><br/><br/>
<button v-on:click="click3()">点击次数= {{count3}} 次【非组件化方式】</button><br/>
</div>
<script src="vue.js"></script>
<script>
new Vue({
el: "#app", data: {//data 数据池
count: 10, count2: 10, count3: 10
},methods: {//methods 属性, 可以定义相应的方法
click1() {
this.count++;
},click2() {
this.count2++;
},click3() {
this.count3++;
}
}
})
</script>
</body>
全局组件方式
<body>
<div id="app">
<h1>组件化编程-全局组件</h1>
<!--使用全局组件-->
<counter></counter>
<br/>
<counter></counter>
<counter></counter>
<counter></counter>
<counter></counter>
</div>
<div id="app2">
<h1>组件化编程-全局组件-app2</h1>
<!--使用全局组件-->
<counter></counter>
<counter></counter>
</div>
<script src="vue.js"></script>
<script>
//1、定义一个全局组件, 名称为 counter
//2. {} 表示就是我们的组件相关的内容
//3. template 指定该组件的界面, 因为会引用到数据池的数据,所以需要是模板字符串
//4. 这里老师说明: 要把组件视为一个 Vue 实例,也有自己的数据池和 methods
//5. 这里老师说明: 对于组件,我们的数据池的数据,是使用函数/方法返回[目的是为了保证每个组件的数据是独立], 不能使用原来的方式
//6. 这时我们达到目前,界面通过 template 实现共享,业务处理也复用
//7. 全局组件是属于所有 vue 实例,因此,可以在所有的 vue 实例使用
Vue.component("counter", {
template: `<button v-on:click="click()">点击次数= {{count}} 次【全局组件化】</button>`,
data() {//这里需要注意,和原来的方式不一样!!!!
return {
count: 10
}
}, methods: {
click() {
this.count++;
}
}
})
//创建 Vue 实例,必须有
let vm = new Vue({
el: "#app"//Vue 实例的挂载点
})
let vm2 = new Vue({
el: "#app2"//Vue 实例的挂载点
})
</script>
</body>
局部组件方式
<body>
<div id="app">
<h1>组件化编程-局部组件</h1>
<!--使用局部组件 ,该组件是从挂载到 app 的 vue 中的-->
<my_counter></my_counter><br/>
<my_counter></my_counter><br/>
<my_counter></my_counter><br/>
</div>
<div id="app2">
<h1>组件化编程-局部组件-app2</h1>
<!--使用局部组件 -->
<hsp_counter></hsp_counter><br/>
<hsp_counter></hsp_counter><br/>
</div>
<script src="vue.js"></script>
<script>
//定义一个组件, 组件的名称为 buttonCounter
//老韩扩展
//1. 可以把常用的组件,定义在某个 commons.js 中 export
//2. 如果某个页面需要使用, 直接 import
const buttonCounter = {
template: `<button v-on:click="click()"> 点 击 次 数 = {{count}} 次 【 局 部 组 件 化 】</button>`,
data() {//这里需要注意,和原来的方式不一样!!!!
return {
count: 10
}
},methods: {
click() {
this.count++;
}
}
}
//创建 Vue 实例,必须有
let vm = new Vue({
el: "#app",//Vue 实例的挂载点
components: { //引入/注册某个组件, 此时 my_counter 就是一个组件, 是一个局部组件,他的使用范围在当前 vue
'my_counter': buttonCounter
}
})
let vm2 = new Vue({
el: "#app2",//Vue 实例的挂载点
components :{//引入/注册组件 buttonCounter
'hsp_counter': buttonCounter
}
})
</script>
</body>
注意事项和细节说明
- 如果方法体, 只有简单的语句,比如 count++, 那么可以进行简写, 在组件化编程.html
演示, 比如
<button v-on:click="count++"> 点 击 次 数 = {{count}} 次 【 非 组 件 化 方 式 】</button><br/>
2. 组件定义需要放置在 new Vue() 前,否则组件注册会失败
组件化小结
1 组件也是一个 Vue 实例,因此它的定义是也存在∶ data、methods、生命周期函数等
2 data 是一个函数,不再是一个对象, 这样每次引用组件都是独立的对象/数据
3 组件渲染需要 html 模板,所以增加了 template 属性,值就是 HTML 模板
生命周期和监听函数(钩子函数)
- Vue 实例有一个完整的生命周期,也就是说从开始创建、初始化数据、编译模板、挂载
DOM、渲染-更新-渲染、卸载等一系列过程,我们称为 Vue 实例的生命周期
- 钩子函数(监听函数): Vue 实例在完整的生命周期过程中(比如设置数据监听、编译模
板、将实例挂载到 DOM 、在数据变化时更新 DOM 等), 也会运行叫做生命周期钩子的函
数
- 钩子函数的 作用就是在某个阶段, 给程序员一个做某些处理的机会

1) new Vue()
new 了一个 Vue 的实例对象,此时就会进入组件的创建过程。
2) Init Events & Lifecycle
初始化组件的事件和生命周期函数
3) beforeCreate
组件创建之后遇到的第一个生命周期函数,这个阶段 data 和 methods 以及 dom 结构都未
被初始化,也就是获取不到 data 的值,不能调用 methods 中的函数
4) Init injections & reactivity
这个阶段中, 正在初始化 data 和 methods 中的方法
5) created
- 这个阶段组件的 data 和 methods 中的方法已初始化结束,可以访问,但是 dom 结构未
初始化,页面未渲染
- 老师说明:在这个阶段,经常会发起 Ajax 请求
6) 编译模板结构(在内存)
7) beforeMount
当模板在内存中编译完成,此时内存中的模板结构还未渲染至页面上,看不到真实的数据
8) Create vm.$el and replace ‘el’ with it
这一步,再在把内存中渲染好的模板结构替换至真实的 dom 结构也就是页面上
9) mounted
此时,页面渲染好,用户看到的是真实的页面数据, 生命周期创建阶段完毕,进入到了运
行中的阶段
10) 生命周期运行中
10.1 beforeUpdate
当执行此函数,数据池的数据新的,但是页面是旧的
10.2 Virtual DOM re-render and patch
根据最新的 data 数据,重新渲染内存中的模板结构,并把渲染好的模板结构,替换至页面
上
10.3 updated
页面已经完成了更新,此时,data 数据和页面的数据都是新的
11) beforeDestroy
当执行此函数时,组件即将被销毁,但是还没有真正开始销毁,此时组件的 data、methods
数据或方法 还可被调用
12) Teardown…… 注销组件和事件监听
13) destroyed
组件已经完成了销毁
- 需求: 展示 VUE 实例生命周期和 钩子函数/监听函数/生命周期函数 执行时机
- 重 点 研 究 几 个 重 要 的 钩 子 函 数 (beforeCreate, created, beforeMount, mounted,
beforeUpdate, updated)
- 在这几个钩子函数中, 数据模型是否加载/使用? 自定义方法是否加载/可用? html 模
板是否加载/使用? html 模板是否完成渲染?
<body>
<!--这里可以视为用户看到的页面-对应前面讲解的页面 dom-->
<div id="app">
<span id="num">{{num}}</span>
<button @click="num++">赞!</button>
<h2>{{name}},有{{num}}次点赞</h2>
</div>
<script src="vue.js"></script>
<script>
let vm = new Vue({
el: "#app", data: {//数据池
name: "kristina", num: 0
}, methods: {
show() {
return this.name;
}, add() {
this.num++;
}
}, beforeCreate() {//生命周期函数-创建 vue 实例前
console.log("=============beforeCreate==========");
console.log(" 数 据 模 型 / 数 据 池 的 数 据 是 否 加 载 / 使 用 ?[no]", this.name, " ", this.num);
//console.log("自定义方法是否加载/使用?[no]", this.show());
console.log(" 用 户 页 面 dom 是 否 加 载 / 使 用 ?[yes]", document.getElementById("num"));
console.log(" 用 户 页 面 dom 是 否 被 渲 染 ?[no]", document.getElementById("num").innerText);
}, created() {//生命周期函数-创建 vue 实例
console.log("=============created==========");
console.log(" 数 据 模 型 / 数 据 池 的 数 据 是 否 加 载 / 使 用 ?[yes]", this.name, " ", this.num);
console.log("自定义方法是否加载/使用?[yes]", this.show());
console.log(" 用 户 页 面 dom 是 否 加 载 / 使 用 ?[yes]", document.getElementById("num"));
console.log(" 用 户 页 面 dom 是 否 被 渲 染 ?[no]", document.getElementById("num").innerText);
//可以发出 Ajax
//接收返回的数据
//再次去更新 data 数据池的数据
//编译内存模板结构
//.....
},beforeMount() {//生命周期函数-挂载前
console.log("=============beforeMount==========");
console.log(" 数 据 模 型 / 数 据 池 的 数 据 是 否 加 载 / 使 用 ?[yes]", this.name, " ", this.num);
console.log("自定义方法是否加载/使用?[yes]", this.show());
console.log(" 用 户 页 面 dom 是 否 加 载 / 使 用 ?[yes]", document.getElementById("num"));
console.log(" 用 户 页 面 dom 是 否 被 渲 染 ?[no]", document.getElementById("num").innerText);
}, mounted() {//生命周期函数-挂载后
console.log("=============mounted==========");
console.log(" 数 据 模 型 / 数 据 池 的 数 据 是 否 加 载 / 使 用 ?[yes]", this.name, " ", this.num);
console.log("自定义方法是否加载/使用?[yes]", this.show());
console.log(" 用 户 页 面 dom 是 否 加 载 / 使 用 ?[yes]", document.getElementById("num"));
console.log(" 用 户 页 面 dom 是 否 被 渲 染 ?[yes]", document.getElementById("num").innerText);
}, beforeUpdate() {//生命周期函数-数据池数据更新前
console.log("=============beforeUpdate==========");
console.log(" 数 据 模 型 / 数 据 池 的 数 据 是 否 加 载 / 使 用 ?[yes]", this.name, " ", this.num);
console.log("自定义方法是否加载/使用?[yes]", this.show());
console.log(" 用 户 页 面 dom 是 否 加 载 / 使 用 ?[yes]", document.getElementById("num"));
console.log(" 用 户 页 面 dom 数 据 是 否 被 更 新 ?[no]", document.getElementById("num").innerText);
//验证数据==>修正
// if(this.num < 10 ) {
// this.num = 8;
// }
}, updated() {//生命周期函数-数据池数据更新后
console.log("=============updated==========");
console.log(" 数 据 模 型 / 数 据 池 的 数 据 是 否 加 载 / 使 用 ?[yes]", this.name, " ", this.num);
console.log("自定义方法是否加载/使用?[yes]", this.show());
console.log(" 用 户 页 面 dom 是 否 加 载 / 使 用 ?[yes]", document.getElementById("num"));
console.log(" 用 户 页 面 dom 数 据 是 否 被 更 新 ?[yes]", document.getElementById("num").innerText);
}
})
</script>
</body>
脚手架


Axios
1 axios 是独立于 vue 的一个项目,不是 Vue 的一部分
2 axios 通常和 Vue 一起使用,实现 Ajax 操作
3 Axios 是一个基于 promise 的 HTTP 库
<body>
<div id="app">
<h1>妖怪列表</h1>
<table border="1" width="300">
<tr>
<td>名字</td>
<td>年龄</td>
</tr>
<tr v-for="monster in monsterList">
<td>{{monster.name}}</td>
<td>{{monster.age}}</td>
</td>
</tr>
</table>
</div>
<script src="vue.js"></script>
<script src="axios.min.js"></script>
<script>
var app = new Vue({
el: '#app',
// data / created() / methods 是一个固定结构
data: {
monsterList: []//数组
},created() { //在页面渲染前执行,调用 list()
this.list()
},methods: {
list() {
/** 老韩解读
* 1. 使用 axios 发送 ajax 请求
* 2. 语 法 格 式 axios. 请 求 方 式 ( 请 求 路 径 ).then( 箭 头 函
数).catch(箭头函数)
* 3. 请求成功,执行 then 的函数, response 就是返回的数据, 名
字有程序员确定
* 4. 请求失败, 执行 catch 的函数
* 5. this.monsterList = response.data.data.items 把 返 回 的
data.items 赋给 monsterList
* 6. 这里的 http://127.0.0.1:63342/axios/response.data.json 路
径需要根据实际的端口和资源名来修改
*/
axios.get('http://localhost:63342/axios/response.data.json')
.then(response => {
console.log(response)
this.monsterList = response.data.data.items
})
.catch(error => {
console.log(error)
})
}
}
})
</script>
</body>
Tomcat
官方文档: https://tomcat.apache.org/tomcat-8.0-doc/
WEB 开发介绍
- WEB,在英语中 web 表示网/网络资源(页面,图片,css,js)意思,它用于表示 WEB 服务器(主机)供浏览器访问的资源
- WEB 服务器(主机)上供外界访问的 Web 资源分为:
- 静态 web 资源(如 html 页面):指 web 页面中供人们浏览的数据始终是不变。
- 动态 web 资源,比如 Servlet(java)、PHP 等。
- 静态 web 资源开发技术
- Html、CSS,js 等
- 常用动态 web 资源开发技术:
Servlet、SpringBoot、SpringMVC、PHP、ASP.NET 等
JavaWeb 开发技术栈图-一图胜千言

BS 与 CS 开发介绍
- B: browser(浏览器, 种类太多 ff, chrome, ie, edge,)
- S: Server(服务端, 考虑很多)
BS 的解读
(1) 兼容性 , 因为浏览器的种类很多,发现你写的程序,在某个浏览器会出现问题,其它浏览器正常
(2) 安全性, 通常情况下,BS 安全性不如 CS 好控制
(3) 易用性, BS 好于 CS, 浏览器电脑有
(4) 扩展性, BS 相对统一,只需要写 Server

手写简单 Web 服务程序
/**
* @author 韩顺平
* @version 1.0
* 这是我们自己写的一个 web 服务, 可以返回 hello.html 给浏览器
*/
public class MyTomcat {
public static void main(String[] args) throws IOException {
//1.在 9999 端口监听
ServerSocket serverSocket = new ServerSocket(9999);
//如果 serverSocket 没有关闭,就等待连接, 不停的等待
while (!serverSocket.isClosed()) {
System.out.println("=====我的 web 服务在 9999 端口监听=====");
//2. 等待浏览器/客户端连接, 得到 socket
// 该 socket 用于通信
Socket socket = serverSocket.accept();
//3. 通过 socket 得到 输出流,[]
OutputStream outputStream = socket.getOutputStream();
// 返回给浏览器/客户端
//4. 读取 hello.html 文件返回即可=> 如何读取文件内容
// 得到文件输入流(字符输入流), 和 src/hello.html
BufferedReader bufferedReader =
new BufferedReader(new FileReader("com.ybs/java/hello.html"));
String buf = "";
// 循环读取 hello.html
while ((buf = bufferedReader.readLine()) != null) {
outputStream.write(buf.getBytes());
}
outputStream.close();
socket.close();
}
serverSocket.close();
}
}
Tomcat 目录结构

老师解读
server.xml 用于配置 tomcat 的基本设置(启动端口,关闭端口, 主机名)
wex.xml 用于指定 tomcat 运行时配置(比如 servlet 等..)
webapps 目录是存放 web 应用,就是网站
IDEA 开发 JavaWeb 工程
开发 javaweb 工程 & 配置 TomCat &启动项目
注意事项和细节
1.热加载选项说明

老韩解读
(1) on update action : 表示当我们更新操作是, Tomcat 会自动更新类和资源 (当jsp/html文 件修改时,可以生效, 但是如果你修改的 java 文件, 需要 Redepoly 才会生效)
(2) on frame deactivation : 表示 IDEA 失去焦点(比如最小化), 也会导致 jsp/html 发生更 新 , 但是 java 修改了,还是需要 redeploy
2.端口修改

这里修改的端口, 只会影响到当前的项目,而不是去修改 server.xml
3. out 目录是什么
当 tomcat 启动时,会生成 out 目录,该目录就是 原项目资源的映射,我们浏览器访问的 资源是 out 目录
- 当我们从外部拷贝资源到项目(图片, 文件, js , css 等), 如果出现 404 不能访问错误, 解 决方式 rebulid project -> 重启 Tomcat
JavaWeb 工程的目录介绍

浏览器访问 Web 服务过程详解

动态 WEB 开发核心-Servlet

什么是 Servlet
Servlet 在开发动态 WEB 工程中,得到广泛的应用,掌握好 Servlet 非常重要了, Servlet(基 石)是 SpringMVC 的基础
Servlet(java 服务器小程序),它的特点:
- 他是由服务器端调用和执行的(一句话:是Tomcat解析和执行)
- 他是用java语言编写的, 本质就是Java类
- 他是按照Servlet规范开发的(除了tomcat->Servlet weblogic->Servlet)
- 功能强大,可以完成几乎所有的网站功能(在以前,我们老程员,使用Servlet开发网站) 技 术栈要求高
Servlet 基本使用
1. servlet3.0 前使用 web.xml , servlet3.0 版本以后(包括 3.0)支持注解, 同时支持 web.xml配置
手动开发 Servlet
需求说明
1、开发一个 HelloServlet
2、当浏览器 访问 http://localhost:8080/web 应用名/helloServlet 时,后台输出 "hi HelloServelt"
具体步骤
- 编写类HelloServlet去实现 Servlet 接口
- 实现 service 方法,处理请求,并响应数据
- 在 web.xml 中去配置 servlet 程序的访问地址
package com.ybs;
/**
* @author 韩顺平
* @version 1.0
*/
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* 老韩解读
* 1. 开发一个Servlet 需要 实现Servlet接口
* 2. 实现Servlet接口的方法5个
*/
public class HelloServlet implements Servlet {
private int count = 0; //属性
/**
* 1.初始化 servlet
* 2.当创建HelloServlet 实例时,会调用init方法
* 3. 该方法只会被调用一次
* @param servletConfig
* @throws ServletException
*/
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("init() 被调用");
}
/**
* 返回ServletConfig 也就是返回Servlet的配置
* @return
*/
@Override
public ServletConfig getServletConfig() {
return null;
}
/**
* 1. service方法处理浏览器的请求(包括get/post)
* 2. 当浏览器每次请求Servlet时,就会调用一次service
* 3. 当tomcat调用该方法时,会把http请求的数据封装成实现ServletRequest接口的request对象
* 4. 通过servletRequest 对象,可以得到用户提交的数据
* 5. servletResponse 对象可以用于返回数据给tomcat->浏览器
* @param servletRequest
* @param servletResponse
* @throws ServletException
* @throws IOException
*/
@Override
public void service(ServletRequest servletRequest,
ServletResponse servletResponse) throws ServletException, IOException {
count++;
//如果count的值,在不停的累计,说明HelloServlet是单例的
System.out.println("hi HelloServlet~ count= " + count);
//Tomcat每处理一次http请求,就生成一个新的线程
System.out.println("当前线程id= " + Thread.currentThread().getId());
//思考->从servletRequest对象来获取请求方式->
//1. ServletRequest 没有得到提交方式的方法
//2. ServletRequest 看看ServletRequest子接口有没有相关方法
//3. 老师小技巧:ctrl+alt+b => 可以看到接口的子接口和实现子类
//4. 把servletReqeust转成 HttpServletRequest引用
//5. 仍然是Java基础的OOP
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String method = httpServletRequest.getMethod();
if("GET".equals(method)) {
doGet(); //用doGet() 处理GET请求
} else if("POST".equals(method)) {
doPost(); //用doPost() 处理POST请求
}
}
/**
* 用于响应get请求的
*/
public void doGet() {
System.out.println("doGet() 被调用..");
}
/**
* 用于响应post请求的
*/
public void doPost() {
System.out.println("doPost() 被调用..");
}
/**
* 返回servlet信息,使用较少
* @return
*/
@Override
public String getServletInfo() {
return null;
}
/**
* 1. 该方法是在servlet销毁时,被调用
* 2. 只会调用一次
*/
@Override
public void destroy() {
System.out.println("destroy() 被调用...");
}
}
<!--老韩小技巧: web.xml主要用来配置该web应用使用到的Servlet
老师解读
1. servlet-name: 给Servlet取名(程序员决定), 该名字唯一
2. servlet-class: Servlet的类的全路径: Tomcat在反射生成该Servlet需要使用
3. url-pattern: 这个就是该servlet访问的url的配置(路径)
4. 这时我们应该这样访问servlet http://localhost:8080/servlet/helloServlet
5. url-pattern 取名是程序员决定的
6. load-on-startup 表示在tomcat 启动时,会自动的加载servlet实例
小技巧: alt+r => 老师配置
-->
<!-- 配置HelloServlet -->
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.ybs.HelloServlet</servlet-class>
<!--<load-on-startup>1</load-on-startup>-->
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/helloServlet</url-pattern>
</servlet-mapping>
浏览器调用 Servlet 流程分析


Servlet 生命周期
主要有三个方法
init()初始化阶段
service()处理浏览器请求阶段
destroy()终止阶段

● 初始化阶段
Servlet 容器(比如: Tomcat)加载 Servlet,加载完成后,Servlet 容器会创建一个 Servlet 实例 并调用 init()方法,init()方法只会调用一次, Servlet 容器在下面的情况装载 Servlet:
Servlet 容器(Tomcat)启动时自动装载某些 servlet,实现这个需要在 web.xml 文件中添加
<load-on-startup>1</load-on-startup>
表示装载的顺序在 Servlet 容器启动后,浏览器首次向 Servlet 发送请求(这个前面说过)
Servlet 重新装载时(比如 tomcat 进行 redeploy【redeploy 会销毁所有的 Servlet 实例】), 浏览器再向 Servlet 发送请求的第 1 次
● 处理浏览器请求阶段(service 方法)
每收到一个 http 请求,服务器就会产生一个新的线程去处理[线程]
创建一个用于封装 HTTP 请求消息的 ServletRequest 对象和一个代表 HTTP 响应消息的 ServletResponse 对象
然后调用 Servlet 的 service()方法并将请求和响应对象作为参数传递进去
● 终止阶段 destory 方法(体现 Servlet 完整的生命周期)
当web 应用被终止,或者Servlet 容器终止运行,或者Servlet 类重新装载时,会调用 destroy() 方法, 比如重启 tomcat ,或者 redeploy web 应用
GET 和 POST 请求的分发处理
开发 Servlet, 通常编写 doGet、doPost 方法。来对表单的 get 和 post 请求进行分发处理
1.创建register.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册</title>
</head>
<body>
<h1>注册用户</h1>
<form action="http://localhost:8080/tomcat/helloServlet" method="get">
u: <input type="text" name="username"/><br><br>
<input type="submit" value="注册用户"/>
</form>
</body>
</html>
package com.ybs;
/**
* @author 韩顺平
* @version 1.0
*/
import javax.servlet.*;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* 老韩解读
* 1. 开发一个Servlet 需要 实现Servlet接口
* 2. 实现Servlet接口的方法5个
*/
public class HelloServlet implements Servlet {
private int count = 0; //属性
/**
* 1.初始化 servlet
* 2.当创建HelloServlet 实例时,会调用init方法
* 3. 该方法只会被调用一次
* @param servletConfig
* @throws ServletException
*/
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("init() 被调用");
}
/**
* 返回ServletConfig 也就是返回Servlet的配置
* @return
*/
@Override
public ServletConfig getServletConfig() {
return null;
}
/**
* 1. service方法处理浏览器的请求(包括get/post)
* 2. 当浏览器每次请求Servlet时,就会调用一次service
* 3. 当tomcat调用该方法时,会把http请求的数据封装成实现ServletRequest接口的request对象
* 4. 通过servletRequest 对象,可以得到用户提交的数据
* 5. servletResponse 对象可以用于返回数据给tomcat->浏览器
* @param servletRequest
* @param servletResponse
* @throws ServletException
* @throws IOException
*/
@Override
public void service(ServletRequest servletRequest,
ServletResponse servletResponse) throws ServletException, IOException {
count++;
//如果count的值,在不停的累计,说明HelloServlet是单例的
System.out.println("hi HelloServlet~ count= " + count);
//Tomcat每处理一次http请求,就生成一个新的线程
System.out.println("当前线程id= " + Thread.currentThread().getId());
//思考->从servletRequest对象来获取请求方式->
//1. ServletRequest 没有得到提交方式的方法
//2. ServletRequest 看看ServletRequest子接口有没有相关方法
//3. 老师小技巧:ctrl+alt+b => 可以看到接口的子接口和实现子类
//4. 把servletReqeust转成 HttpServletRequest引用
//5. 仍然是Java基础的OOP
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String method = httpServletRequest.getMethod();
if("GET".equals(method)) {
doGet(); //用doGet() 处理GET请求
} else if("POST".equals(method)) {
doPost(); //用doPost() 处理POST请求
}
}
/**
* 用于响应get请求的
*/
public void doGet() {
System.out.println("doGet() 被调用..");
}
/**
* 用于响应post请求的
*/
public void doPost() {
System.out.println("doPost() 被调用..");
}
/**
* 返回servlet信息,使用较少
* @return
*/
@Override
public String getServletInfo() {
return null;
}
/**
* 1. 该方法是在servlet销毁时,被调用
* 2. 只会调用一次
*/
@Override
public void destroy() {
System.out.println("destroy() 被调用...");
}
}
通过继承 HttpServlet 开发 Servlet
HttpServlet 介绍
在实际项目中,都是使用继承 HttpServlet 类开发 Servlet 程序,更加方便

1、通过继承 HttpServlet 开发一个 HiServlet
2、当浏览器 访问 http://localhost:8080/web 应用名/hiServlet 时,后台输出 "hi HiServelt"
具体的开发步骤
编写一个类去继承 HttpServlet 类
根据业务需要重写 doGet 或 doPost 方法
到 web.xml 中的配置 Servlet 程序
package com.ybs;
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 HiServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HiServlet doGet");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HiServlet doPost");
}
}
<!-- 配置HiServlet -->
<servlet>
<servlet-name>HiServlet</servlet-name>
<servlet-class>com.ybs.HiServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HiServlet</servlet-name>
<url-pattern>/hiServlet</url-pattern>
</servlet-mapping>
Servlet 注意事项和细节
Servlet 是一个供其他 Java 程序(Servlet 引擎)调用的 Java 类,不能独立运行
针对浏览器的多次 Servlet 请求,通常情况下,服务器只会创建一个 Servlet 实例对象, 也就是说 Servlet 实例对象一旦创建,它就会驻留在内存中,为后续的其它请求服务,直至 web 容器退出/或者 redeploy 该 web 应用,servlet 实例对象才会销毁
在 Servlet 的整个生命周期内,init 方法只被调用一次。而对每次请求都导致 Servlet 引 擎调用一次 servlet 的 service 方法。
对于每次访问请求,Servlet 引擎都会创建一个新的 HttpServletRequest 请求对象和一个 新的 HttpServletResponse 响应对象,然后将这两个对象作为参数传递给它调用的 Servlet 的 service()方法,service 方法再根据请求方式分别调用 doXXX 方法
如果在
<servlet>
元素中配置了一个<load-on-startup>
元素,那么 WEB 应用程序在启动时, 就会装载并创建 Servlet 的实例对象、以及调用 Servlet 实例对象的 init()方法, 老师聊聊(定 时发送邮件的服务/自动启动->完成任务)
Servlet - 注解方式
具体步骤
编写类OkServlet去继承HttpServlet
注解方式配置OkServlet, 一个Servlet支持配置多个urlPattern
package com.ybs.annotation;
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(urlPatterns = {"/ok1"})
public class OkServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("OkServlet doGet");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("OkServlet doPost");
}
}
注解方式代码撑腰
package com.ybs.annotation;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author 韩顺平
* @version 1.0
* 注解的方式来配置
*/
/**
* 老韩解读
* 老韩的小技巧: 显示方法之间的分割线
* 1. @WebServlet 是一个注解 => java基础->注解
* 2. @WebServlet 源码
*
* @Target({ElementType.TYPE})
* @Retention(RetentionPolicy.RUNTIME)
* @Documented => 在javadoc工具生成文档有记录
* public @interface WebServlet {
* String name() default "";
* <p>
* String[] value() default {};
* <p>
* String[] urlPatterns() default {};
* <p>
* int loadOnStartup() default -1;
* <p>
* WebInitParam[] initParams() default {};
* <p>
* boolean asyncSupported() default false;
* <p>
* String smallIcon() default "";
* }
* 3. urlPatterns 对应 web.xml 的 <url-pattern></url-pattern>
* 4. {"/ok1", "/ok2"} 可以给OkServlet配置多个 url-pattern
* 5. 相当于这个@WebServlet(urlPatterns = {"/ok1", "/ok2"}) 代替了 web.xml的配置
* 底层使用了 反射+注解+IO+集合 来完成一个支撑
* 6. 浏览器可以这样访问OkServlet时,可以 http://localhost:8080/servlet/ok1 或者
* http://localhost:8080/servlet/ok2
* 7. 同学们有很多小问号? 解密. => 一会老师再说.
* 8. 我们可以根据 @interface WebServlet 源码知道可以配置哪些
* web.xml init-param 在注解中,如何指定呢? 老师看了源码,老师搞定
* <init-param>
* <param-name></param-name>
* <param-value></param-value>
* </init-param>
* 9. 注解方式开发Servlet和 web.xml配置servlet 流程机制是一样
* 10. /ok1/aa /ok1/bb /ok1/cc /ok1/aa/bb/cc /ok2
* 11. *.action 这时 zs.action ls.action
* 12. http://localhost:8080/servlet/register.html
*/
@WebServlet(urlPatterns = {"/ok1/aa"})
public class OkServlet extends HttpServlet {
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("注解方式 OkServlet init()被调用");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("注解方式 OkServlet doPost()");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println(10 / 0);//报错
System.out.println("注解方式 OkServlet doGet()");
System.out.println(req.getRemoteAddr());
}
}
package com.ybs.annotation;
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.HttpsServer;
import javax.servlet.annotation.WebServlet;
import java.util.HashMap;
/**
* @author 韩顺平
* @version 1.0
* 模拟一把Tomcat是如果通过 @WebServlet(urlPatterns = {"/ok1", "/ok2"})
* 来装载一个Servlet的
* <p>
* 说明:这代码主要的目的,就是打破 注解的神秘感
*/
public class TestAnnotationServlet {
private static final HashMap<String, Object> hm = new HashMap<>();
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
//1. 首先要得到扫描的包 路径 io, 进而得到类的全路径
String classAllPath = "com.hspedu.servlet.annotation.OkServlet";
//2. 得到 OkServlet的Class对象
Class<?> aClass = Class.forName(classAllPath);
//3. 通过class对象,得到Annotation
WebServlet annotation = aClass.getAnnotation(WebServlet.class);
System.out.println(annotation);
String[] strings = annotation.urlPatterns();
for (String url : strings) {
System.out.println("url= " + url);
}
//如果匹配url,如果是第一次,tomcat就会创建一个OkServlet实例,放入到hashmap
Object instance = aClass.newInstance();
System.out.println("instance= " + instance);//OkServlet
//简单的模拟,没有深入.
hm.put("OkServlet", instance);
System.out.println(hm);
}
}
Servlet urlPattern 配置
精确匹配
配置路径 : @WebServlet("/ok/zs")
访问 servlet: localhost:8080/servlet/ok/zs
目录匹配
配置路径 : @WebServlet("/ok/*")
访问文件: localhost:8080/servlet/ok/aaa localhost:8080/servlet/ok/bbb
扩展名匹配
配置路径 : @WebServlet("*.action")
访问文件: localhost:8080/hsp/zs.action localhost:8080/hsp/ls.action
老韩提示: @WebServlet("/*.action")
, 不能带 / , 否则 tomcat 报错
任意匹配
配置路径 : @WebServlet("/") @WebServlet("/*")
访问文件: localhost:8080/hsp/aaa localhost:8080/hsp/bbb localhost:8080/hsp/ccc
老师提醒:/ 和 /*的配置,会匹配所有的请求,这个比较麻烦,要避免
注意事项和使用细节
1、当 Servlet 配置了 "/", 会覆盖 tomcat 的 DefaultServlet, 当其他的 utl-pattern 都匹配 不 上 时 , 都 会 走 这 个 Servlet, 这 样 可 以 拦 截 到 其 它 静 态 资 源
The default servlet for all web applications, that serves static resources.
这个默认的 servlet 是处理静态资源的,一旦拦截,静态资源不能处理
2、当 Servelt 配置了 "/*"
, 表示可以匹配任意访问路径
3、提示: 建议不要使用 / 和 /*, 建议尽量使用精确匹配
4、优先级遵守: 精确路径 > 目录路径 > 扩展名路径 > /* > /
关联篇:HTTP 协议!!
WEB 开发通信协议-HTTP 协议
HTTP 请求包分析(GET)
- HTTP 请求包分析(GET)图, 说明 可以通过浏览器抓包分析 请求行 请求头

案例说明
package com.ybs;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class LoginServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.print("<h1>登录成功~</h1>");
writer.flush();
writer.close();
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws
ServletException, IOException {
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户登录</title>
</head>
<body>
<h1>用户登录</h1>
<form action="http://localhost:8080/http/login" method="get">
u: <input type="text" name="username"/><br/>
p: <input type="password" name="pwd"/><br/>
<input type="submit" value="用户登录"> <input type="reset" value="清空">
</form>
</body>
</html>
<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>com.ybs.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>

HTTP 请求包分析(POST)
package com.ybs;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class LoginServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.print("<h1>登录成功~</h1>");
writer.flush();
writer.close();
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.print("<h1>登录成功~</h1>");
writer.flush();
writer.close();
}
}

GET 请求 POST 请求分别有哪些?
- form 标签 method=get [指定]
- a 标签
- link 标签引入 css [以get方式来获取资源]
- Script 标签引入 js 文件 [以get方式来获取资源]
- img 标签引入图片 [以get请求来获取图片]
- iframe 引入 html 页面 [不演示]
- 在浏览器地址栏中输入地址后敲回车[用的比较多]
POST 请求有哪些
form 标签 method=post
HTTP 请求中怎样选择 Get 和 Post 方式
老韩说明:在大部分情况下,我们不需要考虑这个问题,因为业务本身就会自动区别, 比如你要显示图片,引入 css/js 这个天然的就是 get 请求,比如你登录,发帖,上传文 件, 你就会使用 post(感情的自然流露)
传输的数据大小区别
get 传送的数据量较小。不能大于 2KB(不同浏览器不一样)。
post 传送的数据量较大。一般默认不受限制。
什么情况下使用 post 请求
- post 请求是会在浏览器上隐藏參数部分的,在安全要求的部分都会使用到 POST 请求。
如用户登录。数据增上改等等。都会把參数隐藏起来,这样就不会通过你的请求暴露你
的參数格式。
比方:del?id=1,别人就能够用 del?id=3 来删除你其它数据。
- 在向 server 传递数据较大的时候。使用 POST,get 是有限制的, 比如发帖, 上传文件
什么情况下使用 get 方式呢
- 在前台页面展示,比如分页内容等,可以保留传递参数, 可用来非常好的分享和传播,
POST 中链接地址是不变化的。
建议:
get 方式的安全性较 Post 方式要差些。包括机密信息的话。建议用 Post 数据提交方式;
在做数据查询时。建议用 Get 方式;而在做数据加入、改动或删除时,建议用 Post
方式
HTTP 响应包分析
● HTTP 响应包括 3 个部分
1. 响应行
2. 响应头
3. 响应体

常用的状态码说明
状态行
- 格式: HTTP版本号 状态码 原因叙述
<CRLF>
- 2举例:HTTP/1.1 200 OK
状态码用于表示服务器对请求的处理结果,它是一个三位的十进制数。响应状态码分为 5类,如下所示:

演示 302 状态码使用
T1Servlet 返回 302 的状态码, 并且指定浏览器重定向到 hi.html

演示 304 状态码使用
当我们请求资源的时,服务器会返回该资源的最近修改时间
Last-Modified: Mon, 21 Feb 2022 04:51:31 GMT
如果浏览器禁用缓存, 这个 Last-Modified: 信息就没有使用, 浏览器就每次要求返回该资源
1.如果浏览器没有禁用缓存,
浏览器在请求时,就会 If-Modified-Since: Mon, 21 Feb 2022 04:51:31 GMT, 含义
(1) 告诉服务器我有该资源
(2) 该资源的最近修改时间是 Mon, 21 Feb 2022 04:51:31GM
2.这时服务器就会比较时间,如果服务器的资源更新, 就会返回该资源 , 如果发现没有 修改,就返回304 状态码(但是不会返回该资源) http 请求机制
MIME 类型
1.MIME 是 HTTP 协议中数据类型。 MIME 的英文全称是"Multipurpose Internet Mail Extensions" 多功能 Internet 邮件扩充服务。MIME 类型的格式是"大类型/小类型", 并与某一种文件的扩展名相对应

常见的 MIME 类型

ServletConfig
ServletConfig 基本介绍
- ServletConfig 类是为 Servlet 程序的配置信息的类
- Servlet 程序和 ServletConfig 对象都是由 Tomcat 负责创建
- Servlet 程序默认是第 1 次访问的时候创建,ServletConfig 在 Servlet 程序创建时,就创 建一个对应的 ServletConfig 对 象
ServletConfig 类能干什么
获取 Servlet 程序的 servlet-name 的值
获取初始化参数 init-param
获取 ServletContext 对象
ServletConfig 应用实例
在 web.xml 配置连接 mysql 的用户名和密码
在 DBServlet 执行 doGet()/doPost() 时,可以获取到 web.xml 配置的用户名和密码
package com.ybs;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.Serializable;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author 韩顺平
* @version 1.0
*/
public class DBServlet extends HttpServlet {
/**
* 老师梳理ServletConfig config 使用流程
* 1. 当DBServlet对象初始化时, tomcat会同时创建一个 ServletConfig对象
* 2. 这时如果DBServlet init() 方法中你调用 super.init(config);
* 3. 调用 父类 GenericServlet
* public void init(ServletConfig config) throws ServletException {
* this.config = config;
* this.init();
* }
* 这时就会把 Tomcat创建的 ServletConfig对象赋给 GenericServlet的属性 config
* 4. 因此如果你重写init()方法,记住如果你想在其它方法通过 getServletConfig() 方法获取ServletConfig
* , 则一定要记住 调用 super.init(config);
* @param config
* @throws ServletException
*/
@Override
public void init(ServletConfig config) throws ServletException {
//ConcurrentHashMap, 是一个线程安全的容器.
System.out.println("init" + config);
super.init(config);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//在DBServlet 执行 doGet()/doPost() 时,可以获取到web.xml配置的用户名和密码
//你是一个OOP程序员->现有的方法或对象来搞定
//DBServlet的父类GenericServlet有getServletConfig()
/**
* 老韩解读
* 1. getServletConfig() 方法是 GenericServlet
* 2. 返回的 servletConfig对象是 GenericServlet private transient ServletConfig config;
* 3. 当一个属性被 transient 修饰,表示该属性不会被串行化(有些重要信息,不希望保存到文件)
*/
ServletConfig servletConfig = getServletConfig();
System.out.println("doPost=" + servletConfig);
String username = servletConfig.getInitParameter("username");
String pwd = servletConfig.getInitParameter("passwd");
System.out.println("初始化参数username= " + username);
System.out.println("初始化参数pwd= " + pwd);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
<servlet>
<servlet-name>DbServlet</servlet-name>
<servlet-class>com.ybs.DBServlet</servlet-class>
<init-param>
<param-name>username</param-name>
<param-value>ybs</param-value>
</init-param>
<init-param>
<param-name>passwd</param-name>
<param-value>123456</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>DbServlet</servlet-name>
<url-pattern>/db</url-pattern>
</servlet-mapping>
ServletContext
为什么需要 ServletContext
如果我们希望统计某个 web 应用的所有 Servlet 被访问的次数,怎么办?
方案 1-DB

方案 2-ServletContext

ServletContext 基本介绍
ServletContext 是一个接口,它表示 Servlet 上下文对象
一个 web 工程,只有一个 ServletContext 对象实例
ServletContext 对象 是在 web 工程启动的时候创建,在 web 工程停止的时销毁
ServletContext 对象可以通过 ServletConfig.getServletContext 方法获得对 ServletContext 对象的引用,也可以通过 this.getServletContext()来获得其对象的引用。
由于一个 WEB 应用中的所有 Servlet 共享同一个 ServletContext 对象,因此 Servlet 对象 之间可以通过 ServletContext 对象来实现多个 Servlet 间通讯。ServletContext 对象通常也被 称之为域对象。

ServletContext 可以做什么
- 获取 web.xml 中配置的上下文参数 context-param [信息和整个 web 应用相关,而不是属于某个 Servlet]
- 获取当前的工程路径,格式: /工程路径 =》 比如 /servlet
- 获 取 工 程 部 署 后 在 服 务 器 硬 盘 上 的 绝 对 路 径
- 像 Map 一样存取数据, 多个 Servlet 共享数据
应用实例 **1-**获取工程相关信息
● 需求如下:
获取 web.xml 中配置的上下文参数 context-param
获取当前的工程路径,格式: /工程路径
获取工程部署后在服务器硬盘上的绝对路径
package com.ybs;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author 韩顺平
* @version 1.0
*/
public class ServletContext_ extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取web.xml的context-parameter
//1.获取到ServletContext对象
ServletContext servletContext = getServletContext();
//2. 获取website
String website = servletContext.getInitParameter("website");
String company = servletContext.getInitParameter("company");
//3. 获取项目的工程路径
String contextPath = servletContext.getContextPath();
//4. 获取项目发布会,正在的工作路径
// /表示我们的项目(发布后)的 根路径 D:\hspedu_javaweb\servlet\out\artifacts\servlet_war_exploded
String realPath = servletContext.getRealPath("/");
System.out.println("项目路径= " + contextPath);// /servlet
System.out.println("website= " + website);
System.out.println("company= " + company);
System.out.println("项目发布后的绝对路径= " + realPath);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
<!--配置ServletContext_-->
<servlet>
<servlet-name>ServletContext_</servlet-name>
<servlet-class>com.ybs.ServletContext_</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServletContext_</servlet-name>
<url-pattern>/servletContext_</url-pattern>
</servlet-mapping>
<!--配置整个网站的信息-->
<context-param>
<param-name>website</param-name>
<param-value>http://www.hanshunping.net</param-value>
</context-param>
<context-param>
<param-name>company</param-name>
<param-value>韩顺平教育</param-value>
</context-param>
应用实例 **2-**简单的网站访问次数计数器
- 需求: 完成一个简单的网站访问次数计数器:
- 使用 Chrome 访问 Servlet01, 每访问一次,就增加 1 访问次数,在后台输出,并将结 果返回给浏览器显示
- 使用火狐访问 Servlet02,每访问一次,就增加 1 访问次数,在后台输出,并将结果返 回给浏览器显示
package com.ybs;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @author 韩顺平
* @version 1.0
*/
public class OrderServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取到ServletContext对象
ServletContext servletContext = getServletContext();
// System.out.println("OrderServlet servletContext= " +
// servletContext + " 运行类型=" + servletContext.getClass());
// //从servletContext获取 visit_count 属性 k-v
// Object visit_count = servletContext.getAttribute("visit_count");
// //判断visit_count是否为null
// if (visit_count == null) {//说明是第1次访问网站
// servletContext.setAttribute("visit_count", 1);
// visit_count = 1;
// } else { //是第二次或以后
// //取出visit_count属性的值+1
// visit_count = Integer.parseInt(visit_count + "") + 1;
// //放回到servletContext
// servletContext.setAttribute("visit_count", visit_count);
// }
Integer visit_count = WebUtils.visitCount(servletContext);
//输出显示
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.print("<h1>新网站被访问的次数是" + visit_count + "</h1>");
writer.flush();
writer.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
package com.ybs;
import javax.servlet.ServletContext;
/**
* @author 韩顺平
* @version 1.0
*/
public class WebUtils {
//这个方法就是对访问的次数累积,同时返回次数
public static Integer visitCount(ServletContext servletContext) {
//从servletContext获取 visit_count 属性 k-v
Object visit_count = servletContext.getAttribute("visit_count");
//判断visit_count是否为null
if (visit_count == null) {//说明是第1次访问网站
servletContext.setAttribute("visit_count", 1);
visit_count = 1;
} else { //是第二次或以后
//取出visit_count属性的值+1
visit_count = Integer.parseInt(visit_count + "") + 1;
//放回到servletContext
servletContext.setAttribute("visit_count", visit_count);
}
return Integer.parseInt(visit_count + "");
}
}
HttpServletRequest
HttpServletRequest 对象代表客户端的请求
当客户端/浏览器通过 HTTP 协议访问服务器时,HTTP 请求头中的所有信息都封装在这 个对象中
通过这个对象的方法,可以获得客户端这些信息。

HttpServletRequest 常用方法
- getRequestURI() 获取请求的资源路径 /servlet/loginServlet
- getRequestURL() 获 取 请 求 的 统 一 资 源 定 位 符 ( 绝 对 路 径 ) http://localhost:8080/servlet/loginServlet
- getRemoteHost() 获取客户端的 主机, getRemoteAddr()
- getHeader() 获取请求头
- getParameter() 获取请求的参数
- getParameterValues() 获取请求的参数(多个值的时候使用) , 比如 checkbox, 返回的 数组
- getMethod() 获取请求的方式 GET 或 POST
- setAttribute(key, value); 设置域数据
- getAttribute(key); 获取域数据
- getRequestDispatcher() 获取请求转发对象, 请求转发的核心对象
HttpServletRequest 应用实例
需求: 说明: 在一个表单提交数据给 Servlet , 然后在 Servlet 通过 HttpServletRequest 对象获取相关数据
package com.ybs.servletcontext;
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 HttpServletRequestMethods extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse
response) throws ServletException, IOException {
//这里我们使用 request 对象,获取表单提交的各种数据
System.out.println("HttpServletRequestMethods doPost() 被调用..");
/***********************************
* 获取和 http 请求头相关信息
***********************************/
System.out.println("请求的资源路径 URI= " + request.getRequestURI());
//http://主机/uri
System.out.println(" 请 求 的 统 一 资 源 定 位 符 ( 绝 对 路 径 ) URL= " + request.getRequestURL());
System.out.println("请求的客户端 ip 地址= " + request.getRemoteAddr());//本地就是 127.0 .01
//思考题:如发现某个 ip 在 10s 中,访问的次数超过 100 次,就封 ip
//实现思路:1 用一个集合 concurrentHashmap[ip:访问次数] 2[线程/定时扫描]3 做成处理
// 获取 http 请求头的信息,可以指定其他,比如 User-Agent , Host 等待 老师就举一个例子
System.out.println("http 请求头 HOST= " + request.getHeader("Host"));
// 说 明 , 如 果 我 们 希 望 得 到 请 求 的 头 的 相 关 信 息 , 可 以 使 用
request.getHeader("请求头字段");
System.out.println("该请求的发起地址是= " + request.getHeader("Referer"));
// 请获取访问网站的浏览器是什么?
String userAgent = request.getHeader("User-Agent");
System.out.println("User-Agent= " + userAgent);
// 取出 FireFox, 取出最后
String[] s = userAgent.split(" ");
System.out.println("浏览器=" + s[s.length - 1].split("\\/")[0]);
//课堂练习: 要求同学们取出 Windows NT 10.0 和 Win64
// 主要是 Get / Post
System.out.println("http 请求方式~= " + request.getMethod());
/***********************************
* 获取和请求参数相关信息, 注意要求在返回数据前,获取参数
***********************************/
//1. 获取表单的数据[单个数据]
//username=tom&pwd=&hobby=hsp&hobby=spls
String username = request.getParameter("username");
String pwd = request.getParameter("pwd");
//2. 获取表单一组数据
String[] hobbies = request.getParameterValues("hobby");
System.out.println("username= " + username);
System.out.println("pwd= " + pwd);
//增强 for 循环的快捷键 iter->回车即可 , 能使用快捷键,就使用快捷键
for (String hobby : hobbies) {
System.out.println("hobby=" + hobby);
}
//推而广之, 如果是 单选 , 下拉框 等等. => 作业布置
}
protected void doGet(HttpServletRequest request, HttpServletResponse
response) throws ServletException, IOException {
doPost(request, response);
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册</title>
</head>
<body>
<h1>注册用户</h1>
<form action="http://localhost:8080/servlet/httpServletRequestMethods" method="get">
u: <input type="text" name="username"/><br><br>
p: <input type="password" name="pwd"/><br><br>
选择你喜欢的老师:
<input type="checkbox" name="hobby" value="hsp">韩顺平
<input type="checkbox" name="hobby" value="lh">老韩
<input type="checkbox" name="hobby" value="spls">顺平老师<br/><br/>
<input type="submit" value="注册用户"/>
</form>
</body>
</html>
HttpServletRequest 注意事项和细节
1.获 取 doPost 参 数 中 文 乱 码 解 决 方 案 , 注 意 setCharacterEncoding("utf-8") 要 写 在 request.getParameter()前。
2.注意:如果通过 PrintWriter writer, 有返回数据给浏览器,建议将获取参数代码写在writer.print() 之前,否则可能获取不到参数值(doPost)
3.处理 http 响应数据中文乱码问题

- 再次理解 Http 协议响应 Content-Type 的含义, 比如 text/plain application/x-tar
请求转发
为什么需要请求转发
- 目前我们学习的都是一次请求,对应一个 Servlet, 如图

- 但是在实际开发中,往往业务比较复杂,需要在一次请求中,使用到多个 Servlet 完成 一个任务(Servlet 链, 流水作业) 如图:

请求转发说明
- 实现请求转发:请求转发指一个 web 资源收到客户端请求后,通知服务器去调用另外 一个 web 资源进行处理
- HttpServletRequest 对象(也叫 Request 对象)提供了一个 getRequestDispatcher 方法,该 方法返回一个 RequestDispatcher 对象,调用这个对象的 forward 方法可以实现请求转发
- request 对象同时也是一个域对象,开发人员通过 request 对象在实现转发时,把数据 通过 request 对象带给其它 web 资源处理
- setAttribute方法
- getAttribute方法
- removeAttribute方法
- getAttributeNames方法
实现请求转发

请求转发应用实例
如果是 tom,提示为管理员,其它是普通用户

package com.ybs;
import javax.servlet.RequestDispatcher;
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 CheckServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse
response) throws ServletException, IOException {
System.out.println("CheckServlet 被调用..");
//根据用户名来确定该用户是什么身份
String username = request.getParameter("username");
//注意:如果是同一个 request 对象(请求转发),那么可以在不同的 servlet 中,是 getParameter
if ("tom".equals(username)) {
//分配
request.setAttribute("role", "管理员");
} else {
request.setAttribute("role", "普通用户");
}
//获取分发器
//老韩解读 1. /manageServlet 写的是 要转发的 servlet 的 url
// 2. / 会被解析成 /servlet
// 3. forward(request, response) 表示把当前 servlet 的 request 对象和response 对象,传递给下一个 servlet 使用
RequestDispatcher requestDispatcher =
request.getRequestDispatcher("/manageServlet");
requestDispatcher.forward(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
}
}
package com.ybs;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class ManageServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse
response) throws ServletException, IOException {
System.out.println("ManageServlet 被调用..");
String username = request.getParameter("username");
String role = (String) request.getAttribute("role");
//输出信息
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.print("用户名: " + username + "<br/>");
writer.print("角色 : " + role);
writer.flush();
writer.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
}
}
请求转发注意事项和细节
- 浏览器地址不会变化(地址会保留在第 1 个 servlet 的 url)
- 在同一次 HTTP 请求中,进行多次转发,仍然是一次 HTTP 请求
- 在同一次 HTTP 请求中,进行多次转发,多个 Servlet 可以共享 request 域/对象的数据(因 为始终是同一个 request 对象)
- 可以转发到 WEB-INF 目录下(后面做项目使用)
- 不能访问当前 WEB 工程外的资源
- 因为浏览器地址栏会停止在第一个 servlet ,如果你刷新页面,会再次发出请求(并且会 带数据), 所以在支付页面情况下,不要使用请求转发,否则会造成重复支付
HttpServletResponse
- 每次 HTTP 请求,Tomcat 会创建一个 HttpServletResponse 对象传递给 Servlet 程序去 使用。
- HttpServletRequest 表示请求过来的信息,HttpServletResponse 表示所有响应的信息, 如果需要设置返回给客户端的信息,通过 HttpServletResponse 对象来进行设置即可

HttpServletResponse 类图

3 向客户端返回数据方法
- 字节流 getOutputStream(); 常用于下载(处理二进制数据)
- 字符流 getWriter(); 常用于回传字符串
- (细节:)两个流同时只能使用一个。 使用了字节流,就不能再使用字符流,反之亦然, 否则就会报错
向客户端返回数据注意事项和细节
处理中文乱码问题-方案 1
//设置服务器字符集为UTF-8
response.setCharacterEncoding("UTF-8");
// 通过响应头,设置浏览器也使用UTF-8字符集
response.setHeader("content-Type","text/html; charset=UTF-8");
处理中文乱码问题-方案 2
response.setContentType("text/html;charset=utf-8");
请求重定向

请求重定向应用实例
需 求 : 演 示 请 求 重 定 向 的 使 用 当 访 问 DownServlet 下 载 文 件 , 重 定 向 到 DownServletNew 下载文件
package com.ybs;
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 DownServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// System.out.println("DownServlet 被调用");
// response.setContentType("application/x-tar;charset=utf-8");
// PrintWriter writer = response.getWriter();
// writer.print("hi");
// writer.flush();
// writer.close();
//完成了自己业务
//发出请求重定向-> DownServletNew
//听老师解读
//1. sendRedirect 本质就会 返回 302 状态码 Location: /servlet/downServletNew
//2. 因此 302和 /servlet/downServletNew 是浏览器解析,而不是服务器
//3. 浏览器在解析 /servlet/downServletNew => http://localhost:8080/servlet/downServletNew
//4. 动态获取到application context
String contextPath = getServletContext().getContextPath();
System.out.println("contextPath= " + contextPath);
//response.sendRedirect("/servlet/downServletNew");
response.sendRedirect(contextPath + "/downServletNew");
//response.sendRedirect("http://www.baidu.com");
//第二种重定向的写法
// System.out.println("第二种方式重定向...");
// response.setStatus(302); //设置http响应的状态码
// //设置http响应的 Location: /servlet/downServletNew
// response.setHeader("Location", "/servlet/downServletNew");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
}
package com.ybs;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class DownServletNew extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("DownServletNew 被调用");
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.print("ok");
writer.flush();
writer.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
}
请求重定向注意事项和细节
- 最佳应用场景:网站迁移,比如原域名是 www.hsp.com 迁移到 www.hsp.cn ,但是百度抓取的还是原来网址.
- 浏览器地址会发生变化,本质是两次 http 请求. 3. 不能共享 Request 域中的数据,本质是两次 http 请求,会生成两个 HttpServletRequest对象
- 不能重定向到 /WEB-INF 下的资源
- 可以重定向到 Web 工程以外的资源, 比如 到 www.baidu.com 【在前面的案例演示】
- 重定向有两种方式, 推荐使用第 1 种.

6.动态获取到 application context
String contextPath = getServletContext().getContextPath();
System.out.println("contextPath= " + contextPath);
response.sendRedirect(contextPath + "/downServletNew");
课后作业
package com.ybs.servletcontext;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author 韩顺平
* @version 1.0
*/
public class MyPayServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse
response) throws ServletException, IOException {
//System.out.println("MyPayServlet 被调用...");
//得到金额
String money = request.getParameter("money");
System.out.println("money= " + money.getClass());
//转成 int
//处理一下这个异常
int iMoney = WebUtils.parseString(money);
ServletContext servletContext = getServletContext();
if (iMoney > 100) {
//重定向到 payok.html
//servletContext.getContextPath() 就是 /servlet
response.sendRedirect(servletContext.getContextPath() + "/payok.html");
} else {
response.sendRedirect(servletContext.getContextPath() + "/pay.html");
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse
response) throws ServletException, IOException {
doPost(request, response);
}
}
package com.ybs;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;
@WebServlet(name = "MyPayServlet", value = "/myPayServlet")
public class MyPayServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Integer money = Integer.parseInt(request.getParameter("money"));
if ( money > 100 ){
response.sendRedirect("/servlet/payok.html");
}else {
response.sendRedirect("/servlet/pay.html");
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request,response);
}
}
package com.ybs.servletcontext;
public class WebUtils {
public static int parseString(String str) {
int num = 0;
//try-catch : ctrl+alt+t
//如果不知道,老韩在说什么? 回去看到 java 基础 - 异常
try {
num = Integer.parseInt(str);//
} catch (NumberFormatException e) {
//这个异常不会 throw 给 tomcat
System.out.println("输入的 str 格式不正确...");
}
return num;
}
}
WEB 工程路径专题

工程路径解决方案
解决方案:相对路径
- 说明: 使用相对路径来解决, 一个非常重要的规则:页面所有的相对路径,在默认情况下,都会参考当前浏览器地址栏的路径 http://ip:port/工程名/ + 资源来进行跳转。

2.如果需要指定页面相对路径参考的的路径,可以使用 base 标签来指定
**解决方案:**base 标签
base 基本介绍
- base 标签是 HTML 语言中的基准网址标记,它是一个单标签,位于网页头部文件的 head 标签内
- 一个页面最多只能使用一个 base 元素,用来提供一个指定的默认目标,是一种表达路径和连接网址的标记。
- 常见的 url 路径形式分别有相对路径与绝对路径,如果 base 标签指定了目标,浏览器将通过这个目标来解析当前文档中的所有相对路径,包括的标签有(a、img、link、form)
- 也就是说,浏览器解析时会在路径前加上 base 给的目标,而页面中的相对路径也都转换成了绝对路径。使用了 base 标签就应带上 href 属性和 target 属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>b.html</title>
<!--
老韩解读
1. 如果没有<base href="http://localhost:10000/hspedu_servlet/">
2. 当点击 返回a.html 超链接,将会以当前浏览器的地址为路径来确定 路径
3. 如果增加了<base href="http://localhost:10000/hspedu_servlet/">
4. 将以 base 指定的href的地址为路径,来确定 超链接的路径
-->
<!--<base href="http://localhost:10000/hspedu_servlet/">-->
<!--简写形式-->
<!--使用base 标签搞定
老韩解读
1. 下面老师写的 base href="http://localhost:8080/webpath/"
2. 表示的含义就是当前这个页面的访问所有资源都是以 http://localhost:8080/webpath/ 参照
-->
<!--<base href="http://localhost:8080/webpath/">-->
<!--上面的写法可以简化一把-->
<!--
老韩解读
1. base 标签是哪个在解析? => 浏览器
2. 浏览器在解析 第一个 /时候,会解析成 http://localhost:8080/
3. 浏览器 href="/webpath/" =解析=> href="http://localhost:8080/webpath/"
4. 浏览器 <a href="a.html">返回a.html~</a> , 参考 base => 最后
href="http://localhost:8080/webpath/a.html"
-->
<base href="/webpath/">
</head>
<body>
<h1>这是/d1/d2/b.html</h1>
<!--
老韩解读
1. 返回a.html => 使用前面的相对路径http://localhost:8080/webpath/d1/d2/
2. ../../a.html => http://localhost:8080/webpath/a.html
3. 相对路径会让这个项目相互调用的关系变得复杂
4. 使用base 标签搞定
-->
<!--<a href="../../a.html">返回a.html~</a>-->
<a href="a.html">返回a.html~</a>
</body>
</html>
package com.hspedu.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class Servlet03 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//这里我们希望通过转发来定位b.html
//老师解读
//1. 在服务器端 解析第一个 /时,会被解析成 http://ip:port/项目名[application context]
// 老韩再补充: 项目名=> 说成 application context
//2. "/d1/d2/b.html" => 被解析 http://ip:port/项目名/d1/d2/b.html
System.out.println("Servlet03 进行转发~~~ ....");
request.getRequestDispatcher("/d1/d2/b.html").forward(request,response);
//3. 在服务器进行转发时, 没有 / 就按照默认的方式参考定位 http://ip:port/项目名/
// 老师建议,仍然使用上面的
//request.getRequestDispatcher("d1/d2/b.html").forward(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
作业布置
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<h1>用户登录</h1>
<!--
请写出 login.html 在不通过Servlet转发情况下, 如何 通过表单提交, 找到 user.html, 把所有的写法列出来
1. 完整的url http://localhost:8080/webpath/views/user/user.html
2. 使用相对路径 action="views/user/user.html" => 使用当前浏览器的 地址栏 ip:port/工程路径/ + ...
3. action="/webpath/views/user/user.html"
浏览器对第一个 / 进行解析 为 http://localhost:8080/ + webpath/views/user/user.html
同样是一个完整url
4. 这里老师提一句: 我们使用服务器渲染技术,可以动态得到 /工程路径 , 后面再说
-->
<!--<form action="/webpath/hi" method="post">-->
<form action="/webpath/redirect" method="post">
u: <input type="text" name="username"><br/>
<input type="submit" value="登录">
</form>
</body>
</html>
package com.hspedu.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class HiServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//这里请求转发到 /views/user/user.html
//System.out.println("HiServlet 被调用....");
//使用转发
//注意 第一个 / 被服务器解析成 /webpath/
// 因为请求转发是发生服务器端,所以通过 /webpath/views/user/user.html
// 可以定位该资源
request.getRequestDispatcher("/views/user/user.html")
.forward(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户页面</title>
<!--
1. 使用相对路径: <a href="../../login.html">点击返回登录</a>
2. 使用相对路径是比较麻烦,即使成功,还需要考虑是直接定位到这个资源还是请求转发到这个资源
3. 使用base 标签给一个固定的相对路径
4. <base href="/webpath/"> 该标签是浏览器解析 http://localhost:8080/webpath/
5. 在 base 得到的url 路径基础上 <a href="login.html">点击返回登录</a> =>
http://localhost:8080/webpath/login.html
6. 一定要自己 玩一把,再听评讲,才会有融合贯通.
-->
<base href="/webpath/">
</head>
<body>
<h1>用户页面</h1>
<a href="login.html">点击返回登录</a>
</body>
</html>
WEB 工程路径注意事项和细节
- 在实际开发中,路径都使用绝对路径,而不是相对路径
- 在 web 中 / 斜杠 如果被浏览器解析,得到的地址是:http://ip[域名]:port/比如:
<a href="/">斜杠</a>
- 在 web 中 / 斜杠 如果被服务器解析,得到的地址是:http://ip[域名]:port/工程路径/,你也可以理解成 /工程路径/ 下面的几种情况就是如此:
<url-pattern>/servelturl</url-pattern>
servletContext.getRealPath("/"); ==> **是得到执行路径**/**工作路径**
request.getRequestDispatcher("/");
在 javaWeb 中 路径最后带 / 和 不带 / 含义不同, 一定要小心, 比如
<a href="/a/servlet03">网址</a>
: servlet03 表示资源,<a href="/a/servlet03/">网址</a>
: servlet03 表示路径特别说明:重定向 response.sendRediect("/"); 这条语句虽然是在服务器执行的,但是, 服务器是把斜杠 / 发送给浏览器解析。因此得到地址 http://ip[域名]:port/
老韩小结: 在编写资源路径时: , 考虑这么几点
(1) 这个路径 前面有没有 /
(2) 这个路径 在哪里被解析 [服务器还是浏览器] , 如果前面有 / , 并且是在 浏览器被解析的 被解析成 http://ip:port/ , 如果在服务器端被解析 , 被解析成 /工程路径/
(3) 如果这个路径,前面没有 / , 并且在浏览器被解析,则以浏览器当前的地址栏 去掉资源部分,作为一个相对路径.
(4) 这个路径,最后有没有 / , 如果最后有/ 表示路径, 如果没有 / 表示资源
作业
package com.hspedu.servlet;
import javax.servlet.ServletContext;
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 RedirectServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("RedirectServlet 重定向4 被调用....");
//这里如何通过重定向来定位 /views/user/user.html
//1. 分析出 重定向是发生在 浏览器
//2. 写法1: response.sendRedirect("http://localhost:8080/webpath/views/user/user.html");
//3. 写法2: response.sendRedirect("/webpath/views/user/user.html");
//4. 写法3: response.sendRedirect("views/user/user.html");
//5. 写法4: response.sendRedirect(contextPath + "/views/user/user.html");
//6. 推荐写法是 第4个写法,比较灵活,而且定位资源是稳定的.
String contextPath = getServletContext().getContextPath();//contextPath=/webpath
response.sendRedirect(contextPath + "/views/user/user.html");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}