javaweb尚硅谷版

javaweb尚硅谷版

一、 前端

1.Ajax

1.1 whay ajax?

  • AJAX = Asynchronous JavaScript and XML(异步的 JavaScript和XML)。
  • AJAX不是新的编程语言,而是一种使用现有标准的新方法。
  • AJAX最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。
  • AJAX不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行。
  • XMLHttpRequest只是实现Ajax的一种方式。

工作原理

image-20250119162422695

  • ·简单来说,我们之前发的请求通过类似form表单标签,a标签这种方式,现在通过运行js代码动态决定什么时候发送什么样的请求。
  • 通过运行Js代码发送的请求浏览器可以不用跳转页面,我们可以在JS代码中决定是否要跳转页面
  • ·通过运行JS代码发送的请求,接收到返回结果后,我们可以将结果通过dom编程渲染到页面的某些元素上,实现局部更新

1.2 使用说明

html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script>
function getMessage(){
// 实例化一个xmlHttpRequest
var xmlhttp = new XMLHttpRequest();
// 设置xmlHttpRequest的回调函数
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
//alert("后端响应了: " + xmlhttp.responseText); // 在控制台输出响应
//xmlhttp.responseText 后端响应回来的数据
console.log(xmlhttp.responseText);

//将信息放到指定位置
var inputEle= document.getElementById("message");
inputEle.value=xmlhttp.responseText;

}
}
// 设置发送请求的路径和参数
xmlhttp.open("GET", "/hello?username=zhangsan", true);
// 发送请求
xmlhttp.send();
}
</script>
</head>
<body>

<button onclick="getMessage()">在吗</button>
<input type="text" id="message">
</body>
</html>

java

package com.sgg.servlet;

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("/hello")
public class HelloServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 设置字符编码,以处理中文乱码
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");

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

// 返回响应
resp.getWriter().write("hello " + username);
}
}

image-20250119173412134

2.前端工程化

前端工程化是使用软件工程的方法来单独解决前端的开发流程中模块化、组件化、规范化、自动化的问题,其主要目的为了提高效率和降低或本。

image-20250216152137864

3.es6

3.1 es6的变量和模板字符串

  • let和var的差别
  1. let不能重复声明
  2. let有块级作用域,非函数的花括号遇见let会有块级作用域,也就是只能在花括号里面访问。
  3. let不会预解析进行变量提升
  4. let定义的全局变量不会作为window的属性
  5. let在es6中推荐优先使用
<script>
// 1. let不能重复声明
var i=10
// var i=100

let j=10
let j=100
// 2. let有块级作用域,非函数的花括号遇见let会有块级作用域,也就是只能在花括号里面访问。
{
var i=10
let j=10
console.log(j)//正确
}

console.log(i)
// console.log(j)会报错


// 3. let不会预解析进行变量提升
console.log(i)
var i=10//打印undefind

console.log(j)
var j=10//报错

// 4. let定义的全局变量不会作为window的属性
var a =10 //a会变成window对象的属性
let b =10
console.log(window.a)
console.log(window.b)
// 5. let在es6中推荐优先使用

//6.const就是不可修改的变量,相当于final
const let c=10



/* 模板字符串 字符串换行和字符串拼接问题 */
// "" '' 处理字符串不支持多行
//使用 ``拼接
let city ='背景'
let str = `<ul>
<li></li>
<li>${city}</li>
<li></li>
</ul>`;



</script>

3.2 es6的解构表达式

ES6的解构赋值是一种方便的语法,可以快速将数组或对象中的值拆分并赋值给变量。解构赋值的语法使用花括号{}表示对象,方括号[]表示数组。通过解构赋值,函数更方便进行参数接受等!

数组解构赋值

  • 可以通过数组解构将数组中的值赋值给变量
let[a,b,c]=[1,2,3]
console.log(a);
console.log(b);
console.log(c);
  • 该语句将数组[1,2,3]中的第一个值赋值给a变量,第二个值赋值给b变量,第三个值赋值给c变量。可以使用默认值为变量提供备选值,在数组中缺失对应位置的值时使用该默认值。例如:
let [a,b,c,d,e=10]=arr
console.log(a,b,c,d,e)

let person={
name:"zhangsan",
age:10
}

对象解构赋值

  • 使用解构表达式获取对象属性值
let person={
name:"zhangsan",
age:10
}
let {age,name}=person
console.log(age,name)

  • 解构表达式应用在方法的参数列表
let arr =[11,22,33,44,55]
function showArr([a,b,c]){
console.log(a,b,c)

}

showArr(arr)

3.3 es6的箭头函数

基本功能

let fun1 =function(){}//普通函数声明
let fun2=() =>{}//箭头函数声明

let fun3 =(x)=>{return x+1}//普通
let fun4 =x=>{return x+1}//参数列表只有一个参数,()可以省略
let fun5 =(x)=>console.log(x)//如果方法体中只有一行代码 {}可以省略
let fun6 =(x)=> x+1//方法体中,有且只有一样代码,这行代码是return返回结果的代码,那么{}和return都可以省略

this

箭头函数没有自己的this

箭头函数中的this是外层上下文环境中的this

let person ={
name:'张三',
showName:function(){
console.log(this.name)
},
viewName:()=>{
console.log(this.name)
}
}

person.showName()
person.viewName()

案例

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#xdd{
display: inline-block;
width: 200px;
height: 200px;
background-color: red;
}
</style>

</head>
<body>
<div id="xdd"></div>

<script>
/* //不带箭头
var xdd=document.getElementById("xdd")
xdd.onclick=function(){
let _this=this
window.setTimeout(function(){
_this.style.backgroundColor="yellow"
},2000)
}
*/

var xdd=document.getElementById("xdd")
xdd.onclick=function(){

window.setTimeout(()=>{
this.style.backgroundColor="yellow"
},2000)
}
</script>
</body>
</html>

3.4 rest和spread

rest参数,在形参上使用和JAVA中的可变参数几乎一样

spread参数,在实参上使用rest

<script>
//rest 解决剩余参数接收问题
let fun1=(a,b,c,...arr)=>{
console.log(a,b,c)
console.log(arr)
}
fun1(1,2,3,4,5,6,7,8,9)

//spread 是rest在实参上的使用

let arr=[1,2,3]
let fun2 =(a,b,c)=>{
console.log(a,b,c)
}
fun1(...arr)
//快速合并数组
let a =[1,2,3]
let b =[1,2,3]
let c =[1,2,3]
let d=[...a,...b,...c]

//快速合并对象
let person1={name:"李阳"}
let person2={age:"10"}
let person3={gender:"boy"}
let person4={...person1,...person2,...person3}
</script>

3.5 es6的对象创建和拷贝

对象创建
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
class Person {
#n;//私有属性
age;
get name() {
return this.#n;
}
set name(n) {
this.#n = n;
}
//实例方法
eat(food){
console.log(`${this.age}岁的${this.#n}正在吃${food}`)

}
//静态方法
static sum(a,b){
return a+b
}

//构造器
constructor(name,age){
this.#n=name;
this.age=age;
}
}



// let person = new Person("小米",8); // Use "Person" with uppercase P
// // person.name = "李阳";
// // person.age=23
// console.log(person.name);
// person.eat("火锅")
// console.log(Person.sum(10,20))

//访问私有属性
/* let person = new Person("小米",8);
console.log(person.name) */



</script>
</head>
<body>

</body>
</html>

3.6 es6模块化处理

模块化是一种组织和管理前端代码的方式,将代码拆分成小的模块单元,使得代码更易于维护、扩展和复用。它包括了定义、导出、导入以及管理模块的方法和规范。前端模块化的主要优势如下:

  1. 提高代码可维护性:通过将代码拆分为小的模块单元,使得代码结构更为清晰,可读性更高,便于开发者阅读和维护。
  2. 提高代码可复用性:通过将重复使用的代码变成可复用的模块,减少代码重复率,降低开发成本。
  3. 提高代码可扩展性:通过模块化来实现代码的松耦合,便于更改和替换模块,从而方便地扩展功能。
  • ES6模块化的几种暴露和导入方式
    1. 分别导出
    2. 统一导出
    3. 默认导出
  • ES6中无论以何种方式导出,导出的都是一个对象,导出的内容都可以理解为是向这个对象中添和属性或者方法
分别导出

需要添加export关键字

model.js

//变量
export const PI =3.14//export暴露
const PI2=3.1415926

function sum(a,b){
return a+b
}

//类
class Person{
constructor(name,age){
this.name=name;
this.age=age;
}
sayHello(){
console.log(`hello,my name is ${this.name} ,I am ${this.age} years old`)
}
}

app.js

//导入module.js文件
/*
*代表model.js的所有成员
无论何种方式导入,导入的内容都会被当成一个对象处理
*/

import * as m1 from './module.js'
console.log(m1.PI)
console.log(m1.PI2)

index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- 引用app.js -->
<script src="./app.js" type="module"></script>
</head>
<body>

</body>
</html>

4.VUE

4.1 vite创建前端工程化页面

  • vs的控制台中输入 npm create vite即可
  • cd 到我们创建的目录
  • 运行npm i
  • 运行npm run dev

image-20250217182815143

  • public目录:用于存放一些公共资源,如HTML文件、图像、字体等,这些资源会被直接复制到构建出的目标目录中。
  • src/目录:存放项目的源代码,包括JavaScript、CSs、Vue组件、图像和字体等资源。在开发过程中,这些文件会被Vite 实时编译和处理,并在浏览器中进行实时预览和调试。以下是src内部划分建议:
    1. assets/目录:用于存放一些项目中用到的静态资源,如图片、字体、样式文件等。
    2. components/目录:用于存放组件相关的文件。组件是代码复用的一种方式,用于抽象出一个可复用的U部件,方便在不同的场景中 进行重复使用。
    3. layouts/目录:用于存放布局组件的文件。布局组件通常负责整个应用程序的整体布局,如头部、底部、导航菜单等。
    4. pages/目录:用于存放页面级别的组件文件,通常是路由对应的组件文件。在这个目录下,可以创建对应的文件夹,用于存储不同的 页面组件。
    5. plugins/目录:用于存放vte 插件相关的文件,可以按需加载不同的插件来实现不同的功能,如自动化测试、代码压缩等。
    6. routerl目录:用于存放Wwejis的路由配置文件,负责管理视图和URL之间的映射关系,方便实现页面之间的跳转和数据传递。
    7. store/目录:用于存放Vuex状态管理相关的文件,负责管理应用程序中的数据和状态,方便统一管理和共享数据,提高开发效率。
    8. utils/目录:用于存放一些通用的工具函数,如日期处理函数、字符串操作函数等。
  • vite.config js文件: Vite的配置文件,可以通过该文件配置项目的参数、插件、打包优化等。该文件可以使用CommonS或ES6模块的语法进行配置。
  • package.json文件:标准的 Node.is项目配置文件,包含了项目的基本信息和依赖关系。其中可以通过scripts字段定义几个命令,如dev.build、serve等,用于启动开发、构建和启动本地服务器等操作。
  • Vie项点的入口为src/main,js文件,这是’Vue,js 应用程序的启动文件,也是整个前端应用程序的入口文件。在该文件中,通常会引入Mue.js及其相关插件和组件,同时会创建Vue实例,挂载到HTML页面上指定的DOM元素中。

4.2关于css样式引用的导入方式

<script setup>
/*css样式引用的方式
1.在.vue文件中的style标签中
2.将css样式保存在独立的css文件中,哪个vue文件需要,就在那里导入
在script中导入
import'./style/test.css'
在style中导入
@import'./style/test.css'
3.如果某个样式要在所有vue中显示
可以在main中导入
import './style/test.css'
*/

import'./style/test.css'
</script>


<template>
<div>
<span class="s1">你好</span>
</div>


</template>

<style scoped>
@import'./style/test.css'
</style>

4.3 vite+vue3响应式入门和setup函数

<script setup>
import {ref} from 'vue'
//定义一些要展示到页面上的数据 变量/对象
let counter =ref(2)
//自增的方法
function counterIncr(){
counter.value++
}

//自减的方法
function counterDecr(){
counter.value--
}

/*
响应式数据:在数据变化时,vue会将变量最新的值更新到dom树中,页面上的数据就是最新的
非响应式数据:在数据变化时,vue不会更新
vue3中要经过ref/reactive函数处理才变成最新的
*/

</script>

<template>
<div>
<button @click="counterIncr()">+</button>
<span v-text="counter"></span>
<button @click="counterDecr()">-</button>
</div>

</template>

<style scoped>

</style>

5.vue的视图渲染技术

5.1插值表达式

<script setup>

/*
插值表达式
{{数据名字/函数}}
*/
//定义常见类型的数据
let msg ="hello liyang"
let getMsg=()=>{
return "hello vue3"
}
let age=11
let bee="蜜 蜂"
let carts=[{name:"可乐",price:3,number:5},{name:"雪碧",price:4,number:5},{name:"辣条",price:0.5,number:5}]
//定义一个获取购物车总金额的方法
function computer(){
let count=0
for(let index in carts){
count+=carts[index].price* carts[index].number

}
return count
}
</script>

<template>
<div>
<!-- 将数据绑定到下面的元素 -->
<h1>{{ msg }}</h1>
msg的值为{{ msg }}
msg的方法值为{{ getMsg() }}<br>
<!-- 插值表达式支持一些常见的运算符 -->
年龄:{{ age }},是否成年{{ age>18?'是':'否' }}<br>

<!-- 插值表达式中支持对象调用api -->
{{ bee.split(' ').reverse().join('') }} <br>
{{ computer() }}
</div>
</template>

<style scoped>

</style>

5.2 v-text与v-html

<script setup>
/*
v-text 不识别带有html的代码
v-html 可以识别

{{}}插值表达式
v-*** vue的指令

注意:
命令必须依赖标签,在开始标签中使用
*/
let msg="helloworld"
let msg2="liyang"
let msg3=`${msg} ${msg2}`
// 支持常见的运算符
let age =19

//支持API的调用
//支持函数调用


let fontMsg="<font color='red'>你好</font>"
</script>

<template>
<div>
<h1>{{ msg }}</h1><br>
<h1 v-text="msg3"></h1>
<h1 v-text="`你好 ${msg2}`"></h1>
<h1 v-text="age>18?'成年':'未成年'"></h1>

<h1 v-html="fontMsg"></h1>
</div>

</template>

<style scoped>

</style>

5.3 Atteibute属性渲染 v-band

<script setup>
/*
属性渲染命令
v-bind 将数据绑定在元素属性上
*/
//let imgURL="https://img.lwxpz.me/file/1739857121688_屏幕截图 2024-12-17 112612.png"
const data={
logo:"https://img.lwxpz.me/file/1739857121688_屏幕截图 2024-12-17 112612.png",
name:"liyang",
url:"http://ly.baskly.us.kg"
}
</script>

<template>
<div>
<!-- <img v-bind:src="imgURL"> -->
<a v-bind:href="data.url">
<img v-bind:src="data.logo" v-bind:title="data.name">
</a>
</div>
</template>

<style scoped>

</style>

5.4 v-on 事件绑定

<script setup>
import { ref } from 'vue';

/*
v-on:事件名称="函数名()"
可以简写为@事件名称
*/

function fun1() {
alert("hi");
}

let counter = ref(1);

function fun3(event) {
if (confirm("确定要访问目标链接吗")) {
// 通过返回值控制是否阻止默认行为
event.preventDefault();
}
}

function fun4() {
alert("被点击");
}
</script>

<template>
<div>
<!-- 事件的绑定函数 -->
<button @click="fun1()">hello</button>
<!-- 内联事件处理器 -->
<button @click="counter++">+</button>
<!-- 事件的修饰符 .once 事件只绑定一次 prevent修饰符阻止组件默认行为-->
<button @click.once="counter++">Click Once</button>
<a href="http://ly.baskly.us.kg" @click="fun3($event)">yang</a>
<a href="http://ly.baskly.us.kg" @click.prevent="fun4()">yu</a>
{{ counter }}
</div>
</template>

<style scoped>
</style>

5.5 响应式数据处理的方式

让一个普通数据转换为响应式数据的两种方式 1 ref函数 更适合单个变量 在script标签中操作ref响应式数据需要.value 在template标签中不需要.values 2 reactive函数 更适合对象

<script setup>
import {ref,reactive} from 'vue'
/*
让一个普通数据转换为响应式数据的两种方式
1 ref函数 更适合单个变量
在script标签中操作ref响应式数据需要.value
在template标签中不需要.values
2 reactive函数 更适合对象

*/
let counter =ref(10)

let person=reactive({
name:"李阳",
age:8
})
function incr(){
counter.value++
}

function incrAge(){
person.age++
}
</script>

<template>
<div>
<button @click="incr()">+</button>
<button @click="counter++">+</button>
{{ counter }}
<hr>
<button @click="incrAge()">+</button>
{{ person.age }}
</div>
</template>

<style scoped>

</style>

5.6 条件和列表渲染 v-if/v-show和v-for

v-if/v-show
  • . v-if是“真实的“按条件渲染,因为它确保了在切换时,条件区块内的事件监听器和子组件都会被销毁与重建。
  • . v-if 也是惰性的:如果在初次渲染时条件值为false,则不会做任何事。条件区块只有当条件首次变为true时才被渲染。·相比之下,v-show 简单许多,元素无论初始条件如何,始终会被渲染,只有CSS display属性会被切换。
  • ·总的来说, v-if有更高的切换开销,而”v-show有更高的初始渲染开销。因此,如果需要频繁切换,则使用v-show较好;如果在运行时绑定条件很少改变,则v-if 会更合适。
<script setup>
import{ref} from 'vue'
/*
v-if="表达式/数据" 数据为true 则当前元素会渲染进入dom树
v-else 自动和前一个v-if取反
v-show ="" 数据为true则展示到页面上 否则不展示


v-if 如果数据为false则元素不再dom树中了
v-show
*/

let flag=ref(true)
</script>

<template>
<div>
<h1 v-if="flag">vue is awesome!</h1>
<h1 id="bb" v-else>ononon</h1>
<h1 v-show="flag">hhh</h1>
<button @click="flag=!flag">toggle</button>
</div>
</template>

<style scoped>

</style>

v-for
<script setup>
import {ref, reactive } from "vue";

let pro =ref("产品")

let items = reactive([
{ id: "item1", message: "薯片" },
{ id: "item2", message: "面包" },
{ id: "item3", message: "烤鸡" }
]);
</script>

<template>
<div>
<ul>
<li v-for="item in items" :key="item.id">
{{ pro }} {{ item.message }}
</li>
</ul>
</div>
</template>

<style scoped>
</style>

小德莫demo
<script setup>
import { ref, reactive } from "vue";

let pro = ref("产品");

let items = reactive([
{ id: "item1", message: "薯片" },
{ id: "item2", message: "面包" },
{ id: "item3", message: "烤鸡" }
]);

let carts = reactive([
{ name: "可乐", price: 3, number: 10 },
{ name: "雪碧", price: 3, number: 1 },
{ name: "薯片", price: 5, number: 20 },
{ name: "辣条", price: 2, number: 5 }
]);

function removeItem(index) {
const confirmDelete = confirm("确定要删除该商品吗?");
if (confirmDelete) {
carts.splice(index, 1); // 删除指定索引的商品
}
}

//计算总金额的函数
function compute(){
let total=0
for(let index in carts){
total+= carts[index].price*carts[index].number
}
return total
}
</script>

<template>
<div>
<h1>您的购物车如下</h1>
<table border="1px" class="cart-table">
<thead>
<tr>
<th>序号</th>
<th>名称</th>
<th>价格</th>
<th>数量</th>
<th>小计</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(cart, index) in carts" :key="index">
<td>{{ index + 1 }}</td>
<td>{{ cart.name }}</td>
<td>{{ cart.price.toFixed(2) }}</td> <!-- 格式化价格 -->
<td>{{ cart.number }}</td>
<td>{{ (cart.price * cart.number).toFixed(2) }}</td> <!-- 格式化小计 -->
<td>
<button @click="removeItem(index)">删除</button>
</td>
</tr>
</tbody>
</table>
购物车总金额:{{ compute() }}元
<hr>

<ul>
<li v-for="item in items" :key="item.id">
{{ pro }} {{ item.message }}
</li>
</ul>
</div>
</template>

<style scoped>
.cart-table {
width: 100%;
border-collapse: collapse;
}

.cart-table th, .cart-table td {
padding: 8px;
text-align: center;
}

button {
background-color: red;
color: white;
border: none;
padding: 5px 10px;
cursor: pointer;
}

button:hover {
background-color: darkred;
}

hr {
margin: 20px 0;
}
</style>

5.7 双向绑定

单项绑定
<script setup>
import {ref} from 'vue'
/*
单项绑定 响应式数据发生变化时,更新dom树 用户的操作如果造成页面内容的改变不会影响响应式数据

*/
let message =ref("zhangsan")

</script>

<template>
<div>
<input type="text" v-bind:value="message"><br>
{{ message }}

</div>
</template>

<style scoped>

</style>

双向绑定
<script setup>
import { ref, reactive } from 'vue'
/*
单项绑定 v-bind 响应式数据发生变化时,更新dom树 用户的操作如果造成页面内容的改变不会影响响应式数据
双向绑定 v-model 页面数据由于用户的操作造成改变,也会同步修改响应式数据
双向绑定一边都用于表单标签
v-model:value="数据"
*/
let user = reactive({
username: "",
userPwd: "",
intro:"",
pro:""
})
let hbs=ref([])

function cleanForm(){
user.username=""
user.userPwd=""
user.intro=""
user.pro=""
hbs.value.splice(0,hbs.value.length)
}
</script>

<template>
<div>
<input type="text" v-model="user.username"><br>
<input type="password" v-model="user.userPwd"><br>
爱好:
唱<input type="checkbox" v-model="hbs" value="sing">
跳<input type="checkbox" v-model="hbs" value="dance">
rap<input type="checkbox" v-model="hbs" value="rap">
篮球<input type="checkbox" v-model="hbs" value="basketball">
简介:<textarea v-model="user.intro"></textarea>
<br>
籍贯:
<select v-model="user.pro">
<option value="1">北京</option>
<option value="2">河南</option>
<option value="3">湖北</option>
</select>
<button @click="cleanForm">清空</button>

<p>{{ user }}</p>
{{ hbs }}
</div>
</template>

<style scoped>

</style>

5.8 属性计算

<script setup>
import { reactive,computed } from 'vue';
/*
计算属性:其能不间断的执行这个箭头函数 让其数据保持最新,而原身的做不到
*/
const author=reactive({
name:"李阳",
books:["java从入门到精通"]
})

//通过方法返回数据 每使用一次,执行一次
function hasBook(){
return author.books.length>0?"是":"否"
}
//通过计算属性获取数据 每次使用如果和上次使用的数据未发生变化,则使用上次数据
let bookMessage = computed(()=>{
return author.books.length>0?"是":"否"
})
</script>

<template>
<div>
<p>作者:{{ author.name }}</p>
是否出版过图书:{{ hasBook() }}<br>
是否出版过图书:{{ bookMessage }}
</div>
</template>

<style scoped>

</style>

5.9 数据监听

双向绑定版

<script setup>
import { ref } from 'vue'

let funame = ref('')
let finame = ref('')
</script>

<template>
<div>
姓氏: <input type="text" v-model="funame"><br>
名字: <input type="text" v-model="finame"><br>
全名: {{ funame }} {{ finame }}
</div>
</template>

<style scoped>

</style>

监听版

<script setup>
import { ref, reactive, watch,watchEffect } from 'vue'

let funame = ref('')
let finame = ref('')
let lastname = reactive({
name: ""
})


//任何的响应式数据 ,如果是想监听,直接监听即可,无需将要监听的响应式数据作为参数
//
watchEffect(()=>{
funame.value=finame.value+lastname.value
})

// watch 函数监听 一个ref响应式数据
// 当 finame 发生变化时执行后面的函数
watch(finame, (newValue, oldValue) => {
console.log(`${oldValue} 变为 ${newValue}`)
// 更新 funame 为姓氏 + 名字
funame.value = newValue + lastname.name
})

// watch 函数监听一个reactive响应式数据 专门监听reactive响应式数据中的一个属性
watch(() => lastname.name, (newValue, oldValue) => {
console.log(`${oldValue} 变为 ${newValue}`)
funame.value=finame.value+newValue
})

//watch 函数监听一个reactive响应式数据 专门监听reactive响应式数据中的所有属性
watch(() => lastname, (newValue, oldValue) => {

//此时new和old都一样,都是last
funame.value=finame.value+lastname.name
},{deep:true,immediate:true})//是否要深度监听(监听所有属性),是否要先监听一次


</script>

<template>
<div>
姓氏: <input type="text" v-model="funame"><br>
名字: <input type="text" v-model="finame"><br>
<br>
<input type="text" v-model="lastname.name" placeholder="请输入姓氏"><br>
全名: {{ funame }}
</div>
</template>

<style scoped>

</style>

5.10 组件化

白雪警告

Header.vue

<script setup>

</script>

<template>
<div>
<h5>欢迎:xxx的光临<a href="#">退出登录</a> </h5>
</div>

</template>

<style scoped>

</style>

Navigator.vue

<script setup>
//向父组件发送参数
import {defineEmits} from 'vue'
//定义一个向父组件提交数据的事件 事件名称自定义

const emits= defineEmits(["sendMenu"])

//提交数据的方法
function send(data) {
emits("sendMenu",data)
}
</script>

<template>
<div>
<ul>
<li @click="send('学员管理')">学员管理</li>
<li @click="send('图书管理')">图书管理</li>
<li @click="send('请假管理')">请假管理</li>
<li @click="send('班级管理')">班级管理</li>
<li @click="send('教师管理')">教师管理</li>
<li @click="send('考试管理')">考试管理</li>
</ul>
</div>

</template>

<style scoped>

</style>

Content.vue

<script setup>
//接收父组件的参数
import {defineProps} from 'vue'

defineProps({
message:String
})
</script>

<template>
<div>
这里是展示区
{{ message }}
</div>

</template>

<style scoped>

</style>

App.vue

<script setup>
import {ref} from 'vue'
//引用3个组件
import Header from './components/Header.vue'
import Navigator from './components/Navigator.vue';
import Content from './components/Content.vue';
let menu =ref("")
function receiver(data){
menu.value=data
}

</script>

<template>
<div>

<Header class="header"></Header>
<Navigator @sendMenu="receiver" class="navigator"></Navigator>
<Content class="content" :message="menu"></Content>
</div>

</template>

<style scoped>
.header{
height: 80px;
border: 1px solid red;
}

.navigator{
width:20% ;
height: 500px;
border: 1px solid green;
float: left;
}

.content{
width: 79%;
height: 500px;
border: 1px solid blue;
float:right;
}
</style>

6.Vue3的路由机制router

6.1简介

什么是路由

  • 定义:路由就是根据不同的URL地址展示不同的内容或页面。
  • 通俗理解:路由就像是一个地图,我们要去不同的地方,需要通过不同的路线进行导航。

路由的作用

  • 单页应用程序(SPA)中,路由可以实现不同视图之间的无刷新切换,提升用户体验;
  • 路由还可以实现页面的认证和权限控制,保护用户的隐私和安全;
  • 路由还可以利用浏览器的前进与后退,帮助用户更好地回到之前访问过的页面。

6.2 路由的使用

npm install vue-router

6.3 路由案例

src/components/Add.vue

<script setup>


</script>

<template>
<div>
<h1>add </h1>
</div>

</template>

<style scoped>

</style>

src/components/Home.vue

<script setup>


</script>

<template>
<div>
<h1>home</h1>
</div>

</template>

<style scoped>

</style>

src/components/List.vue

<script setup>


</script>

<template>
<div>
<h1>list </h1>
</div>

</template>

<style scoped>

</style>

src/components/Update.vue

<script setup>


</script>

<template>
<div>
<h1>Update </h1>
</div>

</template>

<style scoped>

</style>

src/routers/router.js

// 导入创建对象需要的函数

import{createRouter,createWebHashHistory} from 'vue-router'

//导入.vue组件
import Home from '../components/Home.vue'
import List from '../components/List.vue'
import Update from '../components/Update.vue'
import Add from '../components/Add.vue'

//创建路由对象
const router=createRouter({
//用于记录路由的历史
history:createWebHashHistory(),

//用于定义多个不同的路径
routes:[
{
path:"/home",
component:Home
},
{
path:"/List",
component:List
},
{
path:"/Update",
component:Update
},
{
path:"/add",
component:Add
},
{
path:"/",
component:Home
}
],
})

//向外暴露router
export default router

App.vue

<script setup>


</script>

<template>
<div>
hello 李阳
<br>
<hr>
<router-link to="/home">home</router-link><br>
<router-link to="/list">list</router-link><br>
<router-link to="/add">add</router-link><br>
<router-link to="/update">update</router-link><br>

<!-- 该标签会被替换成具体的.vue -->
<router-view></router-view>
<hr>
goodby 李阳
</div>

</template>

<style scoped>

</style>

main.js

import { createApp } from 'vue'
import App from './App.vue'

//在整个app.vue中可以使用路由
import router from './routers/router'

const app=createApp(App)
app.use(router)
app.mount('#app')

6.4 路由重定向

在router.js中添加

{
path:"/showAll",
redirect:"/list"
},

表示我们请求showAll还会重定向到list

6.5 编程式路由

普通路由

  • <router-link to="/home">home</router-link><br>这种路由,to中的内容目前是固定的,点击后只能切换/list对象

编程式路由

  • 通过useRouter,动态决定向那个组件切换的路由
  • 在Vue 3和vue Router 4中,你可以使用useRouter来实现动态路由(编程式路由)
  • 这里的useRouter方法返回的是一个router对象,你可以用它来做如导航到新页面、返回上一页面等操作。

案例

  • App.vue
<script setup>
import{useRouter} from 'vue-router'
import{ref} from 'vue'
const router =useRouter()
let mypath=ref("")
function showList(){
//编程式路由实现页面跳转
router.push("/list")

//传对象
router.push({path:"list"})
}

function goMyPage(){
router.push(mypath.value)
}
</script>

<template>
<div>
hello 李阳
<br>
<hr>
<!-- 声明式路由 -->
<router-link to="/home">home</router-link><br>
<router-link to="/list">list</router-link><br>
<router-link to="/add">add</router-link><br>
<router-link to="/update">update</router-link><br>

<!-- 编程式路由 -->
<button @click="goMyPage()">go</button><input type="text" v-model="mypath">

<!-- 该标签会被替换成具体的.vue -->
<hr>
<router-view></router-view>
<hr>
goodby 李阳
</div>

</template>

<style scoped>

</style>

  • routers/router.js
// 导入创建对象需要的函数

import{createRouter,createWebHashHistory} from 'vue-router'

//导入.vue组件
import Home from '../components/Home.vue'
import List from '../components/List.vue'
import Update from '../components/Update.vue'
import Add from '../components/Add.vue'

//创建路由对象
const router=createRouter({
//用于记录路由的历史
history:createWebHashHistory(),

//用于定义多个不同的路径
routes:[
{
path:"/home",
component:Home
},
{
path:"/List",
component:List

},
{
path:"/Update",
component:Update
},
{
path:"/add",
component:Add
},
{
path:"/",
component:Home
},
{
path:"/showAll",
redirect:"/list"
},
],
})

//向外暴露router
export default router

6.6 路由传参

路径参数–params

  • 在路径中使用一个动态字段来实现,我们称之为路径参数
    • 例如:查看数据详情/showDetail/1 ,1就是要查看详情的id,可以动态添值!

键值对参数-query

  • 类似与get请求通过url传参,数据是键值对形式的
    • 例如:查看数据详情/showDetail?hid=1 , hid=1就是要传递的键值对参数
    • 在Vue 3和Vue Router 4中,你可以使用useRoute这个函数从vue的组合式API中获取路由对象。
    • useRoute方法返回的是当前的route对象,你可以用它来获取关于当前路由的信息,如当前的路径、查询参数等。

案例需求:切换到ShowDetail.vue组件时,向该组件通过路由传递参数

6.6.1 路径传参数

/components/ShowDetail.vue

<script setup>
// 接收传递过来的路径参数
import { useRoute } from 'vue-router';
import { ref ,onUpdated} from 'vue';

let languageId =ref(0)
let languageName=ref("")

let route=useRoute()
languageId.value=route.params.id
languageName.value=route.params.language

onUpdated(()=>{
languageId.value=route.params.id
languageName.value=route.params.language

})

</script>

<template>
<div>
<h1>ShowDetail接收路径参数 </h1>
<h3>{{ languageId }}{{ languageName }}</h3>
</div>

</template>

<style scoped>

</style>

routers/router.js

// 导入创建对象需要的函数

import{createRouter,createWebHashHistory} from 'vue-router'

//导入.vue组件
import Home from '../components/Home.vue'
import List from '../components/List.vue'
import Update from '../components/Update.vue'
import Add from '../components/Add.vue'
import ShowDetail from '../components/ShowDetail.vue'

//创建路由对象
const router=createRouter({
//用于记录路由的历史
history:createWebHashHistory(),

//用于定义多个不同的路径
routes:[
{
path:"/showDetail/:id/:language",
component:ShowDetail
},
{
path:"/home",
component:Home
},
{
path:"/List",
component:List

},
{
path:"/Update",
component:Update
},
{
path:"/add",
component:Add
},
{
path:"/",
component:Home
},
{
path:"/showAll",
redirect:"/list"
},
],
})

//向外暴露router
export default router

App.vue

<script setup>
import{useRouter} from 'vue-router'
import{ref} from 'vue'
import ShowDetail from './components/ShowDetail.vue';
const router=useRouter()

function showDetail(id,language){
//方式一
// router.push(`/showDetail/${id}/${language}`)

//方式二
router.push({path:`/showDetail/${id}/${language}`})
}

</script>

<template>
<div>
<router-link to="/showDetail/1/java">声明式路由路径传参数</router-link>
<button @click="showDetail(2,'PHP')"> 编程式路由路径传参</button>

<hr>

<router-view></router-view>
</div>

</template>

<style scoped>

</style>

6.6.2 键值对传参

草拟嘛了个逼 前端这么多东西怎么学啊 我草泥马

components/ShowDwtail2.vue

<script setup>
// 接收传递过来的路径参数
//query 键值对cans
import { useRoute } from 'vue-router';
import { ref ,onUpdated} from 'vue';

let languageId =ref(0)
let languageName=ref("")

let route=useRoute()
languageId.value=route.query.id
languageName.value=route.query.language

onUpdated(()=>{
languageId.value=route.query.id
languageName.value=route.query.language

})

</script>

<template>
<div>
<h1>ShowDetail键值对参数 </h1>
<h3>{{ languageId }}{{ languageName }}</h3>
</div>

</template>

<style scoped>

</style>

router.js

import ShowDetail2 from '../components/ShowDetail2.vue'
//创建路由对象
const router=createRouter({
//用于记录路由的历史
history:createWebHashHistory(),

//用于定义多个不同的路径
routes:[
{
path:"/showDetail2",
component:ShowDetail2
}

})
//向外暴露router
export default router

App.vue

<script setup>
import{useRouter} from 'vue-router'
import{ref} from 'vue'
import ShowDetail from './components/ShowDetail.vue';
const router=useRouter()

function showDetail(id,language){
//方式一
// router.push(`/showDetail/${id}/${language}`)

//方式二
router.push({path:`/showDetail/${id}/${language}`})
}

function showDetail2(id,language){
//方式一
router.push(`/showDetail2/?id=${id}&language=${language}`)
}

</script>

<template>
<div>
<router-link to="/showDetail/1/java">声明式路由路径传参数</router-link>
<button @click="showDetail(2,'PHP')"> 编程式路由路径传参</button>

<hr>

<router-link to="/showDetail2/?id=1&language=java">声明式路由键值对</router-link>
<button @click="showDetail2(3,'C')"> 编程式路由键值对传参</button>

<hr>
<router-view></router-view>
</div>

</template>

<style scoped>

</style>

6.7 路由守卫

在Vue 3中,路由守卫是用于在路由切换期间进行一些特定任务的回调函数。路由守卫可以用于许多任务,例如验证用户是否已登录、在路由切换前提供确认提示、请求数据等。ue 3为路由守卫提供了全面的支持,并提供了以下几种类型的路由守卫

  1. 全局前置守卫:在路由切换前被调用,可以用于验证用户是否已登录、中断导航、请求数据等。
  2. 全局后置守卫:在路由切换之后被调用,可以用于处理数据、操作 DOM、记录日志等。
  3. 守卫代码放在router.js里面
//设置全局前置守卫

//每次路由切换页面前,都会执行这个beforeEach中的回调函数
router.beforeEach(
(to,from,next)=>{
/*
from 上一个页面从哪里来
to下一个页面到哪里去
next放行的方法只有执行了该方法才会放行路由
next() 放行
next("/路径") 路由的重定向

*/

}
)

//设置全局后置守卫
//每次页面切换路由后都会执行afterEach中的函数
router.afterEach(
(to,from)=>{

}
)



//向外暴露router
export default router

6.8 路由练习

登录案例 登录以后才可以进入home ,否则必须进入login

components/Home.vue

<script setup>
import{ref} from 'vue'
import { useRouter } from 'vue-router';
let username=window.sessionStorage.getItem('username')
const router=useRouter()
function logout(){
//清除用户登录的信息
window.sessionStorage.removeItem("username")
//跳转到login
router.push("/login")

}


</script>

<template>
<div>
<h1>home页面</h1>
<h3> 欢迎{{ username }}登录</h3>
<button @click="logout()">退出登录</button>
</div>

</template>

<style scoped>

</style>

components/Login.vue

<script setup>
import {ref}from 'vue'
import { useRouter } from 'vue-router'
let username =ref('')
let password=ref('')
let router=useRouter()

function login(){
//获取用户名密码
//root 123456
//登陆成功自动跳转到/home,失败不跳转,给出提示

if(username.value=='root' && password.value=='123456'){


//将用户名保存在浏览器中
window.sessionStorage.setItem("username",username.value)

//路由跳转 /home
router.push("/home")

}else{
alert("用户名密码有误")
}

}

</script>

<template>
<div>
账号:<input type="text" v-model="username"><br>
密码: <input type="password" v-model="password"><br>
<button @click="login()">登录</button>
</div>

</template>

<style scoped>

</style>

routers/router,js

import {createRouter,createWebHashHistory} from 'vue-router'
import Home from '../components/Home.vue'
import Login from '../components/Login.vue'


const router =createRouter({
history:createWebHashHistory(),
routes:[
{
path:"/home",
component:Home
},
{
path:"/",
component:Home
},
{
path:"/login",
component:Login
}
]
})

//通过路由的前置守卫校验登录
router.beforeEach((to,from,next)=>{
if(to.path=='/login'){
//判断如果是去登录页面,直接放行即可
next()
}else{
//如果是其他任何资源,都要在登录之后才会放行,如果没有登录,则重定向到登录页面
const username=sessionStorage.getItem("username")
if(null!=username){
next()
}else{
next("/login")
}

}
})

export default router

App.vue

<script setup>

</script>

<template>
<div>
<router-view></router-view>
</div>

</template>

<style scoped>

</style>

main.js

import { createApp } from 'vue'
import router from './routers/router'
import App from './App.vue'

const app= createApp(App)
app.use(router)
app.mount('#app')

7.axios

7.1 promise

7.1.0 普通函数和回调函数

普通函数:正常调用的函数,一般函数执行完毕后才会继续执行下一行代码

回调函数:一些特殊的函数,表示未来才会执行的一些功能,后续代码不会等待该函数执行完毕就开始执行了

回调函数是一种基于事件的,自动调用函数 回调函数其他的代码不会等待回调函数执行完毕

简介

前端中的异步编程技术,类似Java中的多线程+线程结果回调!

  • Promise是异步编程的一种解决方案,比传统的解决方案–回调函数和事件–更合理和更强大。它由社区最早提出和实现,ES6将其写进了语言标准,统一了用法,原生提供了Promise对象。
  • 所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise是一个对象,从它可以获取异步操作的消息。Promise提供统一的API,各种异步操作都可以用同样的方法进行处理。

promise 特点

(1〉Promise对象代表一个异步操作,有三种状态,‘Pending`(进行中)、‘Resolved(已完成,又称Fulfilled)和Rejected’(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是“Promise’这个名字的由来,它的英语意思就是“承诺“,表示其他手段无法改变。

(2〉一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能。从’Pending ’变为“Resolved’和从’Pending ‘变为‘Rejected’。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。

基本语法

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
// resolve函数在回调函数中如果调用resolve方法, promise会由pending转换为resolved
// reject函数在回调函数中如果调用reject方法, promise会由pending转换为reject

let promise =new Promise(function(resolve,reject){
console.log("founction")
resolve()
//reject()
})

console.log("ws1")

promise.then(
function(){
//promise 转变为resolved 状态时才会执行的函数
console.log("succeed")
}
).catch(

function(){
//promise转变为reject时才会执行的函数
console.log("fail")
}
)

console.log("ws2")
</script>

</head>
<body>

</body>
</html>

async 和await的基本使用

async

async帮助我们使用简洁的语法获得一个promise对象

  • async用户标识函数的async函数返回的结果就是一个promise
  • 方法如果正常return结果,promise状态就是resolved return后的结果就是成功状态的返回值
  • 方法中出现了异常,则返回的promise就是一个失败状态
  • async函数返回的结果如果是一个promise,则状态由内部的promise决定
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
async function fun1() {
return 10
}

let promise=fun1()

promise.then(
function(){}
).catch(
function(){}
)
</script>

</head>
<body>

</body>
</html>
await

await帮助我们获取promise成功状态的返回值的关键字

  • await右边如果是一个普通值,则直接返回该值如果右边是promise,返回promise成功状态的结果

​ let res = await“张三“

​ let res = await Promise.resolve(“张三”) ​ res =“张三”

  • await右边如果是一个失败状态的promise那么await会直接抛异常
  • await关键字必须在async修饰的函数中使用,async函数中可以没有await
  • awat后边的代码会等待await执行完毕继续运行
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
async function fun1() {
return 10
// let promise =Promise.reject("haha")
// return promise
}
async function fun2(){

let res =await fun1()


}

</script>

</head>
<body>

</body>
</html>

听的我直恶心,跳了跳了

7.2 Axios

小案例

App.vue



<script setup>
import { ref } from 'vue';
import axios from 'axios';

const message = ref('');

async function getLoveMessage() {
try {
const response = await axios({
method: 'get',
url: '/api/api/rand.qinghua?format=json',
data: {}
});
console.log(response.data);
if (response.data.code === 1) {
message.value = response.data.content;
}
} catch (error) {
console.error('请求出错:', error.message);
}
}
</script>

<template>
<div>
<h1>{{ message }}</h1>
<button @click="getLoveMessage()">换一个~</button>
</div>
</template>

<style scoped>

</style>

vite.config.js

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';

export default defineConfig({
plugins: [vue()],
server: {
proxy: {
'/api': {
target: 'https://api.uomg.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
}
});

Axios get 和post方法

二、后端

2.1 HTTP

HTTP超文本传输协议(HTTP-Hyper Text transfer protocol),是一个属于应用层的面向对象的协议,由于其简捷、快速的方式,适用于分布式超媒体信息系统。它于1990年提出,经过十几年的使用与发展,得到不断地完善和扩展。它是一种详细规定了浏览器和万维网服务器之间互相通信的规则,通过因特网传送万维网文档的数据传送协议。客户端与服务端通信时传输的内容我们称之为报文。HTTP协议就是规定报文的格式。HTTP就是一个通信规则,这个规则规定了客户端发送给服务器的报文格式,也规定了服务器发送给客户端的报文格式。实际我们要学习的就是这两种报文。客户端发送给服务器的称为“请求报文“,服务器发送给客户端的称为“响应报文““。


咦 这东西好耳熟,计网学过了

有点无聊,我们去学Servlet去吧

2.2 Servlet

记得我们大专的时候,这块老师讲的很详细,好像很重要的样子,李阳,这一块好好看好好学

image-20241228131707368.png

李师傅要开始瞎编了:

首先当在浏览器我们要点击操作的时候,浏览器会发出请求,这里包含请求的信息,然后传给tomcat

tomcat会创建两个方法,获取我们在java中所写的要的操作,然后根据操作做出相应回来

image-20241228132147260.png

demo-xml方式

前端验证用户名,当用户名已注册返回NO,未注册返回YES

哈哈哈哈哈哈哈 终于到实操了,理论太无聊了

代码部分

java

package com.sgg.servlet;

import javax.servlet.Servlet;
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;

/**
* Created with IntelliJ IDEA.
*
* @Author: 李阳
* @Date: 2024/12/28/13:38
* @Description:
* servlet开发流程:
* 1.创建javaweb项目,同时添加依赖
* 2.重写Servlet方法
* 3.在servlet方法中定义业务处理代码
* 4.在xml中配置Servlet对应的请求路径(后面会用注解)
*/
public class UserServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
/**
* 1.从request对象中获取请求的信息
* 2.处理业务代码
* 3.将相应的输入放到response里面去
*/

//1.根据参数名获取参数值
String username = req.getParameter("username");

//当我们的admin和username的值相等,返回NO
String info ="YES";
if("admin".equals(username)) {
info="NO";
}

//该方法返回的是一个向响应体打印字符串的打印流
PrintWriter writer = resp.getWriter();
writer.println(info);


}
}

html

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

<form method="get" action="userServlet">
用户名:<input type="text" name="username"/><br>
<input type="submit" value="校验"/>
</form>

</body>
</html>

xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">

<!--
1.配置servlet类,并起别名
servlet-class告诉tomcat对应要实例化的类
servlet-name 用于关联请求的映射路径
-->

<servlet>
<servlet-name>userServlet</servlet-name>
<servlet-class>com.sgg.servlet.UserServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>userServlet</servlet-name>
<url-pattern>/userServlet</url-pattern>
</servlet-mapping>

</web-app>
效果

image-20241228141726691.png

image-20241228141739406.png

重点讲解:
<servlet-mapping>
<servlet-name>userServlet</servlet-name>
<url-pattern>/userServlet</url-pattern>
</servlet-mapping>

的/userServlet与

<form method="get" action="userServlet">
用户名:<input type="text" name="username"/><br>
<input type="submit" value="校验"/>
</form>

的action要保持一致

url-pattern

<servlet>
<servlet-name>servlet1</servlet-name>
<servlet-class>com.sgg.servlet.Servlet1</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>servlet1</servlet-name>
<url-pattern>/s1</url-pattern>
<url-pattern>/xx1</url-pattern>
</servlet-mapping>

这里简要说明一下流程,毕竟后面使用注解会方便很多

浏览器访问/xx1然后他会去找<servlet-name>servlet1</servlet-name>然后在找到对应的路径包<servlet-class>com.sgg.servlet.Servlet1</servlet-class> </servlet>

/xx1的路径唯一,其他的都不能使用

路径的精确批准与模糊匹配

精确匹配 /xx1

模糊匹配/*

  • /匹配全部,但是不包含jsp文件
  • /*匹配全部,包含jsp文件
  • /a/*匹配前缀,/a/后面随便写都OK
  • image-20241228154559471.png
  • *.action前缀模糊,匹配后缀也即是要以.action结尾
  • image-20241228154747215.png

注解

嘿嘿嘿嘿嘿嘿 最喜欢的一集

注解可以帮我们省掉xml的配置信息,方便大家

folding green, 查看代码xml测试

<servlet>
<servlet-name>servlet1</servlet-name>
<servlet-class>com.sgg.servlet.Servlet1</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>servlet1</servlet-name>
<url-pattern>/s1</url-pattern>
</servlet-mapping>

endfolding

folding yellow, 使用注解

@WebServlet("/s1")
public class Servlet1 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("李阳你好");
}
}

endfolding

只需要在方法面前加@WebServlet,好耶好用

servletConfig和servletContext

servletConfig

这东西好像是用来获取初始化信息的,感觉用的不多,知道就好了

xml-获取 <init-param>里面的值

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">

<servlet>
<servlet-name>servlet</servlet-name>
<servlet-class>com.sgg.servlet.servlet1</servlet-class>

<!--
配置servlet的初始参数
-->
<init-param>
<param-name>keya</param-name>
<param-value>valueA</param-value>
</init-param>
<init-param>
<param-name>keyb</param-name>
<param-value>valueB</param-value>
</init-param>

</servlet>

<servlet-mapping>
<servlet-name>servlet</servlet-name>
<url-pattern>/servlet1</url-pattern>
</servlet-mapping>
</web-app>

java

package com.sgg.servlet;

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

/**
* Created with IntelliJ IDEA.
*
* @Author: 李阳
* @Date: 2024/12/29/09:54
* @Description:
*/
public class servlet1 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletConfig servletConfig = this.getServletConfig();
//获取初始配置信息

//根据参数名获取参数值
String keya = servletConfig.getInitParameter("keya");
System.out.println("keya: " + keya);

//获取所有初始化参数名称
//hasMoreElements判断有没有下一个参数,有就返回true
//
Enumeration<String> initParameterNames = servletConfig.getInitParameterNames();
while (initParameterNames.hasMoreElements()){
String s = initParameterNames.nextElement();//取出下一元素,并移动指针
System.out.println(s + ": " + servletConfig.getInitParameter(s));


}
}
}

image-20241229102420602


使用注解方式

@WebServlet(
urlPatterns = "/servlet1",
initParams = {@WebInitParam(name = "keya",value = "valuea"),@WebInitParam(name = "keyb",value = "keyB")}
)

完整代码

@WebServlet(
urlPatterns = "/servlet1",
initParams = {@WebInitParam(name = "keya",value = "valuea"),@WebInitParam(name = "keyb",value = "keyB")}
)
public class servlet1 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletConfig servletConfig = this.getServletConfig();
//获取初始配置信息

//根据参数名获取参数值
String keya = servletConfig.getInitParameter("keya");
System.out.println("keya: " + keya);

//获取所有初始化参数名称
//hasMoreElements判断有没有下一个参数,有就返回true
//
Enumeration<String> initParameterNames = servletConfig.getInitParameterNames();
while (initParameterNames.hasMoreElements()){
String s = initParameterNames.nextElement();//取出下一元素,并移动指针
System.out.println(s + ": " + servletConfig.getInitParameter(s));


}
}
servletContext
  • ServletContext对象有称呼为上下文对象,或者叫应用域对象(后面统一讲解域对象)。容器会为每个app创建一个独立的唯一的ServletContext对象
  • ServletContext对象为所有的Servlet所共享
  • ServletContext可以为所有的servlet提供初始配置参数

简而言之:为所有的servlet提供所有参数(ServletConfig是为某一个)

功能1:获取初始化值
package com.sgg.servlet;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
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.util.Enumeration;

/**
* Created with IntelliJ IDEA.
*
* @Author: 李阳
* @Date: 2024/12/29/09:54
* @Description:
*/
@WebServlet("/servlet2")
public class servlet2 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取ServletContext
ServletContext servletContext = getServletContext();
ServletContext servletContext1 = req.getServletContext();
System.out.println( servletContext1);
System.out.println( servletContext);

String encoding = servletContext.getInitParameter("encoding");
System.out.println("encoding: "+encoding);

Enumeration<String> initParameterNames = servletContext.getInitParameterNames();
while (initParameterNames.hasMoreElements()) {
String initParameterName = initParameterNames.nextElement();
System.out.println(initParameterName+"="+servletContext.getInitParameter(initParameterName));
}
}
}

xml

<context-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</context-param>

<context-param>
<param-name>username</param-name>
<param-value>liyang</param-value>
</context-param>

演示

image-20241229105856750.png

好无聊好无聊的课

功能2:获取tomcat路径和文件路径
package com.sgg.servlet;

import javax.servlet.ServletContext;
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.FileOutputStream;
import java.io.IOException;

/**
* Created with IntelliJ IDEA.
*
* @Author: 李阳
* @Date: 2024/12/29/11:01
* @Description:往upload里面写入文件信息-io流
*/

@WebServlet("/servlet3")
public class servlet3 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = getServletContext();

//获得一个指向项目部署位置下的某个文件/目录的磁盘路径真实路径
String path=servletContext.getRealPath("upload");
System.out.println(path);
FileOutputStream fileOutputStream = new FileOutputStream(path+"/a.txt");


//获得项目部署的上下文路径,项目的访问路径
//后序可能会用到绝对路径和相对路径来找资源
//例如我们的项目启动路径可能是/demo1/servlet1
//假设我们使用绝对路径,当我们的demo1改变时,就失效了
//context可以帮我们动态的demo1

String contextPath = servletContext.getContextPath();
System.out.println(contextPath);
}
}

image-20241229112333847.png

啊啊啊啊啊啊,太无聊了

功能3:域对象的API
  • 域对象:一些用于存储数据和传递数据的对象,传递数据不同的范围,我们称之为不同的域,不同的域对象代表不同的戢,共享数据的范围也不同
  • ServletContext代表应用,所以ServletContext域也叫作应用域,是webapp中最大的域,可以在本应用内实现数据的共享和传递
  • webapp中的三大域对象,分别是应用域,会话域,请求域

image-20241229112551836.png

放元素

servlet1

package com.sgg.servlet;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
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;
import java.util.Enumeration;

/**
* Created with IntelliJ IDEA.
*
* @Author: 李阳
* @Date: 2024/12/29/09:54
* @Description:
*/

@WebServlet(
urlPatterns = "/servlet1",
initParams = {@WebInitParam(name = "keya",value = "valuea"),@WebInitParam(name = "keyb",value = "keyB")}
)
public class servlet1 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

ServletContext servletContext = getServletContext();
/**
* void setAttribute(String key,Object value);
* 向域中存储/修改数据
* object getAttribute(String key);
* 获得域中的数据
* void removeAttribute(String key);
* 移除域中的数据
*/
//如果没有人用过就是新增,有就是修改
servletContext.setAttribute("keya", "va");
// servletContext.setAttribute("keyb", "keyB");
//获得域中的数据
// Object keya1 = servletContext.getAttribute("keya");
//移除
// servletContext.removeAttribute("keya");
}
}

sevalet3读取

package com.sgg.servlet;

import javax.servlet.ServletContext;
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.FileOutputStream;
import java.io.IOException;

/**
* Created with IntelliJ IDEA.
*
* @Author: 李阳
* @Date: 2024/12/29/11:01
* @Description:往upload里面写入文件信息-io流
*/

@WebServlet("/servlet3")
public class servlet3 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = getServletContext();

//从域中读取数据
String ka = (String) servletContext.getAttribute("keya");
System.out.println(ka);
}
}

image-20241229115159599.png

控制台读取到了sevalet1里面存放的元素

妙秒喵🐱

修改

servlet1

servletContext.setAttribute("keya", "va");
servletContext.setAttribute("keya", "keyB");

image-20241229115601579.png

烙铁,没毛病哦


HttpServaletRequest

  • HttpServletRequest是一个接口,其父接口是ServletRequest
  • HttpServietRequest是Tomcat将请求报文转换封装而来的对象,在Tomcat调用service方法时传入
  • HttpServletRequest代表客户端发来的请求,所有请求中的信息都可以通过该对象获得

获取请求行信息相关的

image-20241229130836940.png

获取请求头

image-20241229131102672.png

package com.sgg.servlet;

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.util.Enumeration;
import java.util.HashMap;

/**
* Created with IntelliJ IDEA.
*
* @Author: 李阳
* @Date: 2024/12/29/13:12
* @Description:获取请求行和请求头
*/
@WebServlet("/servlet04")
public class servlet4 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

//行相关 GET/POST url:http/1.1
System.out.println(req.getMethod());//获取请求方式
System.out.println(req.getScheme());//请求协议
System.out.println(req.getProtocol());//请求协议以及版本
System.out.println(req.getRequestURL());//完整的URl
System.out.println(req.getRequestURI());//获取请求的URL,项目内的资源路径
/**
* GET
* http
* HTTP/1.1
* http://localhost:8080/class2/servlet04
* /class2/servlet04
*
* URL:统一资源定位符 是http协议下一个具体的资源路径 (class URL interments URI)
* URI:统一资源标识符 是资源定位的要求和规范(interface URI)
*/
System.out.println(req.getLocalPort());//本应用容器的端口号
System.out.println(req.getServerPort());//客户端发请求时使用的端口号
System.out.println(req.getRemotePort());//客户端软件的端口号
/**
* 8080
* 8080
* 55603
*/
//头相关 key:value ...
//获取所有请求头的名字
Enumeration<String> headerNames = req.getHeaderNames();
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
String value = req.getParameter(name);
System.out.println(name+"="+value);

}
//根据名字单独获取指定的某个请求头
String accept = req.getHeader("Accept");
System.out.println("accept:"+accept);

/**
* GET
* http
* HTTP/1.1
* http://localhost:8080/class2/servlet04
* /class2/servlet04
* 8080
* 8080
* 55857
* host=null
* connection=null
* cache-control=null
* sec-ch-ua=null
* sec-ch-ua-mobile=null
* sec-ch-ua-platform=null
* upgrade-insecure-requests=null
* user-agent=null
* accept=null
* sec-fetch-site=null
* sec-fetch-mode=null
* sec-fetch-user=null
* sec-fetch-dest=null
* accept-encoding=null
* accept-language=null
* cookie=null
* accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*\/\*;
* q = 0.8, application / signed - exchange;
* v = b3;
* q = 0.7
*/


}
}

获取请求参数

image-20241229131119709.png

image-20241229140918113.png

java

package com.sgg.servlet;

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.util.Arrays;
import java.util.Enumeration;
import java.util.Map;

/**
* Created with IntelliJ IDEA.
*
* @Author: 李阳
* @Date: 2024/12/29/13:47
* @Description:
*/
@WebServlet("/servlet05")
public class servlet05 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//接收请求参数

//根据参数名获取单个值(键值对)
System.out.println(req.getParameter("username"));
System.out.println(req.getParameter("password"));

//根据参数名获取多个参数
String[] hobbies = req.getParameterValues("hobby");
System.out.println(Arrays.toString(hobbies));

//获取全部参数名
Enumeration<String> parameterNames = req.getParameterNames();
while (parameterNames.hasMoreElements()) {
String parameterName = parameterNames.nextElement();
String[] parameterValues = req.getParameterValues(parameterName);
//大于1就拿全部,小于1就拿第一个
if(parameterValues.length>1){
System.out.println(parameterName+"= "+Arrays.toString(parameterValues));

}else {
System.out.println(parameterName+"= "+parameterValues[0]);
}
}
System.out.println("-------------------------------");
//返回所有参数的map集合 key=参数名 value=参数值
Map<String, String[]> parameterMap = req.getParameterMap();
for (Map.Entry<String, String[]> stringEntry : parameterMap.entrySet()) {
System.out.println(stringEntry.getKey()+"= "+Arrays.toString(stringEntry.getValue()));

}


}
}

html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>测试httpServlet</title>
</head>
<body>
<form method="get" action="servlet05">
姓名:<input type="text" name="username"><br>
密码<input type="password" name="password"><br>
爱好:
<input type="checkbox" name="hobby" value="1"><br>
<input type="checkbox" name="hobby" value="2"><br>
<input type="checkbox" name="hobby" value="3">rap<br>
<input type="checkbox" name="hobby" value="4">篮球<br>
<input type="submit">

</form>

</body>
</html>

post或者get都可以拿到

其他api

image-20241229131136745.png

HttpServletResponse

  • HttpServletResponse是一个接口,其父接口是ServletResponse
  • HttpServletResponse是Tomcat预先创建的,在Tomcat调用service方法时传入
  • HttpServletResponse代表对客户端的响应,该对象会被转换成响应的报文发送给客户端,通过该对象我们可以设置响应信息

image-20241229150022820.png

package com.sgg.servlet;

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.io.PrintWriter;

/**
* Created with IntelliJ IDEA.
*
* @Author: 李阳
* @Date: 2024/12/29/15:00
* @Description:
*/
@WebServlet("/servlet06")
public class servlet06 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String info="<h1>李阳</h1>";
//设置响应行相关API 200/404/405/500 HTTP/1.1
resp.setStatus(200);

//设置响应头
resp.setHeader("sss","value1");

resp.setContentType("text/html");
resp.setContentLength(info.getBytes().length);

//设置响应体
PrintWriter writer = resp.getWriter();
writer.write(info);

//
}
}

2.3请求与转发

  • 请求转发生活举例:张三找李四借钱,李四没有,李四找王五,让王五借给张三

  • 响应重定向生活举例:张三找李四借钱,李四没有,李四让张。去找王五,张三自己再去找王五借钱

  • 请求转发通过HttpServletRequest实现,响应重定向通过HttpServletResponse实现

1请求转发时,请求和响应对象会继续传递给下一个资源

2请求中的参数可以继续向下传递

3请求转发时服务器内部的行为,客户端是不知道的

4客户端只产生了一次请求

image-20241229153756014.png

selectA处理不了给了B,B再响应给客户端

这个就是定义转发的代码

req.getRequestDispatcher("servletB").forward(req, resp);

servletA

package com.sgg.servlet;

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;

/**
* Created with IntelliJ IDEA.
*
* @Author: 李阳
* @Date: 2024/12/29/15:42
* @Description:
*/
@WebServlet("/servletA")
public class servletA extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("servletA");

//请求转发B
req.getRequestDispatcher("servletB").forward(req, resp);
}
}

servletB

package com.sgg.servlet;

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;

/**
* Created with IntelliJ IDEA.
*
* @Author: 李阳
* @Date: 2024/12/29/15:42
* @Description:
*/
@WebServlet("/servletB")
public class servletB extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("servletB");
}
}

image-20241229155049373.png

转发到页面

@WebServlet("/servletA")
public class servletA extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("servletA");

String money = req.getParameter("money");
System.out.println("ServletA"+money);

//请求转发B
req.getRequestDispatcher("WEB-INF/b.html").forward(req, resp);
//req.getRequestDispatcher("a.html").forward(req, resp);
}
}

总结-面试要背
  • 请求转发通过HttpServletRequest对象获取请求转发器实现
  • 请求转发是服务器内部的行为,对客户端是屏蔽的
  • 客户端只发送了一次请求,客户端地址栏不变
  • 服务端只产生了一对请求和响应对象,这一对请求和响应对象会继续传递给下一个资源
  • 因为全程只有一个HttpServletRequset对象,所以请求参数可以传递,请求域中的数据也可以传递
  • 请求转发可以转发给其他Servlet动态资源,也可以转发给一些静态资源以实现页面跳转
  • 请求转发可以转发给WEB-INF下受保护的资源
  • 请求转发不能转发到本项目以外的外部资源

2.4 重定向

客户端发送请求给A,A处理不了,然后A就返回response,把目标头302和location:目标地址 返回给浏览器,浏览器再通过目标地址去请求其他功能页面

image-20241229171354913.png

特点

  • 响应重定向通过HttpServletResponse对象的sendRedirect方法实现
  • 响应重定向是服务端通过302响应码和路径,告诉客户端自己去找其他资源,是在服务端提示下的,客户端的行为
  • 客户端至少发送了两次请求,客户端地址栏是要变化的
  • 服务端产生了多对请求和响应对象,且请求和响应对象不会传递给下一个资源
  • 因为全程产生了多个HttpServletRequset对象,所以请求参数不可以传递,请求域中的数据也不可以传递
  • 重定向可以是其他Servlet动态资源,也可以是一些静态资源以实现页面跳转
  • 重定向不可以到给WEB-INF下受保护的资源
  • 重定向可以到本项目以外的外部资源
package com.sgg.servlet;

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;

/**
* Created with IntelliJ IDEA.
*
* @Author: 李阳
* @Date: 2024/12/29/17:07
* @Description:
*/
@WebServlet("/servlet1")
public class servlet1 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//接收用户请求
System.out.println("servlet1");

//响应重定向
resp.sendRedirect("servlet2");
}
}

2.5 解决响应乱码

resp.setContentType("text/html;charset=utf-8");
resp.getWriter().write("你好,hello");

2.6 MVC

MVC (Model view Controller)是软件工程中的一种软件架构模式,它把软件系统分为模型、视图和控制器三个基本部分。用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。

image-20241230100316483.png

image-20241230101219329.png

2.7 会话管理

  • ·无状态就是不保存状态,即无畎态协议(stateless),HTTP协议自身不对请求和响应之间的通信状态进行保存,也就是说,在HTTP协议这个级别协议对于发送过的请求或者响应都不做持久化处理
  • 简单理解:浏览器发送请求.服务器接收并响应,但是服务器不记录请求是否来自哪个浏览器,服务器没记录浏览器的特征,就是客户端的状态

image-20241231120551892.png

cookie是一种客户端会话技术,cookie由服务端产生,它是服务器存放在浏览器的一小份数据,浏览器以后每次访问该服务器的时候都会将这小份数据携带到服务器去。

  • 服务端创建cookie,将cookie放入响应对象中,Tomcat容器将cookie转化为set-cookie响应头,响应给客户端。
  • 客户端在收到cookie的响应头时,在下次请求该服务的资源时,会以cookie请求头的形式携带之前收到的Cookie
  • cookie是一种键值对格式的数据,从tomcat8.5开始可以保存中文,但是不推荐
  • 由于cookie是存储于客户端的数据,比较容易暴露,一般不存储一些敏感或者影响安全的数据
生存周期
  • 会话级Cookie

    • 服务器端并没有明确指定Cookie的存在时间。

    • 在浏览器端,Cookie数据存在于内存中。

    • 只要浏览器还开着,Cookie数据就一直都在。

    • 浏览器关闭,内存中的cookie数据就会被释放·

  • 持久化Cookie

    • 服务器端明确设置了Cookie的存在时间。

    • 在浏览器端,Cookie数据会被保存到硬盘上

    • Cookie在硬盘上存在的时间根据服务器端限定的时间来管控,不受浏览器关闭的影响。

    • 持久化Cookie到达了预设的时间会被释放

会话级代码演示
package com.sgg.servlet;
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;
/**
* @Description: 创建cookie,将cookie放入response对象
*/

@WebServlet("/servletA")
public class ServletA extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.
Cookie cookie1 = new Cookie("kaya","vala");
Cookie cookie2 = new Cookie("kayb","valb");

//2.
resp.addCookie(cookie1);
resp.addCookie(cookie2);
}
}

捕获

package com.sgg.servlet;

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;

/**
* Created with IntelliJ IDEA.
*
* @Author: 李阳
* @Date: 2024/12/31/13:19
* @Description:获取请求中携带的cookie
*/
@WebServlet("/servletB")
public class ServletB extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

//获取cookie
Cookie[] cookies = req.getCookies();
//请求的多个cookie会进入该数组,没有cookie会为null报空指针异常
for (Cookie cookie : cookies) {
System.out.println(cookie.getName() + " " + cookie.getValue());

}
}
}

持久化
Cookie cookie1 = new Cookie("kaya","vala");
Cookie cookie2 = new Cookie("kayb","valb");
cookie1.setMaxAge(3600);

image-20241231140608015.png

2.7.2 Session

session要和cookie配合使用

HttpSession是一种保留更多信息在服务端的一种技术,服务器会为每一个客户端开辟一块内存空间,即session对象.客户端在发送请求时,都可以使用自已的session.这样服务端就可以通过session来记录某个客户端的状态了

  • 服务端在为客户端创建session时,会同时将session对象的id,即JSESSIONID以cookie的形式放入响应对象。
  • 后端创建完session后,客户端会收到一个特殊的cookie,叫做JSESSIONID
  • 客户端下一次请求时携带JSESSIONID,后端收到后,根据JSESSIONID找到对应的session对象。
  • 通过该机制,服务端通过session就可以存储一些专门针对某个客户端的信息了
  • session也是域对象(后续详细讲解)
  • image-20241231141259769.png

2.7.3 三大域对象

域对象:一些用于存储数据和传递数据的对象,传递数据不同的范围,我们称之为不同的域,不同的域对象代表不同的域,共享数据的范围也不同

  • web项目中,我们一定要熟练使用的域对象分别是请求域,会话域,应用域
  • 请求域对象是HttpServletRequest ,传递数据的范围是一次请求之内及请求转发。
  • 会话域对象是HttpSession,传递数据的范围是一次会话之内,可以跨多个请求。
  • 应用域对象是ServletContext,传递数据的范围是本应用之内,可以跨多个会话

image-20241231143352815.png

image-20241231143732561.png

image-20241231143759794.png

image-20241231143829763.png

域对象的使用
API功能
void setAttribute(String name,String value)向域对象中添加/修改数据
Object getAttribute(String name)从域对象中获取数据
removeAttribute(String name)移除域对象中数据

2.8 过滤器-Filter

1.概述

  • Filter接口定义了过滤器的开发规范,所有的过滤器都要实现该接口
  • ·Filter的工作位置是项目中所有目标资源之前,容器在创建HttpServletRequest和HttpServletResponse对象后,会先调用Filter的doFiter方法
  • Filter的doFilter方法可以控制请求是否继续,如果放行,则请求继续,如果拒绝,则请求到此为止,由过滤器本身做出响应
  • Filter不仅可以对请求做出过滤,也可以在目标资源做出响应前,对响应再次进行处理
  • Filter是GOF中责任链模式的典型案例
  • 。Filter的常用应用包括但不限于:登录权限检查,解决网站乱码,过滤敏感字符,日志记录,性能分析…..

2.举例

  • 公司前台对来访人员进行审核,如果是游客则拒绝进入公司,如果是客户则放行.客户离开时提醒客户不要遗忘物品
  • 停车场保安对来访车辆进行控制,如果没有车位拒绝进入,如果有车位,发放停车卡并放行,车辆离开时收取请车费
  • 地铁验票闸机在人员进入之前检查票,没票拒绝进入,有票验票后放行,人员离开时同样验票

3.应用

  • 日志的记录
  • ·性能的分析。
  • 乱码的处理·
  • 事务的控制·
  • 登录的控制·
  • 跨域的处理
  • … …

image-20241231145350797.png image-20241231143829763.png image-20241231143352815.png image-20241231143732561.png image-20241231143759794.png

4.格式

<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
<display-name>Archetype Created Web Application</display-name>

<!--配置过滤器-->
<filter>
<filter-name>logginFilter</filter-name>
<filter-class>com.sgg.filters.LoggingFilter</filter-class>
</filter>

<!--配置过滤器的过滤资源规则 -->
<filter-mapping>
<filter-name>logginFilter</filter-name>
<!--
url-pattern 根据请求的资源路径对指定的请求进行过滤
/* 过滤全部资源
/a/* 过滤以a开头的资源
*.html 过滤以htmL为后缀的资源
/servlet1 servlet1请求进行过油

对某一个请求过滤
@WebServlet(value = "/servlet1",name = "servlet11")
<servlet-name>servlet11</servlet-name>

一个filter-mapping中可以存在多个servlet-name和url-pattern
-->


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

</web-app>

2.8.5 生命周期

过滤器作为web项目的组件之一和Servlet的生命周期类似,略有不同,没有servlet的load-on-startup的配置,默认就是系统启动立刻构造

阶段对应方法执行时机执行次数
创建对象构造器web应用启动时1
初始化方法void init(FilterConfig filterConfig)构造完毕1
过滤请求void doFilter(ServietRequest servletRequest, ServietResponse servletResponse,FilterChainfilterChain)每次请求多次
销毁default void destroy()web应用关闭时1次

代码我很懒给略了

2.8.6 过滤器链的使用

一个web项目中,可以同时定义多个过滤器,多个过滤器对同一个资源进行过滤时,工作位置有先后,整体形成一个工作链,称之为过滤器链

  • 过滤器链中的过滤器的顺序由filter-mapping顺序决定
  • 每个过滤器过滤的范围不同,针对同一个资源来说,过滤器链中的过滤器个数可能是不同的
  • 如果某个Filter是使用ServletName进行匹配规则的配置,那么这个Filter执行的优先级要更低

image-20250117193438865

工作流程

image-20250119101248427

过滤器链中的过滤器的顺序由filter-mapping顺序决定

<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
<display-name>Archetype Created Web Application</display-name>

<!-- 配置多个过滤器-->

<filter>
<filter-name>filter1</filter-name>
<filter-class>com.sgg.filters.Filter1</filter-class>
</filter>
<filter>
<filter-name>filter2</filter-name>
<filter-class>com.sgg.filters.Filter2</filter-class>
</filter>
<filter>
<filter-name>filter3</filter-name>
<filter-class>com.sgg.filters.Filter3</filter-class>
</filter>

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

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

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

</web-app>

2.8.6.1 使用注解方式

@WebFilter("/*")

使用注解方式代码举例

package com.sgg.filters;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter("/*")
public class Filter1 implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("filter1 方行前代码");
//放行代码
filterChain.doFilter(servletRequest, servletResponse);

System.out.println("filter1放行后代码");
}
}

使用注解后,控制执行顺序需要改变忘记命名顺序,字典的前后区别

2.9 监听器

监听器:专门用于对域对象对象身上发生的事件或状态改变进行监听和相应处理的对象

  • 监听器是GOF设计模式中,观察者模式的典型案例
  • 观察者模式:当被观察的对象发生某些改变时,观察者自动采取对应的行动的一种设计模式
  • 监听器使用的感受类似Js中的事件,被观察的对象发生某些情况时,自动触发代码的执行。
  • 监听器并不监听web项目中的所有组件,仅仅是对三大域对象做相关的事件监听

监听器的分类

  • web中定义八个监听器接口作为监听器的规范,这八个接口按照不同的标准可以形成不同的分类
  • 按监听的对象划分
    • application域监听器 ServletContextListener ServletContextAttributeListener
    • session域监听器 HttpSessionListener HttpSessionAttributeListener HttpSessionBindingListener HttpSessionActivationListener
    • request域监听器 ServletRequestListener ServletRequestAttributeListener
  • 按监听的事件分
    • 域对象的创建和销毁监听器 ServletContextListener HttpSessionListener ServletRequestListener
    • 域对象数据增删改事件监听器 ServletContextAttributeListener HttpSessionAttributeListener ServletRequestAttributeListener
    • 其他监听器 HttpSessionBindingListener HttpSessionActivationListener

2.9.2 监听器的六个主要接口

2.9.2.1 application域监听器

ServletContextListener 监听ServletContext对象的创建与销毁

方法名作用
contextInitialized(ServletContextEvent sce)ServletContext创建时调用
contextDestroyed(ServletContextEvent sce)ServletContext销毁时调用
  • ServletContextEvent对象代表从ServletContext对象身上捕获到的事件,通过这个事件对象我们可以获取到ServletContext对象。

ServletContextAttributeListener 监听ServletContext中属性的添加、移除和修改

方法名作用
attributeAdded(ServletContextAttributeEvent scab)向ServletContext中添加属性时调用
attributeRemoved(ServletContextAttributeEvent scab)从ServletContext中移除属性时调用
attributeReplaced(ServletContextAttributeEvent scab)当ServletContext中的属性被修改时调用
  • ServletContextAttributeEvent对象代表属性变化事件,它包含的方法如下:
方法名作用
getName()获取修改或添加的属性名
getValue()获取被修改的属性值
getServletContext()获取ServletContext对象

ServletContextListener 应用域代码

package com.sgg.listen;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
/**
* Created with IntelliJ IDEA.
*
* @Author: 李阳
* @Date: 2025/01/19/11:19
* @Description:监听器
*/
@WebListener
public class MyApplicationLister implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext servletContext = sce.getServletContext();
System.out.println(servletContext.hashCode()+"应用域被初始化了");
}

@Override
public void contextDestroyed(ServletContextEvent sce) {
ServletContext servletContext = sce.getServletContext();
System.out.println(servletContext.hashCode()+"应用域被销毁");
}
}

[2025-01-19 01:17:29,228] 工件 demo9-listen:Web exploded: 正在部署工件,请稍候…
1135537166应用域被初始化了
1135537166应用域被销毁
已与服务器断开连接

ServletContextAttributeListener监听数据变化代码

package com.sgg.listen;

import javax.servlet.*;
import javax.servlet.annotation.WebListener;
/**
* Created with IntelliJ IDEA.
*
* @Author: 李阳
* @Date: 2025/01/19/11:19
* @Description:监听器
*/
@WebListener
public class MyApplicationLister implements ServletContextListener, ServletContextAttributeListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext servletContext = sce.getServletContext();
System.out.println(servletContext.hashCode()+"应用域被初始化了");
}

@Override
public void contextDestroyed(ServletContextEvent sce) {
ServletContext servletContext = sce.getServletContext();
System.out.println(servletContext.hashCode()+"应用域被销毁");
}

@Override
public void attributeAdded(ServletContextAttributeEvent scae) {
ServletContext servletContext = scae.getServletContext();
String key = scae.getName();
Object value = scae.getValue();
System.out.println(servletContext.hashCode()+"应用域增加了"+key+": "+value);
}

@Override
public void attributeRemoved(ServletContextAttributeEvent scae) {
ServletContext servletContext = scae.getServletContext();
String key = scae.getName();
Object value = scae.getValue();
System.out.println(servletContext.hashCode()+"应用域移除了"+key+": "+value);
}

@Override
public void attributeReplaced(ServletContextAttributeEvent scae) {
ServletContext servletContext = scae.getServletContext();
String key = scae.getName();
Object value = scae.getValue();//获取的是旧的值

//获取新的值
Object newValue = servletContext.getAttribute(key);
System.out.println(servletContext.hashCode()+"应用域修改了"+key+": "+value+"为"+newValue);
}
}

已连接到服务器
[2025-01-19 02:25:48,245] 工件 demo9-listen:Web exploded: 正在部署工件,请稍候…
762047125应用域被初始化了
762047125应用域增加了keya: valuea
762047125应用域修改了keya: valuea为valuexx
762047125应用域移除了keya: valuexx
762047125应用域被销毁
19-Jan-2025 14:26:57.614 信息 [main] org.apache.coyote.AbstractProtocol.stop 正在停止ProtocolHandler ["http-nio-8080"]
19-Jan-2025 14:26:57.630 信息 [main] org.apache.coyote.AbstractProtocol.destroy 正在销毁协议处理器 ["http-nio-8080"]
已与服务器断开连接

日程管理

link 日程表项目后端系统,https://github.com/lyay23/atguigu-scheduleSystem

前端工程化 lyay23/atguigu-vue-scheduleSystem (github.com)

错误

1.实体类名称不匹配

我在经行测试的时候遇到了一些错误

image-20241230144156493.png

控制台说这个名字不匹配

检查发现是因为我的实体类名称不叫uid,而查询报错

解决方案1:为sql语句的uid取别名与pojo的一样

方案2:修改pojo与数据库的uid一致

image-20241230144349702.png

image-20241230144423606.png

两种方法都可行


2.中文乱码问题

image-20250119202153241

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

设置中文