javaWeb笔记
黑马javaweb :
Web开发
什么是web开发
Web:全球广域网,也称为万维网(www World Wide Web),能够通过浏览器访问的网站
网站的工作流程
1.首先我们需要通过浏览器访问发布到前端服务器中的前端程序,这时候前端程序会将前端代码返回给浏览器 2.浏览器得到前端代码,此时浏览器会将前端代码进行解析,然后展示到浏览器的窗口中,这时候我们就看到了网站的页面 3.但是此时这个页面是没有数据的,因为数据在我们的数据库中,所以我们浏览器需要根据前端代码中指定的后台服务器的地址 向 我们的后台服务器 (内部有java程序)发起请求,后台服务器再去从数据库中获取数据,然后返回给浏览器 4.浏览器拿到后台返回的数据后,然后将数据展示在前端资源也就是网页上
网站的开发模式
前后端分离
目前企业开发的主流,市场占有率70%以上)这种开发模式的特点如下 前端人员开发前端程序,前端程序单独部署到前端服务器上 后端人员开开发后端程序,后端程序单独部署到后端服务器上
混合开发
(早期的开发技术,目前慢慢退出市场),这种开发模式的特点是:前端人员开发的代码和后端人员开发的代码在同一个项目中,一起打包部署
Web开发技术栈
前端web开发:
| 技术 | 描述 |
|---|---|
| HTML | 用于构建网站的基础结构的 |
| css | 用于美化页面的,作用和化妆或者整容作用一样 |
| JavaScript | 实现网页和用户的交互 |
| Vue | 主要用于将数据填充到html页面上的 |
| Element | 主要提供了一些非常美观的组件 |
| Nginx | 一款web服务器软件,可以用于部署我们的前端工程 |
后端web开发:
| 技术 | 描述 |
|---|---|
| Maven | 一款java中用于管理项目的软件 |
| Mysql | 最常用的一款数据库软件之一 |
| SpringBoot | spring家族的产品,当前最为主流的项目开发技术。 |
| Mybatis | 用于操作数据库的框架 |
| 阶段 | 内容 | |
|---|---|---|
| Web前端基础 | HTML、CSS、JS、Vue3、Ajax | |
| Web后端基础 | Maven、HTTP协议、Spring IOC、DI、MySQL、JDBC、Mybatis | |
| Web后端实战 | Tlias案例(基于案例讲解web开发的核心知识) | |
| Web后端进阶 | SpringAOP、SpringBoot原理、自定义Starter、Maven高级 | |
| 前端web实战 | Vue工程化、ElementPlus、Tlias案例 | |
| 项目部署 | Linux、Docker |
HTTP协议
版本
HTTP/1.1
-
HTTP 的第一个标准化版本 HTTP/1.1 ( RFC 2068 ) 于 1997 年初发布,支持七种请求方法:OPTIONS,GET,HEAD,POST,PUT,DELETE,和TRACE
-
HTTP/1.1 是 HTTP 1.0 的增强:
-
虚拟主机允许从单个 IP 地址提供多个域。
-
持久连接和流水线连接允许 Web 浏览器通过单个持久连接发送多个请求。
-
缓存支持节省了带宽并使响应速度更快。
-
-
HTTP/1.1 在接下来的 15 年左右将非常稳定。
-
在此期间,出现了 HTTPS(安全超文本传输协议)。它是使用 SSL/TLS 进行安全加密通信的 HTTP 的安全版本。
后面还有2和3版本,但一般使用1.1版本
会话方式
-
浏览器与WEB服务器的连接过程是短暂的,每次连接只处理一个请求和响应。对每一个页面的访问,浏览器与WEB服务器都要建立一次单独的连接。
-
浏览器到WEB服务器之间的所有通讯都是完全独立分开的请求和响应对。
请求和响应报文
报文格式: 主体上分为报文首部和报文主体,中间空行隔开 报文首部可以继续细分为 "行" 和 "头"
请求报文
客户端发给服务端的报文
-
请求报文格式
-
请求首行(请求行); GET/POST 资源路径?参数 HTTP/1.1
-
请求头信息(请求头);
-
空行;
-
请求体;POST请求才有请求体
-
发送GET请求特点
1、由于请求参数在请求首行中已经携带了,所以没有请求体,也没有请求空行 2、请求参数拼接在url地址中,地址栏可见[url?name1=value1&name2=value2],不安全 3、由于参数在地址栏中携带,所以由大小限制[地址栏数据大小一般限制为4k],只能携带纯文本 4、get请求参数只能上传文本数据 5、没有请求体。所以封装和解析都快,效率高, 浏览器默认提交的请求都是get请求比如:地址栏输入回车,超链接,表单默认的提交方式 6,get方式也可以有请求体,但是浏览器form表单的提交都是不把数据放在请求体内的
发送post请求特点
1、POST请求有请求体,而GET请求没有请求体。 2、post请求数据在请求体中携带,请求体数据大小没有限制,可以用来上传所有内容[文件、文本] 3、只能使用post请求上传文件 4、post请求报文多了和请求体相关的配置[请求头] 5、地址栏参数不可见,相对安全 6、post效率比get低
-
请求行组成部分
-
请求方式
-
访问服务器的资源路径?参数1=值1&参数2=值2 ... ... //一般get方式会将参数放在这里
-
协议及版本 HTTP/1.1
-
POST /05_web_tomcat/login_success.html HTTP/1.1
-
请求头
Host: localhost:8080
Connection: keep-alive
Content-Length: 31 -请求体内容的长度
Cache-Control: max-age=0 -无缓存
Origin: http://localhost:8080
Upgrade-Insecure-Requests: 1 -协议的自动升级
Content-Type: application/x-www-form-urlencoded -请求体内容类型[服务器根据类型解析请求体参数]
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.75 Safari/537.36
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: http://localhost:8080/05_web_tomcat/login.html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
Cookie:JSESSIONID-
-
请求空行
-
请求体:浏览器提交给服务器的数据 (post才有)
username=admin&password=1232131
响应报文
响应报文格式
-
响应首行(响应行); 协议/版本 状态码 状态码描述
-
响应头信息(响应头);
-
空行;
-
响应体;
-
响应行组成部分
-
协议及版本 HTTP/1.1
-
响应状态码 200
-
状态描述 OK (可省)
-
HTTP/1.1 200 OK
说明:响应协议为HTTP1.1,响应状态码为200,表示请求成功;
-
响应头
Server: Apache-Coyote/1.1 服务器的版本信息
Accept-Ranges: bytes
ETag: W/"157-1534126125811"
Last-Modified: Mon, 13 Aug 2018 02:08:45 GMT
Content-Type: text/html 响应体数据的类型[浏览器根据类型解析响应体数据]
Content-Length: 157 响应体内容的字节数
Date: Mon, 13 Aug 2018 02:47:57 GMT 响应的时间,这可能会有8小时的时区差
-
响应体
<!--需要浏览器解析使用的内容[如果响应的是html页面,最终响应体内容会被浏览器显示到页面中]-->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
恭喜你,登录成功了...
</body>
</html>
注: 响应头中的 Content-Type 决定着浏览器会如何处理响应体内返回的数据 在请求静态资源时,tomcat会自动根据conf/web.xml内记录的关系来自动加上文件对应的MIME类型 在请求动态资源时,可使用 response.setHeader来设置或直接用response.setContentType来直接设置
响应状态码
响应状态码:响应码对浏览器来说很重要,它告诉浏览器响应的结果。比较有代表性的响应码如下:
-
200: 请求成功,浏览器会把响应体内容(通常是html)显示在浏览器中;
-
302: 重定向,当响应码为302时,表示服务器要求浏览器重新再发一个请求,服务器会发送一个响应头Location指定新请求的URL地址;
-
304: 使用了本地缓存
-
404: 请求的资源没有找到,说明客户端错误的请求了不存在的资源;
-
405: 请求的方式不允许
-
500: 请求资源找到了,但服务器内部出现了错误;
MVC架构模式
定义
MVC(Model View Controller)是软件工程中的一种
软件架构模式,它把软件系统分为模型、视图和控制器三个基本部分。用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。
-
M:Model 模型层,具体功能如下
-
存放和数据库对象的实体类以及一些用于存储非数据库表完整相关的VO对象
-
存放一些对数据进行逻辑运算操作的的一些业务处理代码
-
-
V:View 视图层,具体功能如下
-
存放一些视图文件相关的代码 html css js等
-
在前后端分离的项目中,后端已经没有视图文件,该层次已经衍化成独立的前端项目
-
-
C:Controller 控制层,具体功能如下
-
接收客户端请求,获得请求数据
-
将准备好的数据响应给客户端
-
MVC模式下,项目中的常见包
-
M:
-
实体类包(pojo /entity /bean) 专门存放和数据库对应的实体类和一些VO对象
-
数据库访问包(dao/mapper) 专门存放对数据库不同表格CURD方法封装的一些类
-
服务包(service) 专门存放对数据进行业务逻辑运算的一些类
-
-
C:
-
控制层包(controller)
-
-
V:
-
web目录下的视图资源 html css js img 等
-
前端工程化后,在后端项目中已经不存在了
-
具体结构
contoller层 controller层负责处理外部传来的接口 service层 service层负责提供具体的业务处理方法,供controller层调用,可使用一个接口对应一个实现类来方便调用 在接口方法上提供该方法使用的注释 dao,mapper层 dao或mapper层提供对数据库的处理方法,负责操控数据库 实体类(bean,pojo等) 实体类负责将对数据库操纵的数据进行封装,方便进行专递及使用
前端
Web标准
Web标准也称为网页标准,由一系列的标准组成,大部分由W3C( World Wide Web Consortium,万维网联盟)负责制定 有三个组成部分 HTML:负责网页的结构(页面元素和内容) CSS:负责网页的表现(页面元素的外观、位置等页面样式,如:颜色、大小等) JavaScript:负责网页的行为(交互效果)
HTML
什么是HTML
HTML: HyperText Markup Language,超文本标记语言 超文本:超越了文本的限制,比普通文本更强大。除了文字信息,还可以定义图片、音频、视频等内容 标记语言:由标签构成的语言 HTML标签都是预定义好的。例如:使用 <h1> 标签展示标题,使用<a>展示超链接,使用<img>展示图片,<video>展示视频 HTML代码直接在浏览器中运行,HTML标签由浏览器解析 html由标签,属性,文本,元素组成 标签:一对<> 属性:对标签特征进行设置的一种方式,多个属性用空格隔开 文本:标签内部的文本 元素:开始标签 + 属性 + 文本 + 结束标签 即为一个元素
HTML语法规则
注释语法为: <!-- 文字 --> 根标签<html> 只能有一对 双标签:<> </> 单标签: </> 标签之间可以嵌套发挥作用 属性必须有值,值必须加引号,html单双引号都可以代表字符串 HTML中不严格区分大小写,但大小写不可以混用 属性和属性名一样时,可以省略值的书写 HTML内的一些特殊符号如 < > 等 需要使用特定的一些转义符号表示,转义符号开头都是 &
标签
标签的特点
HTML标签不区分大小写 HTML标签的属性值,采用单引号、双引号都可以 HTML语法相对比较松散
基础
<!DOCTYPE html> HTML5的文档类型声明
<html>
<head>
<title>HTML 快速入门</title>
字符集
<meta charset="UTF-8">
在移动设备上的显示宽度及缩放比例
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
</body>
</html>
其中<html>是根标签,<head>和<body>是子标签 <head> : 定义网页的头部,用来存放给浏览器看的信息,如:CSS样式、网页的标题。 <body> : 定义网页的主体部分,存放给用户看的信息,也是网页的主体内容,如:文字、图片、视频、音频、表格等 <!DOCTYPE html> : HTML5的文档类型声明 一个网页是由多个标签组成的,在<body>标签内为浏览器的内容,每个标签内可以嵌套别的标签,也可以存放文字
标题标签
<h1> 11111111111111 </h1>
<h2> 11111111111111 </h2>
<h3> 11111111111111 </h3>
<h4> 11111111111111 </h4>
<h5> 11111111111111 </h5>
<h6> 11111111111111 </h6>
<!- h1为一级标题,字体也是最大的 ; h6为六级标题,字体是最小的 ->
超链接a标签
<a href="目标网址" target="跳转方式"> 跳转网站</a>
属性:
- href: 指定资源访问的url
- target: 指定在何处打开资源链接
- _self: 默认值,在当前页面打开
- _blank: 在空白页面打开
常见标签
| 标签 | 作用 | 属性/说明 |
|---|---|---|
| <video> | 视频标签 | src:指定视频的url(绝对路径/相对路径) |
| <video> | 视频标签 | controls:是否显示播放控件 |
| <video> | 视频标签 | width:宽度(像素/相对于父元素百分比);备注: 一般width 和 height 我们只会指定一个,另外一个会自动的等比例缩放。 |
| <video> | 视频标签 | height:高度(像素/相对于父元素百分比);备注: 一般width 和 height 我们只会指定一个,另外一个会自动的等比例缩放。 |
| <img> | 图片标签 | src, width,height |
| <p> | 段落标签 | |
| 换行标签 | ||
| <b> / <strong> | 加粗 | <strong> 具有强调语义 |
| <u> / <ins> | 下划线 | <ins> 具有强调语义 |
| <i> / <em> | 倾斜 | <em> 具有强调语义 |
| <s> / <del> | 删除线 | <del> 具有强调语义 |
注
在HTML页面中,我们在编辑器中通过回车实现的换行, 仅仅在文本编辑器中会看到换行效果, 浏览器是不会解析的, HTML中换行需要通过br标签
实体字符
在HTML页面中,我们在代码中录入空格、<、> 这些符号的时候,是没有对应的效果的, 因为浏览器并不能准确的识别,此时,我们就需要通过字符实体来表示空格,<, > 。常见符号的字符实体如下:
| 字符实体 | 属性/说明 |
|---|---|
| & nbsp; | 空格 |
| & lt; | < |
| & gt; | > |
网页底部
<!-- 页脚版权区域 -->
<footer class="footer">
<p class="company-name">江苏传智播客教育科技股份有限公司</p>
<p class="copyright">版权所有 Copyright 2006-2024 All Rights Reserved</p>
</footer>
/* 页脚版权区域 */
.footer {
//背景色
color: white; //文字颜色
text-align: center; //文本居中
padding: 20px 0; //内边距
margin-top: 30px; //顶部外边距
}
.company-name {
font-size: 1.1em;
font-weight: bold;
}
.copyright {
font-size: 0.9em; //字体大小
}
路径表示
在引入图片、视频、音频、css等内容时,我们需要指定文件的路径,而在前端开发中,路径的书写形式分为两类 绝对路径: 绝对磁盘路径: <img src="C:\Users\Administrator\Desktop\HTML\img\logo.png"> 绝对网络路径: <img src="``https://i2.sinaimg.cn/dy/deco/2012/0613/yocc20120613img01/news_logo.png``"> 相对路径: ./ : 当前目录 , ./ 可以省略的 ../: 上一级目录
盒子模型
介绍
-
盒子:页面中所有的元素(标签),都可以看做是一个 盒子,由盒子将页面中的元素包含在一个矩形区域内,通过盒子的视角更方便的进行页面布局。
-
盒子模型组成:内容区域(content)、内边距区域(padding)、边框区域(border)、外边距区域(margin)。 盒子的大小,其实就包括三个部分: border、padding、content,而margin外边距是不包括在盒子之内的
使用
通过css设置 用width,height设置盒子内容大小,若设置 box-sizing: border-box,可让其为整个盒子的大小(不包括margin) 宽高可分为 % 和 px 设置, 百分比是表示其占其父类标签的部分 宽高也可用auto设置,浏览器会自动设置宽高 每个部分最多可接受4个值,顺序为 上右下左
布局标签
布局标签:实际开发网页中,会大量频繁的使用 div 和 span 这两个没有语义的布局标签。 标签:<div> <span> 特点:
- <div>标签:
- 一行只显示一个(独占一行)
- 宽度默认是父元素的宽度,高度默认由内容撑开
- 可以设置宽高(width、height)
- <span>标签:
- 一行可以显示多个
- 宽度和高度默认由内容撑开
- 不可以设置宽高(width、height)//很多时候宽高都不会生效
表单
定义表单
- 表单标签: <form>
- 表单属性:
- action: 规定表单提交时,向何处发送表单数据,表单提交的URL。
- method: 规定用于发送表单数据的方式,常见为: GET、POST。
- GET:表单数据是拼接在url后面的, 如: xxxxxxxxxxx?username=Tom&age=12,url中能携带的表单数据大小是有限制的。
- POST: 表单数据是在请求体(消息体)中携带的,大小没有限制。
- 表单项标签: 不同类型的input元素、下拉列表、文本域等。
- input: 定义表单项,通过type属性控制输入形式
- select: 定义下拉列表, <option> 定义列表项
- textarea: 定义文本域
<!--- <input>: 表单项 , 通过type属性控制输入形式。 -->
type取值 描述
text 默认值,定义单行的输入字段
password 定义密码字段
radio 定义单选按钮
checkbox 定义复选框
file 定义文件上传按钮
date/time/datetime-local 定义日期/时间/日期时间
number 定义数字输入框
email 定义邮件输入框
hidden 定义隐藏域
submit / reset / button 定义提交按钮 / 重置按钮 / 可点击按钮
使用
表单要想能够传输数据,需要设置name属性,在提交时,会以 name=数据 的形式提交
演示
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>HTML-表单</title>
</head>
<body>
<!--
form表单属性:
action: 表单提交的url, 往何处提交数据 . 如果不指定, 默认提交到当前页面
method: 表单的提交方式 .
get: 在url后面拼接表单数据, 比如: ?username=Tom&age=12 , url长度有限制 . 默认值
post: 在消息体(请求体)中传递的, 参数大小无限制的.
-->
<form action="/save" method="get"> <!-- 更改method方法即可更改提交方法 -->
用户名: <input type="text" name="username">
年龄: <input type="text" name="age">
<input type="submit" value="提交">
</form>
</body>
</html>
表格
标签
| 标签 | 描述 |
|---|---|
| <table> | 定义表格整体 |
| <thead> | 用于定义表格头部(可选) |
| <tbody> | 定义表格中的主体部分(可选) |
| <tr> | 表格的行,可以包裹多个 <td> |
| <td> | 表格单元格(普通),可以包裹内容;如果是表头单元格,可以替换为 <th> |
演示
<body>
<table>
<thead>
<tr>
<th>姓名</th>
<th>性别</th>
<th>头像</th>
<th>职位</th>
<th>入职日期</th>
<th>最后操作时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>令狐冲</td>
<td>男</td>
<td><img src="https://via.placeholder.com/50" alt="令狐冲" class="avatar"></td>
<td>讲师</td>
<td>2021-03-15</td>
<td>2023-07-30T12:00:00Z</td>
<td class="btn-group">
<button>编辑</button>
<button>删除</button>
</td>
</tr>
<tr>
<td>任盈盈</td>
<td>女</td>
<td><img src="https://via.placeholder.com/50" alt="任盈盈" class="avatar"></td>
<td>学工主管</td>
<td>2020-04-10</td>
<td>2023-07-29T15:00:00Z</td>
<td class="btn-group">
<button>编辑</button>
<button>删除</button>
</td>
</tr>
</tbody>
</table>
</body>
</html>
CSS
什么是CSS
CSS: Cascading Style Sheet,层叠样式表,用于控制页面的样式(表现) 能够对网页中元素的位置进行像素级精准控制 控制: 能够控制元素的背景色,大小 字体的颜色,大小,以及字体类型等等
CSS引入方式
在CSS有3中引入方式
| 名称 | 语法描述 | 示例 |
|---|---|---|
| 行内样式 | 在标签内使用style属性,属性值是css属性键值对。 | <h1 style="xxx:xxx;">中国新闻网</h1> |
| 内部样式 | 定义<style>标签,在标签内部定义css样式。 | <style> h1 {...} </style> |
| 外部样式 | 定义<link>标签,通过href属性引入外部css文件 | <link rel="stylesheet" href="css/news.css"> |
对于上述3种引入方式,企业开发的使用情况如下:
-
内联样式会出现大量的代码冗余,不方便后期的维护,所以不常用。
-
内部样式,通过定义css选择器,让样式作用于当前页面的指定的标签上。
-
外部样式,html和css实现了完全的分离,企业开发常用方式。
注: 内嵌和外部样式可以定义在任意处,但建议定义在
<head>标签内部
CSS选择器
选择器是选取需设置样式的元素(标签) 通过选择器可以让内部和外部样式定义的CSS格式与对应标签匹配并发挥作用 通用语法: 各个样式之间用; 隔开
选择器名 {
css样式名:css样式值;
css样式名:css样式值;
}
常见选择器
| 选择器 | 写法 | 示例 | 示例说明 |
|---|---|---|---|
| 元素选择器 | 元素名称 {...} | h1 {...} | 选择页面上所有的<h1>标签 |
| 类选择器 | .class属性值 {...} | .cls {...} | 选择页面上所有class属性为cls的标签 |
| id选择器 | #id属性值 {...} | #hid {...} | 选择页面上id属性为hid的标签 |
| 分组选择器 | 选择器1,选择器2{...} | h1,h2 {...} | 选择页面上所有的<h1>和<h2>标签 |
| 属性选择器 | 元素名称[属性] {...} | input[type] {...} | 选择页面上有type属性的<input>标签 |
| 属性选择器 | 元素名称[属性名="值"] {...} | input[type="text"] {...} | 选择页面上type属性为text的<input>标签 |
| 后代选择器 | 元素1元素2{...} | form input {...} | 选择<form>标签内的所有<input>标签 |
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>【新思想引领新征程】推进长江十年禁渔 谱写长江大保护新篇章</title>
<!-- 2. 内部样式 -->
<style>
.publish-date {
color: #b2b2b2;
}
/* 设置超链接取消下划线效果 */
a {
text-decoration: none;
}
</style>
<!-- 3. 外部样式 -->
<!-- <link rel="stylesheet" href="css/news.css"> -->
</head>
<body>
<!-- 定义网页标题, 标题内容: 【新思想引领新征程】推进长江十年禁渔 谱写长江大保护新篇章 -->
<h1 id="title">【新思想引领新征程】推进长江十年禁渔 谱写长江大保护新篇章</h1>
<!-- 定义一个超链接, 链接地址:https://news.cctv.com/, 链接内容:央视网 -->
<a href="https://news.cctv.com/" target="_blank">央视网</a>
<!-- 1. 行内样式 -->
<!-- <span style="color: #b2b2b2;">2024年05月15日 20:07</span> -->
<span class="publish-date">2024年05月15日 20:07</span>
</body>
</html>
优先级
id选择器 > 类选择器 > 标签选择器
注
id属性必须是唯一的。 如果需要重复标识,使用class属性。 一个元素可以为多个类, class = "class1 class2 "
浮动
设置float: 来设置浮动类型,分为left左浮动和 right 右浮动 左浮动 将块依次移动到父元素左上角 右浮动 将块依次移动到父元素右上角
定位
position static 静态 absolute相对整个浏览器的位置,释放原来位置的占用 它的定位是相对于最近的已定位祖先元素(即 position 为 relative、absolute、fixed 或 sticky 的元素) relative 相对原来位置,不释放对原来位置的占用 fixed 相对浏览器窗口,释放对原来位置的占用 位置设置: left,right,top,bottom
盒子模型
介绍
-
盒子:页面中所有的元素(标签),都可以看做是一个 盒子,由盒子将页面中的元素包含在一个矩形区域内,通过盒子的视角更方便的进行页面布局。
-
盒子模型组成:内容区域(content)、内边距区域(padding)、边框区域(border)、外边距区域(margin)。 盒子的大小,其实就包括三个部分: border、padding、content,而margin外边距是不包括在盒子之内的
使用
通过css设置 用width,height设置盒子内容(contend)大小,若设置 box-sizing: border-box,可让其为整个盒子的大小(不包括margin) 宽高可分为 % 和 px 设置, 百分比是表示其占其父类标签的部分 宽高也可用auto设置,浏览器会自动设置宽高 每个部分最多可接受4个值,顺序为 上右下左
总结
JavaScript
基础
JavaScript(简称:JS) 是一门跨平台、面向对象的脚本语言,是用来控制网页行为的,实现人机交互效果。 JavaScript 和 Java 是完全不同的语言,不论是概念还是设计。但是基础语法类似。 是一种运行在浏览器端上的脚本语言 组成: ECMAScript: 规定了JS基础语法核心知识,包括变量、数据类型、流程控制、函数、对象等 BOM:浏览器对象模型,用于操作浏览器本身,如:页面弹窗、地址栏操作、关闭窗口等 DOM:文档对象模型,用于操作HTML文档,如:改变标签内的内容、改变标签内字体样式等
JS引入方式
方式一: 内部脚本,将JS代码定义在HTML页面中 JavaScript代码必须位于<script></script>标签之间 在HTML文档中,可以在任意地方,放置任意数量的<script></script> 一般会把脚本置于<body>元素的底部,可改善显示速度
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JS 引入方式</title>
</head>
<body>
<script>
alert('Hello JS')
</script>
</body>
</html>
方式二: 外部脚本, 将JS代码定义在外部 JS文件中,然后引入到 HTML页面 外部JS文件中,只包含JS代码,不包含<script>标签 引入外部js的<script>标签,必须是双标签 例:
<script src="js/demo.js"></script>
注: demo.js中只有js代码,没有<script>标签 通过<script></script>标签引入外部JS文件时,标签不能自闭合,如:<script src="js/demo.js" /> 一个html可以用多个<script>标签 一个<script>标签不能在引入外部js文件的同时定义内部js脚本
基础语法
js书写规范
结束符:每行js代码,结尾以分号结尾,而结尾的分号可有可无。(建议在一个项目中保持一致,要么全部都加,要么全部都不加) 注释:单行注释,多行注解的写法, 与java中一致
输出语句
| api | 描述 |
|---|---|
| window.alert(...) | 警告框 |
| document.write(...) | 在HTML 输出内容 |
| console.log(...) | 写入浏览器控制台 |
变量
JS中主要通过 let 关键字来声明变量的。 JS是一门弱类型语言,变量是可以存放不同类型的值的 变量名需要遵循如下规则: 组成字符可以是任何字母、数字、下划线(_)或美元符号($),且数字不能开头 变量名严格区分大小写,如:name和Name是不同的变量 不能使用关键字作为变量名,如:let、if、for等
| 关键字 | 解释 |
|---|---|
| var | 早期ECMAScript5中用于变量声明的关键字 |
| let | ECMAScript6中新增的用于变量声明的关键字,相比较var,let只在代码块内生效 |
| const | 声明常量的,常量一旦声明,不能修改 |
var声明的变量可以接受任何数据类型的值。并且var声明的变量的作用于是全局的 let的声明不能重复(也就是再次对同一个变量使用let修饰)。且let的变量是作用于局部的 也就是var在哪里声明都可以作用于整个script区域 而let声明只能作用于它所声明的代码块处
<script>
//变量
let a = 20;
a = "Hello";
alert(a);
//变量a既可以存数字,又可以存字符串。 因为JS是弱类型语言
// 在早期的JS中,声明变量还可以使用 var 关键字来声明|
//特点1 : 作用域比较大, 全局变量
//特点2 : 可以重复定义的
{
var x = 1;
var x = "A";
}
alert(x);
</script>
数据类型
JS中的数据类型分为 :原始数据类型 和 引用数据类型 还有 function类型
原始数据类型
| 数据类型 | 描述 |
|---|---|
| number | 数字(整数、小数、NaN(Not a Number)) |
| string | 字符串,单双引('...')、双引号("...")、反引号(...)皆可,正常使用推荐单引号 |
| boolean | 布尔。true,false |
| null | 对象为空。 JavaScript 是大小写敏感的,因此 null、Null、NULL是完全不同的 |
| undefined | 当声明的变量未初始化时,该变量的默认值是 undefined |
使用typeof 关键字可以返回变量的数据类型
<script>
//原始数据类型
alert(typeof 3); //number
alert(typeof 3.14); //number
alert(typeof "A"); //string
alert(typeof 'Hello');//string
alert(typeof true); //boolean
alert(typeof false);//boolean
alert(typeof null); //object
var a ;
alert(typeof a); //undefined
</script>
对于字符串类型的数据,除了可以使用双引号("...")、单引号('...')以外,还可以使用反引号 (``)。 字符串内可以单引号和双引号交替使用,已达到不用转义就显示效果, 如 '{"name" = littleD}' 而使用反引号引起来的字符串,也称为 模板字符串 模板字符串的使用场景:拼接字符串和变量。 模板字符串的语法: 反引号 内容拼接时,使用 ${ } 来引用变量
<script>
let name = 'Tom';
let age = 18;
console.log('大家好, 我是新入职的' + name + ', 今年' + age + '岁了, 请多多关照'); //原始方式 , 手动拼接字符串
console.log(`大家好, 我是新入职的${name}, 今年${age}岁了, 请多多关照`); //使用模板字符串方式拼接字符串
</script>
引用数据类型
同java一样,对变量赋值为new出来的对象就属于引用数据类型,其对变量的赋值是赋予地址值 引用数据类型为 Object
运算符
| 运算规则 | 运算符 |
|---|---|
| 算术运算符 | + , - , * , / , % , ++ , -- |
| 赋值运算符 | = , += , -= , *= , /= , %= |
| 比较运算符 | > , < , >= , <= , != , == , === 注意 == 会进行类型转换,=== 不会进行类型转换 |
| 逻辑运算符 | && , || , ! |
| 三元运算符 | 条件表达式 ? true_value: false_value |
| 位运算符 | 同java一样 |
==:只比较值是否相等,不区分数据类型,哪怕类型不一致,==也会自动转换类型进行值得比较 ===:不光比较值,还要比较类型,如果类型不一致,直接返回false
<script>
var age = 20;
var _age = "20";
var $age = 20;
alert(age == _age);//true ,只比较值
alert(age === _age);//false ,类型不一样
alert(age === $age);//true ,类型一样,值一样
</script>
函数
定义
方式一: 因为JavaScript是弱数据类型的语言,所以形参不需要声明类型 返回值也不需要声明类型,直接return即可 由于JS是弱类型语言,形参、返回值都不需要指定类型。在调用函数时,实参个数与形参个数可以不一致,但是建议一致 函数也可以作为参数传递
<script>
function 函数名(参数1,参数2..){
要执行的代码
}
//例子
function add(a, b){
return a + b;
}
let result = add(10,20);
alert(result);
//由于JS是弱类型语言,形参、返回值都不需要指定类型。在调用函数时,实参个数与形参个数可以不一致,但是建议一致
var result = add(10,20,30,40);
//函数的调用只需要名称正确即可,参数列表不管的。如上述案例,10传递给了变量a,20传递给了变量b,而30和40没有变量接受,但是不影响函数的正常调用
alert(result);
方式二: 匿名函数:是指一种没有名称的函数,由于它们没有名称,因此无法直接通过函数名来调用,而是通过变量或表达式来调用 匿名函数定义可以通过两种方式:函数表达式 和 箭头函数。
<script>
//函数表达式
var add = function (a,b){
return a + b;
}
//箭头函数
var add = (a,b) => {
return a + b;
}
自定义对象
注: 在定义对象内的方法时,若使用箭头函数,则内部this代表的是其父级对象,默认父级对象是Window
语法1 通过new Object()直接创建对象
var person =new Object();
// 给对象添加属性并赋值
person.name="张小明";
person.age=10;
person.foods=["苹果","橘子","香蕉","葡萄"];
// 给对象添加功能函数
person.eat= function (){
console.log(this.age+"岁的"+this.name+"喜欢吃:")
for(var i = 0;i<this.foods.length;i++){
console.log(this.foods[i])
}
}
//获得对象属性值
console.log(person.name)
console.log(person.age)
//调用对象方法
person.eat();
语法2 通过 {}形式创建对象
<script>
//定义格式
let 对象名 = {
属性名1: 属性值1,
//属性名可以用双引号包围(JSON格式)
"属性名2": 属性值2,
属性名3: 属性值3,
方法名称: function(形参列表){},
//方法可使用简化定义
方法名称(形参列表){}
};
//调用属性
对象名.属性名
//调用函数
对象名.方法名()
//例子
let user = {
name: "Tom",
age: 10,
gender: "男",
sing(){
console.log("悠悠的唱着最炫的民族风~");
}
}
console.log(user.name);
user.eat();
JSON
JSON对象:JavaScript Object Notation,JavaScript对象标记法。JSON是通过JavaScript标记法书写的文本。其格式如下
JSON格式
{
"key":value,
"key":value,
"key":value
}
<script>
//使用演示
let person = {
name: 'itcast',
age: 18,
gender: '男'
}
var personStr ='{"name":"张小明",
"age":20,
"girlFriend":{"name":"铁铃","age":23},
"foods":["苹果","香蕉","橘子","葡萄"],
"pets":[{"petName":"大黄","petType":"dog"},{"petName":"小花","petType":"cat"}]}'
alert(JSON.stringify(person)); //js对象 --> json字符串
let personJson = '{"name": "heima", "age": 18}';
alert(JSON.parse(personJson).name);//json字符串 --> js对象
其中,key必须使用引号并且是双引号标记,value可以是任意数据类型 各个属性之间用逗号隔开 JSON多用于作为数据载体,在网络中进行数据传输//在服务端象和浏览器端对象之间转换 注: js为了方便使用json的传输,提供了关于json的api JSON.stringify(...):作用就是将js对象,转换为json格式的字符串。 JSON.parse(...):作用就是将json格式的字符串,转为js对象。 JSON字符串一般用于传递数据,所以字符串中的函数就显得没有意义
流程控制
而JS中的流程控制语句与JAVA中的流程控制语句的作用,执行机制都是一样的
Array对象
Array对象时用来定义数组的。常用语法格式有如下2种:
//方式一
var 变量名 = new Array(元素列表);
//例
var arr = new Array(1,2,3,4); //1,2,3,4 是存储在数组中的数据(元素)
//方式二
var 变量名 = [ 元素列表 ];
//例
var arr = [1,2,3,4]; //1,2,3,4 是存储在数组中的数据(元素)
//使用
//定义数组
//若传递的参数是单个值,则是指定数组的长度
var arr = new Array(1,2,3,4);
var arr = [1,2,3,4];
//获取数组中的值,索引从0开始计数
console.log(arr[0]);
console.log(arr[1]);
//与java中不一样的是,JavaScript中数组相当于java中的集合,数组的长度是可以变化的。
// 而且JavaScript是弱数据类型的语言,所以数组中可以存储任意数据类型的值。
//特点: 长度可变 类型可变
var arr = [1,2,3,4];
arr[10] = 50;
console.log(arr[10]);
console.log(arr[9]);
console.log(arr[8]);
BOM对象
BOM的全称是Browser Object Model,翻译过来是浏览器对象模型。 也就是JavaScript将浏览器的各个组成部分封装成了对象。我们要操作浏览器的部分功能,可以通过操作BOM对象的相关属性或者函数来完成。 BOM中提供了如下5个对象:
| 对象名称 | 描述 |
|---|---|
| Window | 浏览器窗口对象 |
| Navigator | 浏览器对象 |
| Screen | 屏幕对象 |
| History | 历史记录对象 |
| Location | d地址栏对象 |
BOM编程的对象结构如下
-
window 顶级对象,代表整个浏览器窗口
-
location对象 window对象的属性之一,代表浏览器的地址栏
-
history对象 window对象的属性之一,代表浏览器的访问历史
-
screen对象 window对象的属性之一,代表屏幕
-
navigator对象 window对象的属性之一,代表浏览器软件本身
-
document对象 window对象的属性之一,代表浏览器窗口目前解析的html文档
-
console对象 window对象的属性之一,代表浏览器开发者工具的控制台
-
localStorage对象 window对象的属性之一,代表浏览器的本地数据持久化存储
-
sessionStorage对象 window对象的属性之一,代表浏览器的本地数据会话级存储
-
Window对象
window对象指的是浏览器窗口对象,是JavaScript的全部对象,所以对于window对象,我们可以直接使用, 并且对于window对象的方法和属性,我们可以省略window window对象提供了获取其他BOM对象的属性:
| 属性 | 描述 |
|---|---|
| history | 用于获取history对象 |
| location | 用于获取location对象 |
| Navigator | 用于获取Navigator对象 |
| Screen | 用于获取Screen对象 |
window也提供了一些常用的函数
| 函数 | 描述 |
|---|---|
| alert() | 显示带有一段消息和一个确认按钮的警告框。 |
| comfirm() | 显示带有一段消息以及确认按钮和取消按钮的对话框。 |
| setInterval() | 按照指定的周期(以毫秒计)来调用函数或计算表达式。 |
| setTimeout() | 在指定的毫秒数后调用函数或计算表达式。 |
Location对象
location是指代浏览器的地址栏对象,对于这个对象,我们常用的是href属性,用于获取或者设置浏览器的地址信息
//获取浏览器地址栏信息
alert(location.href);
//设置浏览器地址栏信息
location.href = "https://www.itcast.cn";
数据存储
-
会话级数据 : 内存型数据,是浏览器在内存上临时存储的数据,浏览器关闭后,数据失去,通过window的sessionStorge属性实现
-
持久级数据 : 磁盘型数据,是浏览器在磁盘上持久存储的数据,浏览器关闭后,数据仍在,通过window的localStorge实现
-
可以用于将来存储一些服务端响应回来的数据,比如:token令牌,或者一些其他功能数据,根据数据的业务范围我们可以选择数据存储的会话/持久 级别
<!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>
function saveItem(){
// 让浏览器存储一些会话级数据
window.sessionStorage.setItem("sessionMsg","sessionValue")
// 让浏览器存储一些持久级数据
window.localStorage.setItem("localMsg","localValue")
console.log("haha")
}
function removeItem(){
// 删除数据
sessionStorage.removeItem("sessionMsg")
localStorage.removeItem("localMsg")
}
function readItem(){
console.log("read")
// 读取数据
console.log("session:"+sessionStorage.getItem("sessionMsg"))
console.log("local:"+localStorage.getItem("localMsg"))
}
</script>
</head>
<body>
<button onclick="saveItem()">存储数据</button>
<button onclick="removeItem()">删除数据</button>
<button onclick="readItem()">读取数据</button>
</body>
</html>
注: 在开发者工具中的应用程序中的存储部分找到对应的IP即可查看存储数据
DOM对象
DOM:Document Object Model 文档对象模型。也就是 JavaScript 将 HTML 文档的各个组成部分封装为对象。 封装的对象分为 Document:整个文档对象 Element:元素对象 Attribute:属性对象 Text:文本对象 Comment:注释对象
DOM操作: 将网页的内容当做对象来处理,标签的所有属性在该对象上都可以找到,并且修改这个对象的属性,就会自动映射到标签身上 document对象:网页中所有内容都封装在document对象中 根据HTML代码结构特点,document对象本身是一种树形结构的文档对象。
dom树中节点的类型
-
node 节点,所有结点的父类型
-
element 元素节点,node的子类型之一,代表一个完整标签
-
attribute 属性节点,node的子类型之一,代表元素的属性
-
text 文本节点,node的子类型之一,代表双标签中间的文本
-
获取DOM元素
直接获取: 方式一: 根据CSS选择器来获取DOM元素,获取到匹配到的第一个元素:document.querySelector('CSS选择器'); 方式二: 根据CSS选择器来获取DOM元素,获取匹配到的所有元素:document.querySelectorAll('CSS选择器'); 获取到的所有元素,会封装到一个NodeList节点集合中,是一个伪数组(有长度、有索引的数组,但没有push、pop等数组方法) 处理元素: 可调用元素对象的api来对该元素进行操作 注: 在早期的JS中,我们也可以通过如下方法获取DOM元素 document.getElementById(...):根据id属性值获取,返回单个Element对象。 document.getElementsByTagName(...):根据标签名称获取,返回Element对象数组。 document.getElementsByName():根据name属性值获取,返回Element对象数组。 document.getElementsByClassName():根据class属性值获取,返回Element对象数组。 在用dom获取元素时,一般将<script>标签放在body内的底部,以保证获取元素成功 或者为body加上 onload 属性,在浏览器加载时执行方法 间接获取: 先获取父元素,然后用父元素获取子元素
| 功能 | API | 返回值 |
|---|---|---|
| 查找子标签 | element.children | 返回子标签数组 |
| 查找第一个子标签 | element.firstElementChild | 标签对象 |
| 查找最后一个子标签 | element.lastElementChild | 节点对象 |
通过子元素获取父元素 element.parentElement 获取相邻元素
| 功能 | API | 返回值 |
|---|---|---|
| 查找前一个兄弟标签 | node.previousElementSibling | 标签对象 |
| 查找后一个兄弟标签 | node.nextElementSibling | 标签对象 |
操作元素
操作属性: 元素.属性名 = "" 操作样式: 元素.style.样式名 = "" //原始样式名中的 - 替换为后一位字母的大写 操作文本: 元素.innerText = "" //不识别标签 元素.innerHtml = "" //识别标签(html代码) 增删元素:
| API | 功能 |
|---|---|
| document.createElement(“标签名”) | 创建元素节点并返回,但不会自动添加到文档中 |
| document.createTextNode(“文本值”) | 创建文本节点并返回,但不会自动添加到文档中 |
| element.appendChild(ele) | 将ele添加到element所有子节点后面 |
| parentEle.insertBefore(newEle,targetEle) | 将newEle插入到targetEle前面 |
| parentEle.replaceChild(newEle, oldEle) | 用新节点替换原有的旧子节点 |
| element.remove() | 删除某个标签 |
事件监听
事件
HTML事件是发生在HTML元素上的 “事情”,例如: 按钮被点击 鼠标移到元素上 输入框失去焦点 按下键盘按键...... 可以给这些事件绑定函数,当事件触发时,可以自动的完成对应的功能,这就是事件监听
事件监听语法
事件源.addEventListener('事件类型', 要执行的函数); 事件源: 哪个dom元素触发了事件, 要获取dom元素 事件类型: 用什么方式触发, 比如: 鼠标单击 click, 鼠标经过 mouseover 要执行的函数: 要做什么事
<body>
<input type="button" id="btn1" value="点我一下试试1">
<input type="button" id="btn2" value="点我一下试试2">
<script>
document.querySelector("#btn1").addEventListener('click', ()=>{
alert("按钮1被点击了...");
})
</script>
</body>
</html>
JavaScript对于事件的绑定还提供了另外2种方式(早期版本) 1). 通过html标签中的事件属性进行绑定
<input type="button" id="btn1" value="点我一下试试1" onclick="on()">
<script>
function on(){
alert('试试就试试')
}
</script>
2). 通过DOM中Element元素的事件属性进行绑定
<body>
<input type="button" id="btn1" value="点我一下试试1">
<script>
document.querySelector('#btn1').onclick = function(){
alert("按钮2被点击了...");
}
</script>
</body>
addEventListener 与 on事件 区别: on方式会被覆盖,addEventListener 方式可以绑定多次,拥有更多特性,推荐使用 addEventListener .
常见事件
<script>
//click: 鼠标点击事件
document.querySelector('#b2').addEventListener('click', () => {
console.log("我被点击了...");
})
//mouseenter: 鼠标移入
document.querySelector('#last').addEventListener('mouseenter', () => {
console.log("鼠标移入了...");
})
//mouseleave: 鼠标移出
document.querySelector('#last').addEventListener('mouseleave', () => {
console.log("鼠标移出了...");
})
//keydown: 某个键盘的键被按下
document.querySelector('#username').addEventListener('keydown', () => {
console.log("键盘被按下了...");
})
//keydown: 某个键盘的键被抬起
document.querySelector('#username').addEventListener('keyup', () => {
console.log("键盘被抬起了...");
})
//blur: 失去焦点事件
document.querySelector('#age').addEventListener('blur', () => {
console.log("失去焦点...");
})
//focus: 元素获得焦点
document.querySelector('#age').addEventListener('focus', () => {
console.log("获得焦点...");
})
//input: 用户输入时触发
document.querySelector('#age').addEventListener('input', () => {
console.log("用户输入时触发...");
})
//submit: 提交表单事件
document.querySelector('form').addEventListener('submit', () => {
alert("表单被提交了...");
})
</script>
JS模块化
在js文件中可以导入其他js文件,来实现代码复用
导入import
import { 导入的对象 } from '导入js的路径'
import { printLog } from './utils.js'
暴露export
export function printLog(msg){
}
修改类型
需要在引入script标签时,给其type属性值定为module
<script src="./js/eventDemo.js" type="module"></script>
vue2
Vue(读音 /vjuː /, 类似于 view),是一款用于构建用户界面的渐进式的JavaScript框架
引入vue
通过引入vue.js文件来引入vue
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue-快速入门</title>
<script src="js/vue.js"></script>
</head>
<body>
<div id="app">
<input type="text" v-model="message">
{{message}}
</div>
</body>
<script>
//定义Vue对象
new Vue({
el: "#app", //vue接管区域
data:{
message: "Hello Vue"
}
})
</script>
</html>
vue指令
| 指令 | 作用 |
|---|---|
| v-bind | 为HTML标签绑定属性值,如设置 href , css样式等 |
| v-model | 在表单元素上创建双向数据绑定 |
| v-on | 为HTML标签绑定事件 |
| v-if | 条件性的渲染某元素,判定为true时渲染,否则不渲染 |
| v-else | |
| v-else-if | |
| v-show | 根据条件展示某元素,区别在于切换的是display属性的值 |
| v-for | 列表渲染,遍历容器的元素或者对象的属性 |
详细查阅文档:
插值表达式
插值表达式是Vuejs中实现数据渲染到页面的一种手段,可以直接让数据从模型到视图,不需要dom操作, 大括号里面给我们提供了js语境,可以执行简单的js代码 插值表达式就是{{ }},如下操作,将模型变量中的属性直接放到插值表达式中可以实现数据渲染到页面的效果 插值表达式的js语境支持哪些js语法: 简单插值 字面量,number,string,boolean等 四则运算,+,-,*,/,%, (这里+可以做字符串拼接) 逻辑运算,&&,||,! 三目运算 全局函数 复杂插值 对象 数组 对象数组
<body>
<!--要让下面的东西在vue的管理区域才可以使用插值表达式-->
<!--插值表达式提供了js的语义环境,可以执行简单的js代码-->
<div id="box">
<h1>简单插值</h1>
<h2>字面量</h2>
<p>{{ number }}</p>
<p>{{ string }}</p>
<h2>四则运算</h2>
<p>{{ 1+1 }}</p>
<p>{{ 1-1 }}</p>
<p>{{ '鼠标'+'world' }}</p>
<h2>逻辑运算</h2>
<p>{{ !true }}</p>
<p>{{ false && true }}</p>
<p>{{ false || true }}</p>
<h2>三目运算</h2>
<p> {{ 20 > 18 ? '成年': '未成年' }} </p>
<h2>全局函数</h2>
<p>{{ Math.random() }}</p>
<p>{{ parseInt(10,2) }}</p>
<h1>复杂</h1>
<!--1,数组,2,对象,3,数组对象-->
<h2>数组</h2>
<ul>
<li v-for="item in arr">{{ item }}</li>
</ul>
<h2>对象</h2>
<ul>
<li>{{ obj.name }}</li>
<li>{{ obj.age }}</li>
</ul>
<h2>对象数组</h2>
<ul>
<li v-for="item in arr1">{{ item.name }}---{{ item.age}}</li>
</ul>
<h2>在 vuejs 如何实现HTML代码的正常展示</h2>
<div>{{ html1 }}</div>//这里的标签会被当成字符串,不会被解析
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var vm = new Vue({
el: "#box",
data: {
string:'abc',
number:111,
arr:[1,2,3,4],
obj:{
name:'lily',
age:'18'
},
arr1:[{name:'aa',age:18},{name:'bb',age:19}],
html1:`<ul> <li><a href="#">1</a></li> <li><a href="#">2</a></li> <li><a href="#">3</a></li> <li><a href="#">4</a></li> </ul>`
}
})
</script>
</body>
生命周期
vue的生命周期:指的是vue对象从创建到销毁的过程。 vue的生命周期包含8个阶段:每触发一个生命周期事件,会自动执行一个生命周期方法,这些生命周期方法也被称为钩子方法。 其完整的生命周期如下图所示:
| 状态 | 阶段周期 |
|---|---|
| beforeCreate | 创建前 |
| created | 创建后 |
| beforeMount | 挂载前 |
| mounted | 挂载完成 |
| beforeUpdate | 更新前 |
| updated | 更新后 |
| beforeDestroy | 销毁前 |
| destroyed | 销毁后 |
重点关注的是**mounted mounted:挂载完成,Vue初始化成功,HTML页面渲染成功
//定义Vue对象
new Vue({
el: "#app", //vue接管区域
data:{
},
methods: {
},
mounted () { //挂咱完成时执行内容
alert("vue挂载完成,发送请求到服务端")
}
})
vue3
引入vue
准备一个html文件,并在其中引入Vue模块 (参考官方文档,复制过来即可)【注意:模块化的js,引入时,需要设置 type="module"】
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue-快速入门</title>
</head>
<body>
<div id="app">
{{message}}
</div>
<script type="module">
import { createApp } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'
createApp({
data(){ //数据绑定
return {
message: 'Hello Vue'
}
}
method:{ //事件绑定
handle() {
}
}
//生命周期,钩子函数
mounted(){
}
}).mount('#app')
</script>
</body>
</html>
Vue中定义数据,必须通过data方法来定义,data方法返回值是一个对象,在这个对象中定义数据 插值表达式中编写的变量,一定是Vue中定义的数据,如果插值表达式中编写了一个变量,但是在Vue中未定义,将会报错 Vue应用实例接管的区域是 '#app',超出这个范围,就不受Vue控制了,所以vue的插值表达式,一定写在 <div id="app">...</div> 的里面
vue指令
指令:指的是HTML 标签上带有 v- 前缀的特殊属性,不同指令具有不同含义,可以实现不同的功能 。例如:v-if,v-for… 常见指令:
| 指令 | 作用 |
|---|---|
| v-for | 列表渲染,遍历容器的元素或者对象的属性 |
| v-bind | 为HTML标签绑定属性值,如设置 href , css样式等 |
| v-if/v-else-if/v-else | 条件性的渲染某元素,判定为true时渲染,否则不渲染 |
| v-show | 根据条件展示某元素,区别在于切换的是display属性的值 |
| v-model | 在表单元素上创建双向数据绑定 |
| v-on | 为HTML标签绑定事件 |
详细查阅文档:
Ajax
概述
Ajax: 全称Asynchronous JavaScript And XML,异步的JavaScript和XML。其作用有如下2点 与服务器进行数据交换:通过Ajax可以给服务器发送请求,并获取服务器响应的数据。 异步交互:可以在不重新加载整个页面的情况下,与服务器交换数据并更新部分网页的技术,如:搜索联想、用户名是否可用的校验等等 Axios: 由于使用原生的Ajax比较繁琐,可以使用更加简单的发送Ajax请求的技术Axios 。Axios是对原生的AJAX进行封装,简化书写
引入Axios
可以离线引用也可在线引用
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="js/axios-0.18.0.js"></script>
使用
使用例子
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Axios入门程序</title>
</head>
<body>
<button id="getData">GET</button>
<button id="postData">POST</button>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
//GET请求
document.querySelector('#getData').onclick = function() {
axios({
url:'https://mock.apifox.cn/m1/3083103-0-default/emps/list',
method:'get'
}).then(function(res) {
console.log(res.data);
}).catch(function(err) {
console.log(err);
})
}
//POST请求
document.querySelector('#postData').onclick = function() {
axios({
url:'https://mock.apifox.cn/m1/3083103-0-default/emps/update',
method:'post'
}).then(function(res) {
console.log(res.data);
}).catch(function(err) {
console.log(err);
})
}
</script>
</body>
</html>
Axios还针对不同的请求,提供了别名方式的api axios.请求方式(url [, data [, config]])
| 方法 | 描述 |
|---|---|
| axios.get(url [, config]) | 发送get请求 |
| axios.delete(url [, config]) | 发送delete请求 |
| axios.post(url [, data[, config]]) | 发送post请求 |
| axios.put(url [, data[, config]]) | 发送put请求 |
异步和同步
async、await可以让异步变为同步操作。async就是来声明一个异步方法,await是用来等待异步任务执行。
search() {
//基于axios发送异步请求,请求https://web-server.itheima.net/emps/list,根据条件查询员工列表
axios.get(`https://web-server.itheima.net/emps/list?name=${this.searchForm.name}&gender=${this.searchForm.gender}&job=${this.searchForm.job}`).then(res => {
this.empList = res.data.data
})
},
代码修改后:
async search() {
//基于axios发送异步请求,请求https://web-server.itheima.net/emps/list,根据条件查询员工列表
const result = await axios.get(`https://web-server.itheima.net/emps/list?name=${this.searchForm.name}&gender=${this.searchForm.gender}&job=${this.searchForm.job}`);
this.empList = result.data.data;
},
修改后,代码就变成同步操作了,一行一行的从前往后执行。 在前端项目开发中,经常使用这两个关键字配合,使得代码的可读性和可维护性变高 await关键字只在async函数内有效
文档查阅
https://www.w3school.com.cn/ VUE官方文档:https://cn.vuejs.org/guide/introduction.html Axios:
Maven
作用
依赖管理: 方便快捷的管理项目依赖的资源(jar包),避免版本冲突问题 项目构建: 通过Maven中的命令,就可以很方便的完成项目的编译(compile)、测试(test)、打包(package)、发布(deploy) 等操作 而且这些操作都是跨平台的,也就是说无论你是Windows系统,还是Linux系统,还是Mac系统,这些命令都是支持的 统一项目结构: 论我们使用的是什么开发工具,只要是基于maven构建的java项目,最终的目录结构都是相同的
Maven模型
-
项目对象模型 (Project Object Model)
-
依赖管理模型(Dependency)
-
构建生命周期/阶段(Build lifecycle & phases)
项目对象模型
将我们自己的项目抽象成一个对象模型,有自己专属的坐标
坐标,就是资源(jar包)的唯一标识,通过坐标可以定位到所需资源(jar包)位置。 坐标的组成部分: groupId: 组织名 arfitactId: 模块名 Version: 版本号
依赖管理模型
使用坐标来描述当前项目依赖哪些第三方jar包 当需要依赖其他jar包时,直接在pom文件中引入依赖的坐标即可 这个依赖对应的jar包其实就在我们本地电脑上的maven仓库中
构建生命周期/阶段
当我们需要编译,Maven提供了一个编译插件供我们使用;当我们需要打包,Maven就提供了一个打包插件供我们使用等。
安装
下载
下载地址:https://maven.apache.org/download.cgi 解压后目录结构: bin目录 : 存放的是可执行命令。(mvn 命令重点关注) conf目录 :存放Maven的配置文件。(settings.xml配置文件后期需要修改) lib目录 :存放Maven依赖的jar包。(Maven也是使用java开发的,所以它也依赖其他的jar包)
配置本地仓库
在自己计算机上新一个目录(本地仓库,用来存储jar包) 进入到conf目录下修改settings.xml配置文件 打开settings.xml文件,定位到<localRepository> 复制<localRepository>标签,粘贴到注释的外面 复制之前新建的用来存储jar包的路径,替换掉<localRepository>标签体内容
配置阿里云私服
由于中央仓库在国外,所以下载jar包速度可能比较慢,而阿里公司提供了一个远程仓库,里面基本也都有开源项目的jar包。 打开settings.xml文件,定位到<mirrors>标签 在<mirrors>标签下为其添加子标签<mirror>,内容如下:
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
配置环境变量
在系统变量处新建一个变量MAVEN_HOME。 MAVEN_HOME环境变量的值,设置为maven的解压安装目录 在Path中进行配置。 PATH环境变量的值,设置为:%MAVEN_HOME%\bin
配置关联的JDK版本(可选)
进入到conf目录下修改settings.xml配置文件,在 <profiles> </profiles>中增加如下配置:
<profile>
<id>jdk-17</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>17</jdk>
</activation>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.compilerVersion>17</maven.compiler.compilerVersion>
</properties>
</profile>
IDEA中使用
全局设置
进入IDEA的欢迎页面, 选择 IDEA中 File => close project => Customize => All settings 打开 All settings , 选择 Build,Execution,Deployment => Build Tools => Maven Maven home path处配置Maven的安装目录 User setting file 处配置Maven的setting.xml路径 Local repository 处配置Maven的仓库路径 配置工程的编译版本为对应java版本
创建项目
创建一个空项目,命名为 web-project01 创建好项目之后,进入项目中,要设置JDK的版本号。选择 Project Structure 创建模块,选择Java语言,选择Maven。 填写模块的基本信息(也就是Maven坐标)
Maven项目的结构
|--- 模块 |--- src (源代码目录和测试代码目录)
|--- main (源代码目录)
|--- java (源代码java文件目录)
|--- resources (源代码配置文件目录)
|--- test (测试代码目录)
|--- java (测试代码java目录)
|--- resources (测试代码配置文件目录)
|--- target (编译、打包生成文件存放目录)
导入Maven项目
方式一: File -> Project Structure -> Modules -> Import Module -> 选择maven项目的pom.xml。 方式二: Maven面板 -> +(Add Maven Projects) -> 选择maven项目的pom.xml。 如果没有Maven面板,选择 View => Appearance => Tool Window Bars
Maven仓库
仓库:用于存储资源,管理各种jar包 仓库的本质就是一个目录(文件夹),这个目录被用来存储开发中所有依赖(就是jar包)和插件
Maven仓库分为 本地仓库:自己计算机上的一个目录(用来存储jar包) 中央仓库:由Maven团队维护的全球唯一的。仓库地址:https://repo1.maven.org/maven2 远程仓库(私服):一般由公司团队搭建的私有仓库
当项目中使用坐标引入对应依赖jar包后 首先会查找本地仓库中是否有对应的jar包 如果有,则在项目直接引用 如果没有,则去中央仓库中下载对应的jar包到本地仓库 如果还可以搭建远程仓库(私服),将来jar包的查找顺序则变为: 本地仓库 --> 远程仓库--> 中央仓库
POM文件
POM (Project Object Model) :指的是项目对象模型,用来描述当前的maven项目。 使用pom.xml文件来描述当前项目。 pom.xml文件如下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<!-- POM模型版本 -->
<modelVersion>4.0.0</modelVersion>
<!-- 当前项目坐标 -->
<groupId>com.itheima</groupId>
<artifactId>maven-project01</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- 项目的JDK版本及编码 -->
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!-- 打包方式 -->
<packaging>jar</packaging>
</project>
标签作用
<project> :pom文件的根标签,表示当前maven项目 <modelVersion>:声明项目描述遵循哪一个POM模型版本 虽然模型本身的版本很少改变,但它仍然是必不可少的。目前POM模型版本是4.0.0 坐标 <groupId> <artifactId> <version> 定位项目在本地仓库中的位置,由以上三个标签组成一个坐标 <maven.compiler.source> :编译JDK的版本 <maven.compiler.target> :运行JDK的版本 <project.build.sourceEncoding> : 设置项目的字符集 <packaging>:maven项目的打包方式,通常设置为jar或war(默认值:jar)
Maven坐标
坐标: Maven中的坐标是资源的唯一标识 , 通过该坐标可以唯一定位资源位置 资源可以是插件、依赖、当前项目 使用坐标来定义项目或引入项目中需要的依赖 Maven坐标主要组成: groupId:定义当前Maven项目隶属组织名称(通常是域名反写,例如:com.itheima) artifactId:定义当前Maven项目名称(通常是模块名称,例如 order-service、goods-service) version:定义当前项目版本号 SNAPSHOT: 功能不稳定、尚处于开发中的版本,即快照版本 RELEASE: 功能趋于稳定、当前更新停止,可以用于发行的版本
依赖管理
引入依赖
依赖:指当前项目运行所需要的jar包。一个项目中可以引入多个依赖 例如:在当前工程中,我们需要用到logback来记录日志,此时就可以在maven工程的pom.xml文件中,引入logback的依赖。具体步骤如下 在pom.xml中编写<dependencies>标签 在<dependencies>标签中使用<dependency>引入坐标 定义坐标的 groupId、artifactId、version 点击刷新按钮,引入最新加入的坐标
<dependencies>
<!-- 依赖 : spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.1.4</version>
</dependency>
</dependencies>
注: 可通过install插件将自己编写的Maven模块安装到本地仓库
依赖传递
所谓maven的依赖传递,指的就是如果在maven项目中,A 依赖了B,B依赖了C,C依赖了D,那么在A项目中,也会有C、D依赖,因为依赖会传递。 只有依赖范围是compile的依赖才会被传递//但父子工程依然会继承下来 排除依赖: 如果,传递下来的依赖,在项目开发中,我们确实不需要,此时,我们可以通过Maven中的排除依赖功能,来将这个依赖排除掉。 排除依赖:指主动断开依赖的资源,被排除的资源无需指定版本 这里的排除依赖是定义在该依赖里面的,作用是排除该依赖传递下来的某个依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.1.4</version>
<!--排除依赖, 主动断开依赖的资源-->
<exclusions>
<exclusion>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-observation</artifactId>
</exclusion>
</exclusions>
</dependency>
依赖范围
依赖的jar包,默认情况下,可以在任何地方使用,在main目录下,可以使用;在test目录下,也可以使用。 在maven中,如果希望限制依赖的使用范围,可以通过 <scope>…</scope> 设置其作用范围。 作用范围: 主程序范围有效。(main文件夹范围内) 测试程序范围有效。(test文件夹范围内) 是否参与打包运行。(package指令范围内) scope标签的取值范围:
| scope值 | 主程序 | 测试程序 | 打包(运行) | 范例 |
|---|---|---|---|---|
| compile(默认) | Y | Y | Y | log4j |
| test | - | Y | - | junit |
| provided | Y | Y | - | servlet-api |
| runtime | - | Y | Y | jdbc驱动 |
依赖冲突
依赖冲突 引入的依赖出现版本号不同的依赖 Maven自动处理: 最短路径优先:文件层级越高的优先,低的自动被忽略不被引入 若路径层级相同,则在pom中路径靠前的依赖优先使用
生命周期
介绍
Maven的生命周期就是为了对所有的构建过程进行抽象和统一。 描述了一次项目构建,经历哪些阶段。 Maven从大量项目和构建工具中学习和反思,然后总结了一套高度完美的,易扩展的项目构建生命周期。 这个生命周期包含了项目的清理,初始化,编译,测试,打包,集成测试,验证,部署和站点生成等几乎所有构建步骤。
Maven对项目构建的生命周期划分为3套(相互独立): clean:清理工作。 default:核心工作。如:编译、测试、打包、安装、部署等。 site:生成报告、发布站点等。 每套生命周期包含一些阶段(phase),阶段是有顺序的,后面的阶段依赖于前面的阶段。 常用阶段: clean:移除上一次构建生成的文件 compile:编译项目源代码 test:使用合适的单元测试框架运行测试(junit) package:将编译后的文件打包,如:jar、war等 install:安装项目到本地仓库 Maven的生命周期是抽象的,这意味着生命周期本身不做任何实际工作。在Maven的设计中,实际任务(如源代码编译)都交由插件来完成。 IDEA工具为了方便程序员使用maven生命周期,在右侧的maven工具栏中,已给出快速访问通道。
生命周期的顺序是:clean --> validate --> compile --> test --> package --> verify --> install --> site --> deploy 我们需要关注的就是:clean --> compile --> test --> package --> install 在同一套生命周期中,我们在执行后面的生命周期时,前面的生命周期都会执行。
执行
有两种执行方式: 在idea工具右侧的maven工具栏中,选择对应的生命周期,双击执行 在DOS命令行中,通过maven命令执行
单元测试
测试
是一种用来促进鉴定软件的正确性、完整性、安全性和质量的过程。 阶段划分:单元测试、集成测试、系统测试、验收测试。 测试方法:白盒测试、黑盒测试 及 灰盒测试。
Junit
单元测试: 就是针对最小的功能单元(方法),编写测试代码对其正确性进行测试。 JUnit: 最流行的Java测试框架之一,提供了一些功能,方便程序进行单元测试(第三方公司提供)。
使用
在pom.xml中,引入JUnit的依赖。
<!--Junit单元测试依赖-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.9.1</version>
<scope>test</scope>
</dependency>
在test/java目录下,创建测试类,并编写对应的测试方法,并在方法上声明@Test注解
@Test
public void testGetAge(){
Integer age = new UserService().getAge("110002200505091218");
System.out.println(age);
}
运行单元测试 (测试通过:绿色;测试失败:红色)。
断言
JUnit提供了一些辅助方法,用来帮我们确定被测试的方法是否按照预期的效果正常工作,这种方式称为断言。
| 断言方法 | 描述 |
|---|---|
| assertEquals(Object exp, Object act, String msg) | 检查两个值是否相等,不相等就报错。 |
| assertNotEquals(Object unexp, Object act, String msg) | 检查两个值是否不相等,相等就报错。 |
| assertNull(Object act, String msg) | 检查对象是否为null,不为null,就报错。 |
| assertNotNull(Object act, String msg) | 检查对象是否不为null,为null,就报错。 |
| assertTrue(boolean condition, String msg) | 检查条件是否为true,不为true,就报错。 |
| assertFalse(boolean condition, String msg) | 检查条件是否为false,不为false,就报错。 |
| assertSame(Object exp, Object act, String msg) | 检查两个对象引用是否相等,不相等,就报错。 |
常见注解
在JUnit中还提供了一些注解,还增强其功能,常见的注解有以下几个:
| 注解 | 说明 | 备注 |
|---|---|---|
| @Test | 测试类中的方法用它修饰才能成为测试方法,才能启动执行 | 单元测试 |
| @BeforeEach | 用来修饰一个实例方法,该方法会在每一个测试方法执行之前执行一次。 | 初始化资源(准备工作) |
| @AfterEach | 用来修饰一个实例方法,该方法会在每一个测试方法执行之后执行一次。 | 释放资源(清理工作) |
| @BeforeAll | 用来修饰一个静态方法,该方法会在所有测试方法之前只执行一次。 | 初始化资源(准备工作) |
| @AfterAll | 用来修饰一个静态方法,该方法会在所有测试方法之后只执行一次。 | 释放资源(清理工作) |
| @ParameterizedTest | 参数化测试的注解 (可以让单个测试运行多次,每次运行时仅参数不同) | 用了该注解,就不需要@Test注解了 |
| @ValueSource | 参数化测试的参数来源,赋予测试方法参数 | 与参数化测试注解配合使用 |
| @DisplayName | 指定测试类、测试方法显示的名称 (默认为类名、方法名) |
常见问题
更新依赖索引
有时候给idea配置完maven仓库信息后,在idea中依然搜索不到仓库中的jar包。 这是因为仓库中的jar包索引尚未更新到idea中。这个时候我们就需要更新idea中maven的索引了,具体做法如下: 打开设置----搜索maven----Repositories----选中本地仓库-----点击Update
清理maven仓库
由于网络原因,依赖没有下载完整导致的,在maven仓库中生成了xxx.lastUpdated文件,该文件不删除,不会再重新下载。 解决方案: 根据maven依赖的坐标,找到仓库中对应的 xxx.lastUpdated 文件,删除,删除之后重新加载项目即可 重新加载依赖,依赖下载了之后,maven面板可能还会报红,此时可以关闭IDEA,重新打开IDEA加载此项目即可。
配置文件
XML
XML是EXtensible Markup Language的缩写,翻译过来就是可扩展标记语言。 所以很明显,XML和HTML一样都是标记语言,也就是说它们的基本语法都是标签。
-
可扩展 三个字表面上的意思是XML允许自定义格式。但这不代表你可以随便写。
-
在XML基本语法规范的基础上,你使用的那些第三方应用程序、框架会通过XML约束的方式强制规定配置文件中可以写什么和怎么写
-
XML基本语法这个知识点的定位是:我们不需要从零开始,从头到尾的一行一行编写XML文档,而是在第三方应用程序、框架已提供的配置文件的基础上修改。要改成什么样取决于你的需求,而怎么改取决XML基本语法和具体的XML约束。
基本语法
XML的基本语法和HTML的基本语法简直如出一辙。其实这不是偶然的,XML基本语法+HTML约束=HTML语法。在逻辑上HTML确实是XML的子集 XML文档声明 这部分基本上就是固定格式,要注意的是文档声明一定要从第一行第一列开始写
<?xml version="1.0" encoding="UTF-8"?>
-
根标签
-
根标签有且只能有一个。
-
-
标签关闭
-
双标签:开始标签和结束标签必须成对出现。
-
单标签:单标签在标签内关闭。
-
-
标签嵌套
-
可以嵌套,但是不能交叉嵌套。
-
-
注释不能嵌套
-
标签名、属性名建议使用小写字母
-
属性
-
属性必须有值
-
属性值必须加引号,单双都行
-
约束
将来我们主要就是根据XML约束中的规定来编写XML配置文件,而且会在我们编写XML的时候根据约束来提示我们编写, 而XML约束主要包括DTD和Schema两种。
Schema约束要求我们一个XML文档中,所有标签,所有属性都必须在约束中有明确的定义。 引入约束
<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">
DOM4J进行XML解析
DOM4J的使用步骤
-
导入jar包 dom4j.jar
-
创建解析器对象(SAXReader)
-
解析xml 获得Document对象
-
获取根节点RootElement
-
获取根节点下的子节点
1.2.2 DOM4J的API介绍
1.创建SAXReader对象
SAXReader saxReader = new SAXReader();
2. 解析XML获取Document对象: 需要传入要解析的XML文件的字节输入流
Document document = reader.read(inputStream);
3. 获取文档的根标签
Element rootElement = documen.getRootElement()
4. 获取标签的子标签
//获取所有子标签
List<Element> sonElementList = rootElement.elements();
//获取指定标签名的子标签
List<Element> sonElementList = rootElement.elements("标签名");
5. 获取标签体内的文本
String text = element.getText();
6. 获取标签的某个属性的值
String value = element.attributeValue("属性名");
Tomcat
介绍
Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目, 由Apache、Sun 和其他一些公司及个人共同开发而成。最新的Servlet 和JSP 规范总是能在Tomcat 中得到体现, 因为Tomcat 技术先进、性能稳定,而且免费,因而深受Java 爱好者的喜爱并得到了部分软件开发商的认可,成为目前比较流行的Web 应用服务器。
安装
下载
-
Tomcat官方网站:
-
安装版:需要安装,一般不考虑使用。
-
解压版: 直接解压缩使用,我们使用的版本。
Tomcat是由java开发的,因此需要注意适合的JDK版本以及java环境变量配置
点击bin/startup.bat启动Tomcat 默认开启端口号为8080 直接关闭窗口或者运行 bin/shutdown.bat关闭tomcat 处理dos窗口日志中文乱码问题: 修改conf/logging.properties,将所有的UTF-8修改为GBK
目录
-
bin:该目录下存放的是二进制可执行文件,如果是安装版,那么这个目录下会有两个exe文件:tomcat10.exe、tomcat10w.exe,前者是在控制台下启动Tomcat,后者是弹出GUI窗口启动Tomcat;如果是解压版,那么会有startup.bat和shutdown.bat文件,startup.bat用来启动Tomcat,但需要先配置JAVA_HOME环境变量才能启动,shutdawn.bat用来停止Tomcat;
-
conf:这是一个非常非常重要的目录,这个目录下有四个最为重要的文件:
-
server.xml:配置整个服务器信息。例如修改端口号。默认HTTP请求的端口号是:8080
-
tomcat-users.xml:存储tomcat用户的文件,这里保存的是tomcat的用户名及密码,以及用户的角色信息。可以按着该文件中的注释信息添加tomcat用户,然后就可以在Tomcat主页中进入Tomcat Manager页面了;
<tomcat-users xmlns="http://tomcat.apache.org/xml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd"
version="1.0">
<role rolename="admin-gui"/>
<role rolename="admin-script"/>
<role rolename="manager-gui"/>
<role rolename="manager-script"/>
<role rolename="manager-jmx"/>
<role rolename="manager-status"/>
<user username="admin"
password="admin"
roles="admin-gui,admin-script,manager-gui,manager-script,manager-jmx,manager-status"
/>
</tomcat-users>web.xml:部署描述符文件,这个文件中注册了很多MIME类型,即文档类型。这些MIME类型是客户端与服务器之间说明文档类型的,如用户请求一个html网页,那么服务器还会告诉客户端浏览器响应的文档是text/html类型的,这就是一个MIME类型。客户端浏览器通过这个MIME类型就知道如何处理它了。当然是在浏览器中显示这个html文件了。但如果服务器响应的是一个exe文件,那么浏览器就不可能显示它,而是应该弹出下载窗口才对。MIME就是用来说明文档的内容是什么类型的!
-
context.xml:对所有应用的统一配置,通常我们不会去配置它。
-
-
lib:Tomcat的类库,里面是一大堆jar文件。如果需要添加Tomcat依赖的jar文件,可以把它放到这个目录中,当然也可以把应用依赖的jar文件放到这个目录中,这个目录中的jar所有项目都可以共享之,但这样你的应用放到其他Tomcat下时就不能再共享这个目录下的jar包了,所以建议只把Tomcat需要的jar包放到这个目录下;
-
logs:这个目录中都是日志文件,记录了Tomcat启动和关闭的信息,如果启动Tomcat时有错误,那么异常也会记录在日志文件中。
-
temp:存放Tomcat的临时文件,这个目录下的东西可以在停止Tomcat后删除!
-
webapps:存放web项目的目录,其中每个文件夹都是一个项目;如果这个目录下已经存在了目录,那么都是tomcat自带的项目。其中ROOT是一个特殊的项目,在地址栏中访问:http://127.0.0.1:8080,没有给出项目目录时,对应的就是ROOT项目.http://localhost:8080/examples,进入示例项目。其中examples"就是项目名,即文件夹的名字。 在地址栏访问时,指定的路径和项目部署目录不同时,有时也可以访问(需要特定配置)
-
work:运行时生成的文件,最终运行的文件都在这里。通过webapps中的项目生成的!可以把这个目录下的内容删除,再次运行时会生再次生成work目录。当客户端用户访问一个JSP文件时,Tomcat会通过JSP生成Java文件,然后再编译Java文件生成class文件,生成的java和class文件都会存放到这个目录下。
-
LICENSE:许可证。
-
NOTICE:说明文件。
WEB项目的结构
web项目存放于webapps中
-
app 本应用根目录
-
static 非必要目录,约定俗成的名字,一般在此处放静态资源 ( css js img)
-
WEB-INF 必要目录,必须叫WEB-INF,受保护的资源目录,浏览器通过url不可以直接访问的目录
-
classes 必要目录,src下源代码,配置文件,编译后会在该目录下,web项目中如果没有源码,则该目录不会出现
-
lib 必要目录,项目依赖的jar编译后会出现在该目录下,web项目要是没有依赖任何jar,则该目录不会出现
-
web.xml 必要文件,web项目的基本配置文件. 较新的版本中可以没有该文件,但是学习过程中还是需要该文件
-
-
index.html 非必要文件,index.html/index.htm/index.jsp为默认的欢迎页
-
WEB项目部署
方式1 直接将编译好的项目放在webapps目录下 方式2 将编译好的项目打成war包放在webapps目录下,tomcat启动后会自动解压war包 方式3 可以将项目放在非webapps的其他目录下,在tomcat中通过配置文件指向app的实际磁盘路径
方式3: 在磁盘的自定义目录上准备一个app 在tomcat的conf下创建Catalina/localhost目录,并在该目录下准备一个app.xml文件
<!--
path: 项目的访问路径,也是项目的上下文路径,就是在浏览器中,输入的项目名称
docBase: 项目在磁盘中的实际路径
-->
<Context path="/app" docBase="D:\mywebapps\app" />
启动tomcat访问测试即可 注: 这里xml文件的名称以及path路径的名称都要相同, 项目文件的名称可以不同
路径设置
修改为自定义路径访问
如果你希望使用自定义路径(如 /mycustompath),可以通过以下方法实现。
方法 1:重命名 WAR 文件
-
将你的 WAR 文件重命名为你想要的路径名称。例如,将
myapp.war重命名为mycustompath.war。 -
将
mycustompath.war放到 Tomcat 的webapps目录下。 -
启动 Tomcat,应用会自动部署,并通过
http://localhost:8080/mycustompath访问。
方法 2:修改 server.xml
-
打开 Tomcat 的
conf/server.xml文件。 -
在
<Host>标签内添加以下内容:xml
复制
<Context path="/mycustompath" docBase="myapp" reloadable="true" />
运行 HTML
-
path="/mycustompath"是你希望使用的自定义路径。 -
docBase="myapp"是你的应用名称(可以是 WAR 文件名或解压后的目录名)。
-
-
保存文件并重启 Tomcat。
方法 3:使用上下文配置文件
-
在 Tomcat 的
conf/Catalina/localhost/目录下创建一个 XML 文件,文件名是你的自定义路径。例如,创建mycustompath.xml。 -
在文件中添加以下内容:
xml
复制
<Context docBase="/path/to/your/myapp" reloadable="true" />
运行 HTML
-
docBase是你的应用路径(可以是 WAR 文件或解压后的目录的绝对路径)。
-
-
保存文件并重启 Tomcat。
-
应用会通过
http://localhost:8080/mycustompath访问。
IDEA内使用
关联Tomcat
可以在创建项目前设置本地tomcat,也可以在打开某个项目的状态下找到settings 找到 Build,Execution,Eeployment下的Application Servers ,找到+号 选择Tomcat Server 选择tomcat的安装目录
创建WEB项目
为项目添加Tomcat依赖 module => 对应模块 => Dependencies => 点+号 ,Library 选择Tomcat 回到文件目录(坐标那个一个长条的)选择modules,添加 framework support 选择Web Application 注意Version,勾选 Create web.xml 删除index.jsp ,替换为 index.html 处理配置文件 在工程下创建resources目录,专门用于存放配置文件(都放在src下也行,单独存放可以尽量避免文件集中存放造成的混乱) 标记目录为资源目录,不标记的话则该目录不参与编译(Resouces Root) 处理依赖jar包问题 在WEB-INF下创建lib目录 复制jar文件进入lib目录 将lib目录添加为当前项目的依赖,后续可以用maven统一解决 环境级别推荐选择module 级别,降低对其他项目的影响,name可以空着不写
运行WEB项目
检查idea是否识别modules为web项目并存在将项目构建成发布结构的配置 就是检查工程目录下,web目录有没有特殊的识别标记 以及artifacts下,有没有对应 _war_exploded,如果没有,就点击+号添加 使用build构建或者直接idea运行tomcat IDEA运行: 点击向下箭头,出现 Edit Configurations选项
出现运行配置界面 点击+号,添加本地tomcat服务器 选择Deployment,通过+添加要部署到Tomcat中的artifact applicationContext中是默认的项目上下文路径,也就是url中需要输入的路径, 这里可以自己定义,可以和工程名称不一样,也可以不写,但是要保留/,我们这里暂时就用默认的 直接启动模块
原理
-
idea并没有直接进将编译好的项目放入tomcat的webapps中
-
idea根据关联的tomcat,创建了一个tomcat副本,将项目部署到了这个副本中
-
idea的tomcat副本在C:\用户\当前用户\AppData\Local\JetBrains\IntelliJIdea2022.2\tomcat\中
-
idea的tomcat副本并不是一个完整的tomcat,副本里只是准备了和当前项目相关的配置文件而已
-
idea启动tomcat时,是让本地tomcat程序按照tomcat副本里的配置文件运行
-
idea的tomcat副本部署项目的模式是通过conf/Catalina/localhost/*.xml配置文件的形式实现项目部署的
JDBC
概念
-
JDBC:Java Database Connectivity,意为Java数据库连接。
-
JDBC是Java提供的一组独立于任何数据库管理系统的API。
-
Java提供接口规范,由各个数据库厂商提供接口的实现,厂商提供的实现类封装成jar文件,也就是我们俗称的数据库驱动jar包。
-
学习JDBC,充分体现了面向接口编程的好处,程序员只关心标准和规范,而无需关注实现过程。
核心组成
-
接口规范:
-
为了项目代码的可移植性,可维护性,SUN公司从最初就制定了Java程序连接各种数据库的统一接口规范。这样的话,不管是连接哪一种DBMS软件,Java代码可以保持一致性。
-
接口存储在java.sql和javax.sql包下。
-
-
实现规范:
-
因为各个数据库厂商的DBMS软件各有不同,那么各自的内部如何通过SQL实现增、删、改、查等操作管理数据,只有这个数据库厂商自己更清楚,因此把接口规范的实现交给各个数据库厂商自己实现。
-
厂商将实现内容和过程封装成jar文件,我们程序员只需要将jar文件引入到项目中集成即可,就可以开发调用实现过程操作数据库了。
-
搭建步骤
-
注册驱动【依赖的驱动类,进行安装】
-
获取连接【Connection建立连接】
-
创建发送SQL语句对象【Connection创建发送SQL语句的Statement】
-
发送SQL语句,并获取返回结果【Statement 发送sql语句到数据库并且取得返回结果】
-
结果集解析【结果集解析,将查询结果解析出来】
-
资源关闭【释放ResultSet、Statement 、Connection】
public class JdbcQuick {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取数据库连接
// ("jdbc:mysql://IP地址:端口号/内部数据库名称", "数据库用户名", "密码");
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/atguigu", "root", "atguigu");
//3.创建Statement对象
PreparedStatement preparedStatement = connection.prepareStatement("select emp_id,emp_name,emp_salary,emp_age from t_emp");
//4.编写SQL语句并执行,获取结果
ResultSet resultSet = preparedStatement.executeQuery();
//5.处理结果
while (resultSet.next()) {
int empId = resultSet.getInt("emp_id");
String empName = resultSet.getString("emp_name");
String empSalary = resultSet.getString("emp_salary");
int empAge = resultSet.getInt("emp_age");
System.out.println(empId + "\t" + empName + "\t" + empSalary + "\t" + empAge);
}
//6.释放资源(先开后关原则)
resultSet.close();
preparedStatement.close();
connection.close();
}
}
核心API
驱动注册
-
Class.forName("com.mysql.cj.jdbc.Driver"); -
在 Java 中,当使用 JDBC(Java Database Connectivity)连接数据库时,需要加载数据库特定的驱动程序,以便与数据库进行通信。加载驱动程序的目的是为了注册驱动程序,使得 JDBC API 能够识别并与特定的数据库进行交互。
-
从JDK6开始,不再需要显式地调用
Class.forName()来加载 JDBC 驱动程序,只要在类路径中集成了对应的jar文件,会自动在初始化时注册驱动程序。
Connection
-
Connection接口是JDBC API的重要接口,用于建立与数据库的通信通道。换而言之,Connection对象不为空,则代表一次数据库连接。
-
在建立连接时,需要指定数据库URL、用户名、密码参数。
-
URL:jdbc:mysql://localhost:3306/atguigu
-
jdbc:mysql://IP地址:端口号/数据库名称?参数键值对1&参数键值对2
-
-
-
Connection接口还负责管理事务,Connection接口提供了commit和rollback方法,用于提交事务和回滚事务。 -
可以创建
Statement对象,用于执行 SQL 语句并与数据库进行交互。 -
在使用JDBC技术时,必须要先获取Connection对象,在使用完毕后,要释放资源,避免资源占用浪费及泄漏。
Statement
-
Statement接口用于执行 SQL 语句并与数据库进行交互。它是 JDBC API 中的一个重要接口。通过Statement对象,可以向数据库发送 SQL 语句并获取执行结果。 -
结果可以是一个或多个结果。
-
增删改:受影响行数单个结果。
-
查询:单行单列、多行多列、单行多列等结果。
-
-
但是
Statement接口在执行SQL语句时,会产生SQL注入攻击问题:-
当使用
Statement执行动态构建的 SQL 查询时,往往需要将查询条件与 SQL 语句拼接在一起,直接将参数和SQL语句一并生成,让SQL的查询条件始终为true得到结果。
-
PreparedStatement
-
PreparedStatement是Statement接口的子接口,用于执行预编译的 SQL 查询,作用如下:-
预编译SQL语句:在创建PreparedStatement时,就会预编译SQL语句,也就是SQL语句已经固定。
-
防止SQL注入:
PreparedStatement支持参数化查询,将数据作为参数传递到SQL语句中,采用?占位符的方式,将传入的参数用一对单引号包裹起来'',无论传递什么都作为值。有效防止传入关键字或值导致SQL注入问题。 -
性能提升:PreparedStatement是预编译SQL语句,同一SQL语句多次执行的情况下,可以复用,不必每次重新编译和解析。
-
-
后续的学习我们都是基于PreparedStatement进行实现,更安全、效率更高!
@Test
public void insert() throws SQLException {
//1.注册驱动
// Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取数据库连接
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/atguigu","root", "atguigu");
//3.创建Statement对象
PreparedStatement preparedStatement = connection.prepareStatement("insert into t_emp (emp_name,emp_salary,emp_age)values (?, ?,?)");
//4.为占位符赋值,索引从1开始,编写SQL语句并执行,获取结果
preparedStatement.setString(1,"rose");
preparedStatement.setDouble(2,666.66);
preparedStatement.setDouble(3,28);
int result = preparedStatement.executeUpdate();
//5.处理结果
if(result>0){
System.out.println("添加成功");
}else{
System.out.println("添加失败");
}
//6.释放资源(先开后关原则)
preparedStatement.close();
connection.close();
}
CURD
查询:
//3.创建Statement对象
PreparedStatement preparedStatement = connection.prepareStatement("select emp_id,emp_name,emp_salary,emp_age from t_emp");
//4.编写SQL语句并执行,获取结果
ResultSet resultSet = preparedStatement.executeQuery();
新增:
//3.创建Statement对象
PreparedStatement preparedStatement = connection.prepareStatement("insert into t_emp (emp_name,emp_salary,emp_age)values (?, ?,?)");
//4.为占位符赋值,索引从1开始,编写SQL语句并执行,获取结果
preparedStatement.setString(1,"rose");
preparedStatement.setDouble(2,666.66);
preparedStatement.setDouble(3,28);
int result = preparedStatement.executeUpdate();
//executeUpdate可多次执行,修改插入的占位符的数据即可
修改:
//3.创建Statement对象
PreparedStatement preparedStatement = connection.prepareStatement("update t_emp set emp_salary = ? where emp_id = ?");
//4.为占位符赋值,索引从1开始,编写SQL语句并执行,获取结果
preparedStatement.setDouble(1,888.88);
preparedStatement.setDouble(2,8);
int result = preparedStatement.executeUpdate();
删除:
//3.创建Statement对象
PreparedStatement preparedStatement = connection.prepareStatement("delete from t_emp where emp_id = ?");
//4.为占位符赋值,索引从1开始,编写SQL语句并执行,获取结果
preparedStatement.setInt(1,8);
int result = preparedStatement.executeUpdate();
拓展
实体类和ORM
-
在使用JDBC操作数据库时,我们会发现数据都是零散的,明明在数据库中是一行完整的数据,到了Java中变成了一个一个的变量,不利于维护和管理。而我们Java是面向对象的,一个表对应的是一个类,一行数据就对应的是Java中的一个对象,一个列对应的是对象的属性,所以我们要把数据存储在一个载体里,这个载体就是实体类!
-
ORM(Object Relational Mapping)思想,对象到关系数据库的映射,作用是在编程中,把面向对象的概念跟数据库中表的概念对应起来,以面向对象的角度操作数据库中的数据,即一张表对应一个类,一行数据对应一个对象,一个列对应一个属性!
-
当下JDBC中这种过程我们称其为手动ORM。后续我们也会学习ORM框架,比如MyBatis、JPA等。
主键回显
-
在数据中,执行新增操作时,主键列为自动增长,可以在表中直观的看到,但是在Java程序中,我们执行完新增后,只能得到受影响行数,无法得知当前新增数据的主键值。在Java程序中获取数据库中插入新数据后的主键值,并赋值给Java对象,此操作为主键回显。
//2.获取数据库连接
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/atguigu", "root", "atguigu");
//3.创建preparedStatement对象,传入需要主键回显参数Statement.RETURN_GENERATED_KEYS
PreparedStatement preparedStatement = connection.prepareStatement("insert into t_emp (emp_name, emp_salary, emp_age)values (?, ?,?)",Statement.RETURN_GENERATED_KEYS);
//4.编写SQL语句并执行,获取结果
Employee employee = new Employee(null,"rose",666.66,28);
preparedStatement.setString(1,employee.getEmpName());
preparedStatement.setDouble(2,employee.getEmpSalary());
preparedStatement.setDouble(3,employee.getEmpAge());
int result = preparedStatement.executeUpdate();
//5.处理结果
if(result>0){
System.out.println("添加成功");
}else{
System.out.println("添加失败");
}
//6.获取生成的主键列值,返回的是resultSet,在结果集中获取主键列值
ResultSet resultSet = preparedStatement.getGeneratedKeys();
if (resultSet.next()){
int empId = resultSet.getInt(1);
employee.setEmpId(empId);
}
批量操作
-
插入多条数据时,一条一条发送给数据库执行,效率低下!
-
通过批量操作,可以提升多次操作效率!
//获取连接,在url后面加上rewriteBatchedStatements=true参数
Connection connection = DriverManager.getConnection("jdbc:mysql:///atguigu?rewriteBatchedStatements=true", "root", "atguigu");
/*
注意:1、必须在连接数据库的URL后面追加?rewriteBatchedStatements=true,允许批量操作
2、新增SQL必须用values。且语句最后不要追加;结束
3、调用addBatch()方法,将SQL语句进行批量添加操作
4、统一执行批量操作,调用executeBatch()
*/
String sql = "insert into t_emp (emp_name,emp_salary,emp_age) values (?,?,?)";
//创建预编译的PreparedStatement,传入SQL语句
PreparedStatement preparedStatement = connection.prepareStatement(sql);
for(int i = 0;i<10000;i++){
//为占位符赋值
preparedStatement.setString(1, "marry"+i);
preparedStatement.setDouble(2, 100.0+i);
preparedStatement.setInt(3, 20+i);
preparedStatement.addBatch(); //进行批量操作,最终是按 values (,,) (,,) ...这样一次插入多个执行
}
//执行批量操作
preparedStatement.executeBatch();
preparedStatement.close();
connection.close();
连接池
介绍
问题:
每次操作数据库都要获取新连接,使用完毕后就close释放,频繁的创建和销毁造成资源浪费。
连接的数量无法把控,对服务器来说压力巨大。
连接池:
连接池就是数据库连接对象的缓冲区,通过配置,由连接池负责创建连接、管理连接、释放连接等操作。
预先创建数据库连接放入连接池,用户在请求时,通过池直接获取连接,使用完毕后,将连接放回池中,避免了频繁的创建和销毁,同时解决了创建的效率。
当池中无连接可用,且未达到上限时,连接池会新建连接。
池中连接达到上限,用户请求会等待,可以设置超时时间。
常见连接池: JDBC 的数据库连接池使用 javax.sql.DataSource接口进行规范,所有的第三方连接池都实现此接口,自行添加具体实现!也就是说,所有连接池获取连接的 和回收连接方法都一样,不同的只有性能和扩展功能!
-
DBCP 是Apache提供的数据库连接池,速度相对C3P0较快,但自身存在一些BUG。
-
C3P0 是一个开源组织提供的一个数据库连接池,速度相对较慢,稳定性还可以。
-
Proxool 是sourceforge下的一个开源项目数据库连接池,有监控连接池状态的功能, 稳定性较c3p0差一点
-
Druid 是阿里提供的数据库连接池,是集DBCP 、C3P0 、Proxool 优点于一身的数据库连接池,性能、扩展性、易用性都更好,功能丰富。
-
Hikari(ひかり[shi ga li]) 取自日语,是光的意思,是SpringBoot2.x之后内置的一款连接池,基于 BoneCP (已经放弃维护,推荐该连接池)做了不少的改进和优化,口号是快速、简单、可靠。
Druid
使用步骤:
-
引入Druid.jar包。
-
编码。
硬编码
在java代码内定义参数
/*
硬编码:将连接池的配置信息和Java代码耦合在一起。
1、创建DruidDataSource连接池对象。
2、设置连接池的配置信息【必须 | 非必须】
3、通过连接池获取连接对象
4、回收连接【不是释放连接,而是将连接归还给连接池,给其他线程进行复用】
*/
//1.创建DruidDataSource连接池对象。
DruidDataSource druidDataSource = new DruidDataSource();
//2.设置连接池的配置信息【必须 | 非必须】
//2.1 必须设置的配置
druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
druidDataSource.setUrl("jdbc:mysql:///atguigu");
druidDataSource.setUsername("root");
druidDataSource.setPassword("atguigu");
//2.2 非必须设置的配置
druidDataSource.setInitialSize(10);
druidDataSource.setMaxActive(20);
//3.通过连接池获取连接对象
Connection connection = druidDataSource.getConnection();
System.out.println(connection);
//基于connection进行CRUD
//4.回收连接
connection.close();
软编码
-
在项目目录下创建resources文件夹,标识该文件夹为资源目录,创建db.properties配置文件,将连接信息定义在该文件中。
-
# druid连接池需要的配置参数,key固定命名
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql:///atguigu
username=root
password=atguigu
initialSize=10
maxActive=20 -
Java代码:
-
@Test
public void testResourcesDruid() throws Exception {
//1.创建Properties集合,用于存储外部配置文件的key和value值。
Properties properties = new Properties();
//2.读取外部配置文件,获取输入流,加载到Properties集合里。
InputStream inputStream = DruidTest.class.getClassLoader().getResourceAsStream("db.properties");
properties.load(inputStream);
//3.基于Properties集合构建DruidDataSource连接池
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
//4.通过连接池获取连接对象
Connection connection = dataSource.getConnection();
System.out.println(connection);
//5.开发CRUD
//6.回收连接
connection.close();
}
-
-
工具类封装
我们在使用JDBC的过程中,发现部分代码存在冗余的问题:
-
创建连接池。
-
获取连接。
-
连接的回收。
ThreadLocal
JDK 1.2的版本中就提供java.lang.ThreadLocal,为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。通常用来在在多线程中管理共享数据库连接、Session等。
ThreadLocal用于保存某个线程共享变量,原因是在Java中,每一个线程对象中都有一个ThreadLocalMap<ThreadLocal, Object>,其key就是一个ThreadLocal,而Object即为该线程的共享变量。
而这个map是通过ThreadLocal的set和get方法操作的。对于同一个static ThreadLocal,不同线程只能从中get,set,remove自己的变量,而不会影响其他线程的变量。
在进行对象跨层传递的时候,使用ThreadLocal可以避免多次传递,打破层次间的约束。
线程间数据隔离。
进行事务操作,用于存储线程事务信息。
数据库连接,
Session会话管理。1、ThreadLocal对象.get: 获取ThreadLocal中当前线程共享变量的值。
2、ThreadLocal对象.set: 设置ThreadLocal中当前线程共享变量的值。
3、ThreadLocal对象.remove: 移除ThreadLocal中当前线程共享变量的值。
ThreadLocal的核心特性是 线程隔离。每个线程都有自己独立的ThreadLocal变量副本,线程之间不会互相干扰。具体来说:
线程 A 使用
ThreadLocal时,会从ThreadLocal中获取到线程 A 自己的变量副本。线程 B 使用
ThreadLocal时,会从ThreadLocal中获取到线程 B 自己的变量副本。线程 A 和线程 B 的操作是完全独立的,线程 A 无法访问线程 B 的
ThreadLocal变量,反之亦然。
工具类
/**
* 1、维护一个连接池对象、维护了一个线程绑定变量的ThreadLocal对象
* 2、对外提供在ThreadLocal中获取连接的方法
* 3、对外提供回收连接的方法,回收过程中,将要回收的连接从ThreadLocal中移除!
* 注意:工具类仅对外提供共性的功能代码,所以方法均为静态方法!
* 注意:使用ThreadLocal就是为了一个线程在多次数据库操作过程中,使用的是同一个连接!
*/
public class JDBCUtilV2 {
//创建连接池引用,因为要提供给当前项目的全局使用,所以创建为静态的。
private static DataSource dataSource;
private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();
//在项目启动时,即创建连接池对象,赋值给dataSource
static {
try {
Properties properties = new Properties();
InputStream inputStream = JDBCUtil.class.getClassLoader().getResourceAsStream("db.properties");
properties.load(inputStream);
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
//对外提供在连接池中获取连接的方法
public static Connection getConnection(){
try {
//在ThreadLocal中获取Connection、
Connection connection = threadLocal.get();
//threadLocal里没有存储Connection,也就是第一次获取
if (connection == null) {
//在连接池中获取一个连接,存储在threadLocal里。
connection = dataSource.getConnection();
threadLocal.set(connection);
}
return connection;
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
//对外提供回收连接的方法
public static void release(){
try {
Connection connection = threadLocal.get();
if(connection!=null){
//从threadLocal中移除当前已经存储的Connection对象
threadLocal.remove();
//如果开启了事务的手动提交,操作完毕后,归还给连接池之前,要将事务的自动提交改为true
connection.setAutoCommit(true);
//将Connection对象归还给连接池
connection.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
事务
定义
-
数据库事务就是一种SQL语句执行的缓存机制,不会单条执行完毕就更新数据库数据,最终根据缓存内的多条语句执行结果统一判定! 一个事务内所有语句都成功及事务成功,我们可以触发commit提交事务来结束事务,更新数据! 一个事务内任意一条语句失败,即为事务失败,我们可以触发rollback回滚结束事务,数据回到事务之前状态!
-
一个业务涉及多条修改数据库语句! 例如:
-
经典的转账案例,转账业务(A账户减钱和B账户加钱,要一起成功)
-
批量删除(涉及多个删除)
-
批量添加(涉及多个插入)
-
-
事务的特性:
-
原子性(Atomicity)原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生, 要么都不发生。
-
一致性(Consistency)事务必须使数据库从一个一致性状态变换到另外一个一致性状态。
-
隔离性(Isolation)事务的隔离性是指一个事务的执行不能被其他事务干扰, 即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
-
持久性(Durability)持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的, 接下来的其他操作和数据库故障不应该对其有任何影响
-
-
事务的提交方式:
-
自动提交:每条语句自动存储一个事务中,执行成功自动提交,执行失败自动回滚!
-
手动提交: 手动开启事务,添加语句,手动提交或者手动回滚即可,set autocommit = false //关闭自动提交
-
使用: 在使用 set autocommit = false 打开手动提交后,接下来的操作都是原子性的,在使用commit后代表完成此次操作, rollback可撤销此次整个操作 使用完后 set autocommit = true 关闭手动提交
编写流程
结构
在编写业务时,有以下结构, dao:数据库访问对象,负责提供一些对数据库进行操作的接口 分为接口和实现类,接口用于规范方法,以及告诉调用者如何使用,实现类即是具体的实现方法 在接口每个业务方法上通常会加上该接口具体作用的注释 实体类: (pojo,beans,entities) 用于代表一个表内的内容,表内查到的一行数据都封装在实体类里
Servlet
简介
动态资源和静态资源: 静态资源 无需在程序运行时通过代码运行生成的资源,在程序运行之前就写好的资源. 例如:html css js img ,音频文件和视频文件 动态资源 需要在程序运行时通过代码运行生成的资源,在程序运行之前无法确定的数据,运行时动态生成,例如Servlet,Thymeleaf ... ... 动态资源指的不是视图上的动画效果或者是简单的人机交互效果 Servlet简介: Servlet (server applet) 是运行在服务端(tomcat)的Java小程序,是sun公司提供一套定义动态资源规范; 从代码层面上来讲Servlet就是一个接口 用来接收、处理客户端请求、响应给浏览器的动态资源。在整个Web应用中, Servlet主要负责接收处理请求、协同调度功能以及响应数据。我们可以把Servlet称为Web应用中的控制器 不是所有的JAVA类都能用于处理客户端请求,能处理客户端请求并做出响应的一套技术标准就是Servlet Servlet是运行在服务端的,所以 Servlet必须在WEB项目中开发且在Tomcat这样的服务容器中运行 请求响应与HttpServletRequest和HttpServletResponse之间的对应关系 tomcat接收客户端的请求,会将请求报文封装到HttpServletRequest对象中,然后回将返回的HttpServletResponse对象转换为 响应报文返回,在servlet中,定义一个实现servlet的类,实现其service方法可以实现对客户端请求的处理以及实现响应
开发流程
配置servlet
public class UserServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求中的参数
String username = req.getParameter("username"); //请求行或体里面的参数都可以获取
if("atguigu".equals(username)){
//通过响应对象响应信息
resp.getWriter().write("NO");
}else{
resp.getWriter().write("YES");
}
}
}
-
自定义一个类,要继承HttpServlet类或者实现Servlet接口
-
重写service方法,该方法主要就是用于处理用户请求的服务方法
-
HttpServletRequest 代表请求对象,是有请求报文经过tomcat转换而来的,通过该对象可以获取请求中的信息
-
HttpServletResponse 代表响应对象,该对象会被tomcat转换为响应的报文,通过该对象可以设置响应中的信息
-
Servlet对象的生命周期(创建,初始化,处理服务,销毁)是由tomcat管理的,无需我们自己new
-
HttpServletRequest HttpServletResponse 两个对象也是由tomcat负责转换,在调用service方法时传入给我们用的
在web.xml为UseServlet配置请求的映射路径
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0">
<servlet>
<!--给UserServlet起一个别名-->
<servlet-name>userServlet</servlet-name>
<servlet-class>com.atguigu.servlet.UserServlet</servlet-class>
</servlet>
<servlet-mapping>
<!--关联别名和映射路径-->
<servlet-name>userServlet</servlet-name> 别名
<!--可以为一个Servlet匹配多个不同的映射路径,但是不同的Servlet不能使用相同的url-pattern-->
<url-pattern>/userServlet</url-pattern> 路径
<url-pattern>/userServlet2</url-pattern> 多个路径
<!-- <url-pattern>/userServlet2</url-pattern>-->
<!--
/ 表示通配所有资源,不包括jsp文件
/* 表示通配所有资源,包括jsp文件
/a/* 匹配所有以a前缀的映射路径
*.action 匹配所有以action为后缀的映射路径
-->
<!-- <url-pattern>/*</url-pattern>-->
</servlet-mapping>
</web-app>
-
Servlet并不是文件系统中实际存在的文件或者目录,所以为了能够请求到该资源,我们需要为其配置映射路径
-
servlet的请求映射路径配置在web.xml中
-
servlet-name作为servlet的别名,可以自己随意定义,见名知意就好
-
url-pattern标签用于定义Servlet的请求映射路径
-
一个servlet可以对应多个不同的url-pattern
-
多个servlet不能使用相同的url-pattern
注解配置路径
使用@WebServlet注解替换Servlet配置
@WebServlet(
name = "userServlet",
//value = "/user",
urlPatterns = {"/userServlet1","/userServlet2","/userServlet"}, //可以直接使用value,两者互为别名
initParams = {@WebInitParam(name = "encoding",value = "UTF-8")},
loadOnStartup = 6 //在tomcat开启时就进行实例化及初始化
)
public class UserServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {}
生命周期
应用程序中的对象不仅在空间上有层次结构的关系, 在时间上也会因为处于程序运行过程中的不同阶段而表现出不同状态和不同行为——这就是对象的生命周期。 简单的叙述生命周期,就是对象在容器中从开始创建到销毁的过程 Servlet对象是Servlet容器创建的,生命周期方法都是由容器(目前我们使用的是Tomcat)调用的。 这一点和我们之前所编写的代码有很大不同。在今后的学习中我们会看到,越来越多的对象交给容器或框架来创建, 越来越多的方法由容器或框架来调用,开发人员要尽可能多的将精力放在业务逻辑的实现上。
| 生命周期 | 对应方法 | 执行时机 | 执行次数 |
|---|---|---|---|
| 构造对象 | 构造器 | 第一次请求或者容器启动 | 1 |
| 初始化 | init() | 构造完毕后 | 1 |
| 处理服务 | service(HttpServletRequest req,HttpServletResponse resp) | 每次请求 | 多次 |
| 销毁 | destory() | 容器关闭 | 1 |
loadOnStartup为-1时,初始化是在第一次请求时进行,若为正整数时,随容器启动进行,数字大小为启动顺序
public class ServletLifeCycle extends HttpServlet {
public ServletLifeCycle(){
System.out.println("构造器");
}
@Override
public void init() throws ServletException {
System.out.println("初始化方法");
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("service方法");
}
@Override
public void destroy() {
System.out.println("销毁方法");
}
}
<servlet>
<servlet-name>servletLifeCycle</servlet-name>
<servlet-class>com.atguigu.servlet.ServletLifeCycle</servlet-class>
<!--load-on-startup
如果配置的是正整数则表示容器在启动时就要实例化Servlet,
数字表示的是实例化的顺序
-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>servletLifeCycle</servlet-name>
<url-pattern>/servletLiftCycle</url-pattern>
</servlet-mapping>
Servlet对象在容器中是单例的 容器是可以处理并发的用户请求的,每个请求在容器中都会开启一个线程 多个线程可能会使用相同的Servlet对象,所以在Servlet中,我们不要轻易定义一些容易经常发生修改的成员变量 load-on-startup中定义的正整数表示实例化顺序,如果数字重复了,容器会自行解决实例化顺序问题,但是应该避免重复 Tomcat容器中,已经定义了一些随系统启动实例化的servlet,我们自定义的servlet的load-on-startup尽量不要占用数字1-5 如defaultServlet,如果在对动态资源匹配后没有匹配到对应的路径,会使用defaultServlet对静态路径进行匹配 每个web.xml文件都有一个共有的配置,在conf/web.xml内
继承结构
Servlet接口
Servlet 规范接口,所有的Servlet必须实现
+ public void init(ServletConfig config) throws ServletException;
+ 初始化方法,容器在构造servlet对象后,自动调用的方法,容器负责实例化一个ServletConfig对象,并在调用该方法时传入
+ ServletConfig对象可以为Servlet 提供初始化参数
+ public ServletConfig getServletConfig();
+ 获取ServletConfig对象的方法,后续可以通过该对象获取Servlet初始化参数
+ public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
+ 处理请求并做出响应的服务方法,每次请求产生时由容器调用
+ 容器创建一个ServletRequest对象和ServletResponse对象,容器在调用service方法时,传入这两个对象
+ public String getServletInfo();
+ 获取ServletInfo信息的方法
+ public void destroy();
+ Servlet实例在销毁之前调用的方法
GenericServlet 抽象类
GenericServlet 抽象类是对Servlet接口一些固定功能的粗糙实现,以及对service方法的再次抽象声明,并定义了一些其他相关功能方法
HttpServlet 抽象类
abstract class HttpServlet extends GenericServlet HttpServlet抽象类,除了基本的实现以外,增加了更多的基础功能(对service的拓展)
+ private static final String METHOD_DELETE = "DELETE";
+ private static final String METHOD_HEAD = "HEAD";
+ private static final String METHOD_GET = "GET";
+ private static final String METHOD_OPTIONS = "OPTIONS";
+ private static final String METHOD_POST = "POST";
+ private static final String METHOD_PUT = "PUT";
+ private static final String METHOD_TRACE = "TRACE";
+ 上述属性用于定义常见请求方式名常量值
+ public HttpServlet() {}
+ 构造器,用于处理继承
+ public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException
+ 对服务方法的实现
+ 在该方法中,将请求和响应对象转换成对应HTTP协议的HttpServletRequest HttpServletResponse对象
+ 调用重载的service方法
+ public void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
+ 重载的service方法,被重写的service方法所调用
+ 在该方法中,通过请求方式判断,调用具体的do***方法完成请求的处理
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+ protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+ protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+ protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+ protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+ protected void doTrace(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+ 对应不同请求方式的处理方法
+ 除了doOptions和doTrace方法,其他的do*** 方法都在故意响应错误信息
自定义Servlet中,必须要对处理请求的方法进行重写
-
要么重写service方法
-
要么重写doGet/doPost方法
配置参数
ServletConfig
为Servlet提供初始配置参数的一种对象,每个Servlet都有自己独立唯一的ServletConfig对象 容器会为每个Servlet实例化一个ServletConfig对象,并通过Servlet生命周期的init方法传入给Servlet作为属性 API:
| 方法名 | 作用 |
|---|---|
| getServletName() | 获取<servlet-name>HelloServlet</servlet-name>定义的Servlet名称 |
| getServletContext() | 获取ServletContext对象 |
| getInitParameter() | 获取配置Servlet时设置的『初始化参数』,根据名字获取值 |
| getInitParameterNames() | 获取所有初始化参数名组成的Enumeration对象 |
设置参数: 注解: 参数是以键值对新式存在, 在@WebServlet注解中有一个注解数组,用其可以实现注解定义配置 XML配置: 在<servlet> 内配置
<init-param>
<param-name>参数名</param-name>
<param-value>参数值</param-value>
</init-param>
ServletContext
ServletContext对象有称呼为上下文对象,或者叫应用域对象 容器会为每个app创建一个独立的唯一的ServletContext对象 ServletContext对象为所有的Servlet所共享 ServletContext可以为所有的Servlet提供初始配置参数 参数配置: 在xml中 <web-app>内配置
<context-param>
<param-name>paramB</param-name>
<param-value>valueB</param-value>
</context-param>
获取ServletContext并获取参数:
public class ServletA extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 从ServletContext中获取为所有的Servlet准备的参数
ServletContext servletContext = this.getServletContext(); //也可以使用req调用
String valueA = servletContext.getInitParameter("paramA");
System.out.println("paramA:"+valueA);
// 获取所有参数名
Enumeration<String> initParameterNames = servletContext.getInitParameterNames();
// 迭代并获取参数名
while (initParameterNames.hasMoreElements()) {
String paramaterName = initParameterNames.nextElement();
System.out.println(paramaterName+":"+servletContext.getInitParameter(paramaterName));
}
}
}
其他API: 获取资源的真实路径(返回该文件在服务器磁盘上的完整路径)
String realPath = servletContext.getRealPath("资源在web目录中的路径");
获取项目的上下文路径(访问路径,也就是部署时指定的路径,通常是项目的访问路径)
String contextPath = servletContext.getContextPath();
域对象的相关API 域对象: 一些用于存储数据和传递数据的对象,传递数据不同的范围,我们称之为不同的域,不同的域对象代表不同的域,共享数据的范围也不同 ServletContext代表应用,所以ServletContext域也叫作应用域,是webapp中最大的域,可以在本应用内实现数据的共享和传递 webapp中的三大域对象,分别是应用域,会话域,请求域 三大域对象都具有的API如下
| API | 功能解释 |
|---|---|
| void setAttribute(String key,Object value); | 向域中存储/修改数据 |
| Object getAttribute(String key); | 获得域中的数据 |
| void removeAttribute(String key); | 移除域中的数据 |
使用引用域可实现各个Servlet之间传递信息 注: Enumeration是一个迭代器
HttpServletRequest
介绍: HttpServletRequest是一个接口,其父接口是ServletRequest HttpServletRequest是Tomcat将请求报文转换封装而来的对象,在Tomcat调用service方法时传入 HttpServletRequest代表客户端发来的请求,所有请求中的信息都可以通过该对象获得 常见API:
-
获取请求行信息相关(方式,请求的url,协议及版本)
| API | 功能解释 |
|---|---|
| StringBuffer getRequestURL(); | 获取客户端请求的url |
| String getRequestURI(); | 获取客户端请求项目中的具体资源uri |
| int getServerPort(); | 获取客户端发送请求时的端口 |
| int getLocalPort(); | 获取本应用在所在容器的端口 |
| int getRemotePort(); | 获取客户端程序的端口 |
| String getScheme(); | 获取请求协议 |
| String getProtocol(); | 获取请求协议及版本号 |
| String getMethod(); | 获取请求方式 |
注: URI为 用于标识某一资源的字符串,可以是资源的名称、位置或两者结合。 URL为 URI的子集,不仅标识资源,还提供了资源的访问路径和方式。
-
获得请求头信息相关
| API | 功能解释 |
|---|---|
| String getHeader(String headerName); | 根据头名称获取请求头 |
| Enumeration<String> getHeaderNames(); | 获取所有的请求头名字 |
| String getContentType(); | 获取content-type请求头 |
-
获得请求参数相关
| API | 功能解释 |
|---|---|
| String getParameter(String parameterName); | 根据请求参数名获取请求单个参数值 |
| String[] getParameterValues(String parameterName); | 根据请求参数名获取请求多个参数值数组 |
| Enumeration<String> getParameterNames(); | 获取所有请求参数名 |
| Map<String, String[]> getParameterMap(); | 获取所有请求参数的键值对集合 |
| BufferedReader getReader() throws IOException; | 获取读取请求体的字符输入流 |
| ServletInputStream getInputStream() throws IOException; | 获取读取请求体的字节输入流 |
| int getContentLength(); | 获得请求体长度的字节数 |
-
其他API
| API | 功能解释 |
|---|---|
| String getServletPath(); | 获取请求的Servlet的映射路径 |
| ServletContext getServletContext(); | 获取ServletContext对象 |
| Cookie[] getCookies(); | 获取请求中的所有cookie |
| HttpSession getSession(); | 获取Session对象 |
| void setCharacterEncoding(String encoding) ; | 设置请求体字符集 |
HttpServletResponse
定义:
-
HttpServletResponse是一个接口,其父接口是ServletResponse
-
HttpServletResponse是Tomcat预先创建的,在Tomcat调用service方法时传入
-
HttpServletResponse代表对客户端的响应,该对象会被转换成响应的报文发送给客户端,通过该对象我们可以设置响应信息
常见API:
-
设置响应行相关
| API | 功能解释 |
|---|---|
| void setStatus(int code); | 设置响应状态码 |
-
设置响应头相关
| API | 功能解释 |
|---|---|
| void setHeader(String headerName, String headerValue); | 设置/修改响应头键值对 |
| void setContentType(String contentType); | 设置content-type响应头及响应字符集(设置MIME类型) |
-
设置响应体相关
| API | 功能解释 |
|---|---|
| PrintWriter getWriter() throws IOException; | 获得向响应体放入信息的字符输出流 |
| ServletOutputStream getOutputStream() throws IOException; | 获得向响应体放入信息的字节输出流 |
| void setContentLength(int length); | 设置响应体的字节长度,其实就是在设置content-length响应头 |
-
其他API
| API | 功能解释 |
|---|---|
| void sendError(int code, String message) throws IOException; | 向客户端响应错误信息的方法,需要指定响应码和响应信息 |
| void addCookie(Cookie cookie); | 向响应体中增加cookie |
| void setCharacterEncoding(String encoding); | 设置响应体字符集 |
MIME类型
-
MIME类型,可以理解为文档类型,用户表示传递的数据是属于什么类型的文档
-
浏览器可以根据MIME类型决定该用什么样的方式解析接收到的响应体数据
-
可以这样理解: 前后端交互数据时,告诉对方发给对方的是 html/css/js/图片/声音/视频/... ...
-
tomcat/conf/web.xml中配置了常见文件的拓展名和MIMIE类型的对应关系
-
常见的MIME类型举例如下
| 文件拓展名 | MIME类型 |
|---|---|
| .html | text/html |
| .css | text/css |
| .js | application/javascript |
| .png /.jpeg/.jpg/... ... | image/jpeg |
| .mp3/.mpe/.mpeg/ ... ... | audio/mpeg |
| .mp4 | video/mp4 |
| .m1v/.m1v/.m2v/.mpe/... ... | video/mpeg |
请求转发和响应重定向
概述
什么是请求转发和响应重定向
-
请求转发和响应重定向是web应用中间接访问项目资源的两种手段,也是Servlet控制页面跳转的两种手段
-
请求转发通过HttpServletRequest实现,响应重定向通过HttpServletResponse实现
-
请求转发生活举例: 张三找李四借钱,李四没有,李四找王五,让王五借给张三
-
响应重定向生活举例:张三找李四借钱,李四没有,李四让张三去找王五,张三自己再去找王五借钱
请求转发
-
请求转发通过HttpServletRequest对象获取请求转发器实现
-
请求转发是服务器内部的行为,对客户端是屏蔽的
-
客户端只发送了一次请求,客户端地址栏不变
-
服务端只产生了一对请求和响应对象,这一对请求和响应对象会继续传递给下一个资源
-
因为全程只有一个HttpServletRequset对象,所以请求参数可以传递,请求域中的数据也可以传递
-
请求转发可以转发给其他Servlet动态资源,也可以转发给一些静态资源以实现页面跳转
-
请求转发可以转发给WEB-INF下受保护的资源
-
请求转发不能转发到本项目以外的外部资源
@WebServlet("/servletA")
public class ServletA extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求转发器
// 转发给servlet ok
RequestDispatcher requestDispatcher = req.getRequestDispatcher("servletB");
// 转发给一个视图资源 ok
//RequestDispatcher requestDispatcher = req.getRequestDispatcher("welcome.html");
// 转发给WEB-INF下的资源 ok
//RequestDispatcher requestDispatcher = req.getRequestDispatcher("WEB-INF/views/view1.html");
// 转发给外部资源 no
//RequestDispatcher requestDispatcher = req.getRequestDispatcher("http://www.atguigu.com");
// 获取请求参数
String username = req.getParameter("username");
System.out.println(username);
// 做出转发动作
requestDispatcher.forward(req,resp);
}
}
响应重定向
-
响应重定向通过HttpServletResponse对象的sendRedirect方法实现
-
响应重定向是服务端通过302响应码和路径,告诉客户端自己去找其他资源,是在服务端提示下的,客户端的行为
-
客户端至少发送了两次请求,客户端地址栏是要变化的
-
服务端产生了多对请求和响应对象,且请求和响应对象不会传递给下一个资源
-
因为全程产生了多个HttpServletRequset对象,所以请求参数不可以传递,请求域中的数据也不可以传递
-
重定向可以是其他Servlet动态资源,也可以是一些静态资源以实现页面跳转
-
重定向不可以到给WEB-INF下受保护的资源
-
重定向可以到本项目以外的外部资源
@WebServlet("/servletA")
public class ServletA extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求参数
String username = req.getParameter("username");
System.out.println(username);
// 响应重定向
// 重定向到servlet动态资源 OK
resp.sendRedirect("servletB");
// 重定向到视图静态资源 OK
//resp.sendRedirect("welcome.html");
// 重定向到WEB-INF下的资源 NO
//resp.sendRedirect("WEB-INF/views/view1");
// 重定向到外部资源
//resp.sendRedirect("http://www.atguigu.com");
}
}
乱码
字符集
乱码产生的原因 数据的编码和解码使用的不是同一个字符集 使用了不支持某个语言文字的字符集 IDEA编码: IDEA默认使用UTF-8编码 可在File Encoding 中设置
请求乱码
GET乱码: GET方式提交参数的方式是将参数放到URL后面,如果使用的不是UTF-8,那么会对参数进行URL编码处理 HTML中的<meta charset='字符集'/>影响了GET方式提交参数的URL编码 GET方式提交时,会对数据进行URL编码处理 乱码解决: 方式一:设置html的编码和tomcat编码一致 方式二:修改tomcat配置conf/server.xml中 Connecter 添加 URIEncoding="字符集" 这里修改的编码只对URI中起效,不对请求体产生作用 POST乱码: POST请求将参数放在请求体中进行发送 请求体使用的字符集受到了<meta charset="字符集"/> 的影响 乱码解决: 方式一:请求时,使用UTF-8字符集提交请求体 方式二: 后端在获取参数前,设置解析请求体使用的字符集和请求发送时使用的字符集一致 request对象调用setCharacterEncoding("字符集");方法设置
响应乱码
浏览器在接收响应信息时,使用了不同的字符集或者是不支持中文的字符集就会出现乱码 解决: 通过设置content-type响应头,告诉浏览器以指定的字符集解析响应体 调用 response对象的setContentType("MIME类型;chatset = 字符集"); 实现
路径
前端路径
前端中所有引用到其他资源的属性,都为路径
相对路径
以当前资源( html文件 )所在路径(所在文件夹的路径)寻找资源 语法: 不以 / 开头 ./ 表示当前资源的路径 ../ 表示当前资源上一层路径 (可以多次声明) 例
-
访问index.html的url为 :
http://localhost:8080/web03 -
当前资源为 : index.html
-
当前资源的所在路径为 :
http://localhost:8080/web03/ -
要获取的目标资源url为 :
http://localhost:8080/web03/static/img/logo.png -
index.html中定义的了 :
<img src="static/img/logo.png"/> -
寻找方式就是在当前资源所在路径(
http://localhost:8080/web03/)后拼接src属性值(static/img/logo.png), 正好是目标资源正常获取的url(http://localhost:8080/web03/static/img/logo.png)
缺点: 目标资源受到当前资源路径的影响,不同位置,相对路径写法不同
注: 只用servlet转发请求,浏览器内URI地址不会变化,因此在获得转发来的HTML文件内使用的相对路径指向的资源时,可能会获取失败 在使用相对路径时,需要注意的是浏览器的路径,而不是服务端的实际路径
绝对路径
始终以固定的路径作为出发点去找资源目标,和当前资源的所在路径没有关系 语法:以 /开头 不同项目中,固定的路径出发点不同 出发点为当前的IP:HOST 地址
例
web/a/b/c/test.html中引入web/static/img/logo.png
-
访问test.html的url为 : http://localhost:8080/web03_war_exploded/a/b/c/test.html
-
绝对路径的基准路径为 : http://localhost:8080
-
要获取的目标资源url为 : http://localhost:8080/web03_war_exploded/static/img/logo.png
-
test.html中定义的了 :
<img src="/web03_war_exploded/static/img/logo.png"/> -
寻找方式就是在基准路径(http://localhost:8080)后面拼接src属性值(/web03_war_exploded/static/img/logo.png),得到的正是目标资源访问的正确路径
base标签
base标签定义页面相对路径公共前缀
-
base 标签定义在head标签中,用于定义相对路径的公共前缀
-
base 标签定义的公共前缀只在相对路径上有效,绝对路径中无效
-
如果相对路径开头有 ./ 或者../修饰,则base标签对该路径同样无效
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!--定义相对路径的公共前缀,将相对路径转化成了绝对路径-->
<base href="/web03_war_exploded/"> 前面加上了/ 因此为绝对路径
</head>
<body>
<img src="static/img/logo.png"> <!--最终访问的是 ip:host/web03_war_exploded/static/img/logo.png-->
</body>
</html>
重定向和转发路径
相对路径: 转发和重定向的相对路径同HTML内的相对路径一样,是相对于当前路径进行定向的 绝对路径: 响应重定向的路径同HTML路径一致 请求转发的绝对路径基于当前项目,在项目上下文路径(访问路径)为缺省值时,无需改变,直接以/开头即可
会话管理
Cookie
概述
cookie是一种客户端会话技术,cookie由服务端产生,它是服务器存放在浏览器的一小份数据,浏览器以后每次访问该服务器的时候都会将这小份数据携带到服务器去。
-
服务端创建cookie,将cookie放入响应对象中,Tomcat容器将cookie转化为set-cookie响应头,响应给客户端
-
客户端在收到cookie的响应头时,在下次请求该服务的资源时,会以cookie请求头的形式携带之前收到的Cookie
-
cookie是一种键值对格式的数据,从tomcat8.5开始可以保存中文,但是不推荐
-
由于cookie是存储于客户端的数据,比较容易暴露,一般不存储一些敏感或者影响安全的数据
-
Cookie通常是针对单个网站的,不同网站的Cookie是不互通的。这是因为Cookie的域属性限制了Cookie只能在特定的域及其子域中使用
使用
@WebServlet("/servletA")
public class ServletA extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 创建Cookie
Cookie cookie1 =new Cookie("c1","c1_message");
Cookie cookie2 =new Cookie("c2","c2_message");
// 将cookie放入响应对象
resp.addCookie(cookie1);
resp.addCookie(cookie2);
}
}
//获取请求中的cookie
Cookie[] cookies = req.getCookies();
//迭代cookies数组
if (null != cookies && cookies.length!= 0) {
for (Cookie cookie : cookies) {
System.out.println(cookie.getName()+":"+cookie.getValue());
时效性
默认情况下Cookie的有效期是一次会话范围内,我们可以通过cookie的setMaxAge()方法让Cookie持久化保存到浏览器上
-
会话级Cookie
-
服务器端并没有明确指定Cookie的存在时间
-
在浏览器端,Cookie数据存在于内存中
-
只要浏览器还开着,Cookie数据就一直都在
-
浏览器关闭,内存中的Cookie数据就会被释放
-
-
持久化Cookie
-
服务器端明确设置了Cookie的存在时间
-
在浏览器端,Cookie数据会被保存到硬盘上
-
Cookie在硬盘上存在的时间根据服务器端限定的时间来管控,不受浏览器关闭的影响
-
持久化Cookie到达了预设的时间会被释放
-
cookie.setMaxAge(int expiry)参数单位是秒,表示cookie的持久化时间,如果设置参数为0,表示将浏览器中保存的该cookie删除
// 创建Cookie
Cookie cookie1 =new Cookie("c1","c1_message");
cookie1.setMaxAge(60);
提交路径
访问互联网资源时不能每次都需要把所有Cookie带上。访问不同的资源时,可以携带不同的cookie,我们可以通过cookie的setPath(String path) 对cookie的路径进行设置
public class ServletA extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 创建Cookie
Cookie cookie1 =new Cookie("c1","c1_message");
// 设置cookie的提交路径
cookie1.setPath("/web03_war_exploded/servletB");
Cookie cookie2 =new Cookie("c2","c2_message");
// 将cookie放入响应对象
resp.addCookie(cookie1);
resp.addCookie(cookie2);
}
}
这样只有当向/web03_war_exploded/servletB 发起请求时,才会携带cookie
Session
概述
HttpSession是一种保留更多信息在服务端的一种技术,服务器会为每一个客户端开辟一块内存空间,即session对象. 客户端在发送请求时,都可以使用自己的session. 这样服务端就可以通过session来记录某个客户端的状态了
-
服务端在为客户端创建session时,会同时将session对象的id,即JSESSIONID以cookie的形式放入响应对象
-
后端创建完session后,客户端会收到一个特殊的cookie,叫做JSESSIONID
-
客户端下一次请求时携带JSESSIONID,后端收到后,根据JSESSIONID找到对应的session对象
-
通过该机制,服务端通过session就可以存储一些专门针对某个客户端的信息了
-
session也是域对象(后续详细讲解)
使用
@WebServlet("/servletA")
public class ServletA extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求中的参数
String username = req.getParameter("username");
// 获取session对象
HttpSession session = req.getSession();
// 获取Session的ID
String jSessionId = session.getId();
System.out.println(jSessionId);
// 判断session是不是新创建的session
boolean isNew = session.isNew();
System.out.println(isNew);
// 向session对象中存入数据
session.setAttribute("username",username);
}
}
// 获取session对象
HttpSession session = req.getSession();
// 获取Session的ID
String jSessionId = session.getId();
System.out.println(jSessionId);
// 判断session是不是新创建的session
boolean isNew = session.isNew();
System.out.println(isNew);
// 从session中取出数据
String username = (String)session.getAttribute("username");
System.out.println(username);
getSession方法的处理逻辑 1.若请求中不携带JSESSIONID,则新创建一个JSESSIONID对象,并且直接存入response返回对象中,不用手动操作 2.若请求中携带JSESSIONID,但在服务端中不存在该JSESSIONID,则仍创建新的JSESSIONID并返回 3.若携带JSESSIONID,则服务端中存在该JSESSIONID,则返回旧的(先前创建的)JSESSIONID
时效性
默认的session最大闲置时间(两次使用同一个session中的间隔时间) 在tomcat/conf/web.xml配置为30分钟
我们可以自己在当前项目的web.xml对最大闲置时间进行重新设定
也可以通过HttpSession的API 对最大闲置时间进行设定
// 设置最大闲置时间
session.setMaxInactiveInterval(60);
// 设置最大闲置时间
session.setMaxInactiveInterval(60);
三大域
概述
域对象: 一些用于存储数据和传递数据的对象,传递数据不同的范围,我们称之为不同的域,不同的域对象代表不同的域,共享数据的范围也不同
-
请求域对象是HttpServletRequest ,传递数据的范围是一次请求之内及请求转发
-
会话域对象是HttpSession,传递数据的范围是一次会话之内,可以跨多个请求
-
应用域对象是ServletContext,传递数据的范围是本应用之内,可以跨多个会话
使用
域对象的API
| API | 功能 |
|---|---|
| void setAttribute(String name,String value) | 向域对象中添加/修改数据 |
| Object getAttribute(String name); | 从域对象中获取数据 |
| removeAttribute(String name); | 移除域对象中的数据 |
过滤器
概述
Filter,即过滤器,是JAVAEE技术规范之一,作用目标资源的请求进行过滤的一套技术规范,是Java Web项目中
最为实用的技术之一
-
Filter接口定义了过滤器的开发规范,所有的过滤器都要实现该接口
-
Filter的工作位置是项目中所有目标资源之前,容器在创建HttpServletRequest和HttpServletResponse对象后,会先调用Filter的doFilter方法
-
Filter的doFilter方法可以控制请求是否继续,如果放行,则请求继续,如果拒绝,则请求到此为止,由过滤器本身做出响应
-
Filter不仅可以对请求做出过滤,也可以在目标资源做出响应前,对响应再次进行处理
使用
Filter接口API
| API | 目标 |
|---|---|
| default public void init(FilterConfig filterConfig) | 初始化方法,由容器调用并传入初始配置信息filterConfig对象 |
| public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) | 过滤方法,核心方法,过滤请求,决定是否放行,响应之前的其他处理等都在该方法中 |
| default public void destroy() | 销毁方法,容器在回收过滤器对象之前调用的方法 |
public class LoggingFilter implements Filter {
private SimpleDateFormat dateFormat =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException,
ServletException {
// 参数父转子
HttpServletRequest request =(HttpServletRequest) servletRequest;
HttpServletResponse response =(HttpServletResponse) servletResponse;
// 拼接日志文本
String requestURI = request.getRequestURI();
String time = dateFormat.format(new Date());
String beforeLogging =requestURI+"在"+time+"被请求了";
// 打印日志
System.out.println(beforeLogging);
// 获取系统时间
long t1 = System.currentTimeMillis();
// 放行请求
filterChain.doFilter(request,response);
//在执行完放行后,最后业务执行完毕还会返回这里,执行下面的代码
// 获取系统时间
long t2 = System.currentTimeMillis();
// 拼接日志文本
String afterLogging =requestURI+"在"+time+"的请求耗时:"+(t2-t1)+"毫秒";
// 打印日志
System.out.println(afterLogging);
}
}
说明
-
doFilter方法中的请求和响应对象是以父接口的形式声明的,实际传入的实参就是HttpServletRequest和HttpServletResponse子接口级别的,可以安全强转
-
filterChain.doFilter(request,response); 这行代码的功能是放行请求,如果没有这一行代码,则请求到此为止
-
filterChain.doFilter(request,response);在放行时需要传入request和response,意味着请求和响应对象要继续传递给后续的资源,这里没有产生新的request和response对象
配置过滤器以及过滤器的过滤范围
在web.xml内配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0">
<!--配置filter,并为filter起别名-->
<filter>
<filter-name>loggingFilter</filter-name>
<filter-class>com.atguigu.filters.LoggingFilter</filter-class>
</filter>
<!--为别名对应的filter配置要过滤的目标资源-->
<filter-mapping>
<filter-name>loggingFilter</filter-name>
<!--通过映射路径确定过滤资源-->
<url-pattern>/servletA</url-pattern>
<!--通过后缀名确定过滤资源-->
<url-pattern>*.html</url-pattern>
<!--通过servlet别名确定过滤资源-->
<servlet-name>servletBName</servlet-name>
</filter-mapping>
</web-app>
说明
-
filter-mapping标签中定义了过滤器对那些资源进行过滤
-
子标签url-pattern通过映射路径确定过滤范围
-
/servletA 精确匹配,表示对servletA资源的请求进行过滤
-
*.html 表示对以.action结尾的路径进行过滤
-
/* 表示对所有资源进行过滤
-
一个filter-mapping下可以配置多个url-pattern
-
-
子标签servlet-name通过servlet别名确定对那些servlet进行过滤
-
使用该标签确定目标资源的前提是servlet已经起了别名
-
一个filter-mapping下可以定义多个servlet-name
-
一个filter-mapping下,servlet-name和url-pattern子标签可以同时存在
-
生命周期
| 阶段 | 对应方法 | 执行时机 | 执行次数 |
|---|---|---|---|
| 创建对象 | 构造器 | web应用启动时 | 1 |
| 初始化方法 | void init(FilterConfig filterConfig) | 构造完毕 | 1 |
| 过滤请求 | void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) | 每次请求 | 多次 |
| 销毁 | default void destroy() | web应用关闭时 | 1次 |
过滤器链
一个web项目中,可以同时定义多个过滤器,多个过滤器对同一个资源进行过滤时,工作位置有先后,整体形成一个工作链,称之为过滤器链
-
过滤器链中的过滤器的顺序由filter-mapping顺序决定(由上倒下,优先级降低)
-
每个过滤器过滤的范围不同,针对同一个资源来说,过滤器链中的过滤器个数可能是不同的
-
如果某个Filter是使用ServletName进行匹配规则的配置,那么这个Filter执行的优先级要更低
注解方式配置
<!--配置filter,并为filter起别名-->
<filter>
<filter-name>loggingFilter</filter-name>
<filter-class>com.atguigu.filters.LoggingFilter</filter-class>
<!--配置filter的初始参数-->
<init-param>
<param-name>dateTimePattern</param-name>
<param-value>yyyy-MM-dd HH:mm:ss</param-value>
</init-param>
</filter>
<!--为别名对应的filter配置要过滤的目标资源-->
<filter-mapping>
<filter-name>loggingFilter</filter-name>
<!--通过映射路径确定过滤资源-->
<url-pattern>/servletA</url-pattern>
<!--通过后缀名确定过滤资源-->
<url-pattern>*.html</url-pattern>
<!--通过servlet别名确定过滤资源-->
<servlet-name>servletBName</servlet-name>
</filter-mapping>
转为注解
@WebFilter(
filterName = "loggingFilter",
initParams = {@WebInitParam(name="dateTimePattern",value="yyyy-MM-dd HH:mm:ss")},
urlPatterns = {"/servletA","*.html"},
servletNames = {"servletBName"}
)
监听器
概述
监听器:专门用于对域对象对象身上发生的事件或状态改变进行监听和相应处理的对象
监听器的分类
-
web中定义八个监听器接口作为监听器的规范,这八个接口按照不同的标准可以形成不同的分类
-
按监听的对象划分
-
application域监听器 ServletContextListener ServletContextAttributeListener
-
session域监听器 HttpSessionListener HttpSessionAttributeListener HttpSessionBindingListener HttpSessionActivationListener
-
request域监听器 ServletRequestListener ServletRequestAttributeListener
-
-
按监听的事件分
-
域对象的创建和销毁监听器 ServletContextListener HttpSessionListener ServletRequestListener
-
域对象数据增删改事件监听器 ServletContextAttributeListener HttpSessionAttributeListener ServletRequestAttributeListener
-
其他监听器 HttpSessionBindingListener HttpSessionActivationListener
-
使用
实现对应的接口,重写对应方法,然后在web.xml中声明监听器或直接@WebListener注解声明
例:
@WebListener
public class ApplicationListener implements ServletContextListener , ServletContextAttributeListener {
// 监听初始化
@Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext application = sce.getServletContext();
System.out.println("application"+application.hashCode()+" initialized");
}
// 监听销毁
@Override
public void contextDestroyed(ServletContextEvent sce) {
ServletContext application = sce.getServletContext();
System.out.println("application"+application.hashCode()+" destroyed");
}
// 监听数据增加
@Override
public void attributeAdded(ServletContextAttributeEvent scae) {
String name = scae.getName();
Object value = scae.getValue();
ServletContext application = scae.getServletContext();
System.out.println("application"+application.hashCode()+" add:"+name+"="+value);
}
// 监听数据移除
@Override
public void attributeRemoved(ServletContextAttributeEvent scae) {
String name = scae.getName();
Object value = scae.getValue();
ServletContext application = scae.getServletContext();
System.out.println("application"+application.hashCode()+" remove:"+name+"="+value);
}
// 监听数据修改
@Override
public void attributeReplaced(ServletContextAttributeEvent scae) {
String name = scae.getName();
Object value = scae.getValue();
ServletContext application = scae.getServletContext();
Object newValue = application.getAttribute(name);
System.out.println("application"+application.hashCode()+" change:"+name+"="+value+" to "+newValue);
}
}

浙公网安备 33010602011771号