第一章 Web开发概述

第一节 Web基础概念简介

1、服务器与客户端

①线下的服务器与客户端

image.png

②线上的服务器与客户端

image.png

③客户端的各种形式

[1]PC端网页

image.png

[2]移动端

image.png

[3]Iot设备

image.png

④服务器的各种形式

点我查看完整内容

2、服务器端应用程序

我们要开发的就是服务器端应用程序
image.png

3、业务

项目中的功能就是业务。

4、请求和响应

①发生在饭馆的请求和响应

image.png

②项目中的请求和响应

image.png

5、项目的逻辑构成

  • 请求:请求是项目中最基本的逻辑单元,就像万事万物都由原子构成举例:点超链接跳转到注册页面
  • 功能:一个功能包含很多个请求举例:注册用户功能
    • 请求1:点超链接跳转到注册页面
    • 请求2:发送请求获取短信验证码
    • 请求3:检查用户名是否可用
    • 请求4:提交表单完成注册
  • 模块:一个模块包含很多功能举例:用户信息管理模块
    • 功能1:用户注册功能
    • 功能2:用户登录功能
    • 功能3:个人中心——账户安全功能
    • 功能4:个人中心——账户绑定功能
    • 功能5:个人中心——收货地址功能
    • 功能6:个人中心——我的银行卡功能
  • 子系统:根据项目规模的不同,子系统这层逻辑概念可能有也可能没有。如果设置了子系统,那么子系统中也必然包含很多模块。其实庞大项目的子系统已经相当于一个项目了,甚至比小型项目整个都大。举例:认证中心子系统
    • 模块1:用户信息管理模块
    • 模块2:权限管理模块
    • 模块3:授权管理模块
    • 模块4:权限检查模块
  • 项目:为了解决现实生活中的实际问题开发一个项目,这个项目就是为这个需求提供的一整套解决方案。举例:电商项目
    • 子系统1:认证中心子系统
    • 子系统2:商品管理子系统
    • 子系统3:购物车子系统
    • 子系统4:仓储子系统
    • 子系统5:物流子系统
    • 子系统6:订单子系统

image.png

6、架构

①概念

『架构』其实就是项目的『结构』。只不过『结构』这个词太小了,不适合用来描述项目这么大的东西,所以换了另一个更大的词:架构。所以当我们聊一个项目的架构时,我们聊的是项目是由哪些部分组成的。

②发展演变历程

[1]单一架构

一个项目就是一个工程,这样的结构就是单一架构,也叫all in one。我们现在的JavaWeb阶段、SSM阶段都是学习单一架构开发技术。

[2]分布式架构

一个项目中包含很多工程,每个工程作为一个模块。模块之间存在调用关系。分布式架构阶段的技术分为两类:

  • Java框架:SpringBoot、SpringCloud、Dubbo等等。
  • 中间件:Redis、ElasticSearch、FastDFS、Nginx、Zookeeper、RabbitMQ等等。

image.png

③单一架构技术体系

  • 视图:用户的操作界面+数据的动态显示
    • 前端技术:HTML/CSS/JavaScript
    • 服务器端页面模板技术:Thymeleaf
  • 控制层:处理请求+跳转页面
    • 服务器:Tomcat
    • 控制器:Servlet
    • 域对象:request、session、servletContext
    • 过滤器:Filter
    • 监听器:Listener
    • 异步交互:Ajax
  • 业务逻辑层:业务逻辑计算
  • 持久化层:操作数据库

image.png

7、本阶段技术体系

image.png

8、本阶段案例简介

image.png

第二章 HTML&CSS

第一节 单一架构回顾

我们从现在的JavaWeb阶段到后面学习SSM框架阶段都是在学习单一架构项目开发的技术。而在JavaWeb阶段由于重点是探讨如何实现Web开发,所以必须学习一部分前端开发的技术。本节就是让大家明确我们现在要学习的内容在整个架构体系中处于什么位置。

1、单一架构技术体系

image.png

2、视图层

严格来说视图分成两层:

  • 前端技术:HTML/CSS/JavaScript
  • 服务器端页面模板技术:Thymeleaf

其中HTML、CSS、JavaScript都是工作在浏览器上的,所以它们都属于前端技术。而Thymeleaf是在服务器上把动态内容计算出具体数据,所以严格来说Thymeleaf是后端技术。
image.png

这里大家会有个疑问:为什么在『视图』这个地方已经有HTML、CSS、JavaScript这些前端技术了,能够生成用户可以操作的界面,那为什么还需要Thymeleaf这样一个后端技术呢? 简单来说原因是Thymeleaf=HTML+动态数据,而HTML不支持动态数据,这部分需要借助Thymeleaf来完成。

3、Web2.0

Web2.0是相对于更早的网页开发规范而提出的新规范。Web2.0规范之前的网页开发并没有明确的将HTML、CSS、JavaScript代码分开,而是互相之间纠缠在一起,导致代码维护困难,开发效率很低。

在开发中我们把这样彼此纠缠、互相影响的现象称为『耦合』。而把耦合在一起的东西拆解开,让他们彼此独立出来称为『解耦』。各个组成部分独立完成自己负责的功能,和其他模块无关称为『内聚』。 将来大家经常会听到一句话:软件开发提倡『 高内聚,低耦合』。 一个软件项目只有做到了高内聚、低耦合才能算得上结构严谨,模块化程度高,有利于开发和维护。

所以Web2.0规范主张将网页代码分成下面三个部分:

  • 结构:由HTML实现,负责管理网页的内容。将来网页上不管是静态还是动态的数据都是填写到HTML的标签里。
  • 表现:由CSS实现,负责管理网页内容的表现形式。比如:颜色、尺寸、位置、层级等等。也就是给数据穿上一身漂亮的衣服。
  • 行为:由JavaScript实现,负责实现网页的动态交互效果。比如:轮播图、表单验证、鼠标滑过显示下拉菜单、鼠标滑过改变背景颜色等等。

    第二节 HTML简介

    1、名词解释

    HTML是Hyper Text Markup Language的缩写。意思是『超文本标记语言』

    2、超文本

    HTML文件本质上是文本文件,而普通的文本文件只能显示字符。但是HTML技术则通过HTML标签把其他网页、图片、音频、视频等各种多媒体资源引入到当前网页中,让网页有了非常丰富的呈现方式,这就是超文本的含义——本身是文本,但是呈现出来的最终效果超越了文本。
    image.png

    3、标记语言

    说HTML是一种『标记语言』是因为它不是向Java这样的『编程语言』,因为它是由一系列『标签』组成的,没有常量、变量、流程控制、异常处理、IO等等这些功能。HTML很简单,每个标签都有它固定的含义和确定的页面显示效果。
    标签是通过一组尖括号+标签名的方式来定义的:
    1. <p>HTML is a very popular fore-end technology.</p>
    这个例子中使用了一个p标签来定义一个段落,

    『开始标签』

    『结束标签』。开始标签和结束标签一起构成了一个完整的标签。开始标签和结束标签之间的部分叫『文本标签体』,也简称『标签体』
    有的时候标签里还带有『属性』
    1. <a href="http://www.xxx.com">show detail</a>
    href=”http://www.xxx.com"就是属性,href是**『属性名』**,"http://www.xxx.com"是**『属性值』**。
    还有一种标签是『单标签』
    1. <input type="text" name="username" />

    4、HelloWorld

    image.png

    5、HTML文件结构

    ①文档类型声明

    HTML文件中第一行的内容,用来告诉浏览器当前HTML文档的基本信息,其中最重要的就是当前HTML文档遵循的语法标准。这里我们只需要知道HTML有4和5这两个大的版本,HTML4版本的文档类型声明是:
    1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    2. "http://www.w3.org/TR/html4/loose.dtd">
    HTML5版本的文档类型声明是:
    1. <!DOCTYPE html>
    现在主流的技术选型都是使用HTML5,之前的版本基本不用了。
    历史上HTML的各个版本:
版本名称 年份
HTML 1991
HTML+ 1993
HTML2.0 1995
HTML3.2 1997
HTML4.01 1999
XHTML1.0 2000
HTML5 2012
XHTML5 2013

②根标签

html标签是整个文档的根标签,所有其他标签都必须放在html标签里面。上面的文档类型不能当做普通标签看待。

所谓『根』其实是『树根』的意思。在一个树形结构中,根节点只能有一个。

③头部

head标签用于定义文档的头部,其他头部元素都放在head标签里。头部元素包括title标签、script标签、style标签、link标签、meta标签等等。

④主体

body标签定义网页的主体内容,在浏览器窗口内显示的内容都定义到body标签内。

⑤注释

HTML注释的写法是:

  1. <!-- 注释内容 -->

注释的内容不会显示到浏览器窗口内,是开发人员用来对代码内容进行解释说明。

6、HTML语法规则

  • 根标签有且只能有一个
  • 无论是双标签还是单标签都必须正确关闭
  • 标签可以嵌套但不能交叉嵌套
  • 注释不能嵌套
  • 属性必须有值,值必须加引号,单引号或双引号均可
  • 标签名不区分大小写但建议使用小写

    第三节 使用HTML展示文章

    以文章的组织形式展示数据是HTML最基本的功能了,网页上显示的文章在没有做任何CSS样式设定的情况下如下图所示:
    image.png
    本节我们要学习的HTML标签如下表:
标签名称 功能
h1~h6 1级标题~6级标题
p 段落
a 超链接
ul/li 无序列表
img 图片
div 定义一个前后有换行的块
span 定义一个前后无换行的块

为了方便编写代码,我们在IDEA中创建一个静态Web工程来操作:
image.png

1、标题标签

①代码

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <h1>这是一级标题</h1>
  9. <h2>这是二级标题</h2>
  10. <h3>这是三级标题</h3>
  11. <h4>这是四级标题</h4>
  12. <h5>这是五级标题</h5>
  13. <h6>这是六级标题</h6>
  14. </body>
  15. </html>

②页面显示效果

image.png
注意:标题标签前后有换行。

2、段落标签

①代码

  1. <p>There is clearly a need for CSS to be taken seriously by graphic artists. The Zen Garden aims to excite, inspire, and encourage participation. To begin, view some of the existing designs in the list. Clicking on any one will load the style sheet into this very page. The code remains the same, the only thing that has changed is the external .css file. Yes, really.</p>

②页面显示效果

image.png
注意:段落标签前后有换行。

3、超链接

①代码

  1. <a href="page02-anchor-target.html">点我跳转到下一个页面</a>

②页面显示效果

image.png
点击后跳转到href属性指定的页面

4、路径

在我们整个Web开发技术体系中,『路径』是一个贯穿始终的重要概念。凡是需要获取另外一个资源的时候都需要用到路径。要想理解路径这个概念,我们首先要认识一个概念:『文件系统』。

①文件系统

我们写代码的时候通常都是在Windows系统来操作,而一个项目开发完成后想要让所有人都能够访问到就必须『部署』到服务器上,也叫『发布』。而服务器通常是Linux系统。
Windows系统和Linux系统的文件系统有很大差别,为了让我们编写的代码不会因为从Windows系统部署到了Linux系统而出现故障,实际开发时不允许使用物理路径
物理路径举例:
D:\aaa\pro01-HTML\page01-article-tag.html
D:\aaa\pro01-HTML\page02-anchor-target.html
幸运的是不管是Windows系统还是Linux系统环境下,目录结构都是树形结构,编写路径的规则是一样的。
image.png
所以我们以项目的树形目录结构为依据来编写路径就不用担心操作系统平台发生变化之后路径错误的问题了。有了这个大前提,我们具体编写路径时有两种具体写法:

  • 相对路径
  • 绝对路径(建议使用)

    ②相对路径

    相对路径都是以『当前位置』为基准来编写的。假设我们现在正在浏览a页面,想在a页面内通过超链接跳转到z页面。
    image.png
    那么按照相对路径的规则,我们现在所在的位置是a.html所在的b目录:
    image.png
    z.html并不在b目录下,所以我们要从b目录出发,向上走,进入b的父目录——c目录:
    image.png
    c目录还是不行,继续向上走,进入c的父目录——d目录:
    image.png
    在从d目录向下经过两级子目录——e目录、f目录才能找到z.html:
    image.png
    所以整个路径的写法是:
    1. <a href="../../e/f/z.html">To z.html</a>
    可以看到使用相对路径有可能会很繁琐,而且在后面我们结合了在服务器上运行的Java程序后,相对路径的基准是有可能发生变化的,所以不建议使用相对路径

    ③绝对路径

    [1]通过IDEA服务器打开HTML文件
    测试绝对路径的前提是通过IDEA的内置服务器访问我们编写的HTML页面——这样访问地址的组成结构才能和我们以后在服务器上运行的Java程序一致。
    image.png
    image.png
    [2]服务器访问地址的组成
    image.png
    [3]绝对路径的写法
    绝对路径要求必须是以『正斜线』开头。这个开头的正斜线在整个服务器访问地址中对应的位置如下图所示:
    image.png
    这里标注出的这个位置代表的是『服务器根目录』,从这里开始我们就是在服务器的内部查找一个具体的Web应用。
    所以我们编写绝对路径时就从这个位置开始,按照目录结构找到目标文件即可。拿前面相对路径中的例子来说,我们想在a.html页面中通过超链接访问z.html。此时路径从正斜线开始,和a.html自身所在位置没有任何关系:
    image.png
    1. <a href="/d/e/f/z.html">To z.html</a>
    [4]具体例子
    编写超链接访问下面的页面:
    image.png
    1. <a href="/aaa/pro01-HTML/animal/cat/miao.html">Cat Page</a>
    [5]小结
    为了和我们后面学习的内容和正确的编码方式保持一致,建议大家从现在开始就使用绝对路径。

    5、换行

    ①代码

    1. We would like to see as much CSS1 as possible. CSS2 should be limited to widely-supported elements only. The css Zen Garden is about functional, practical CSS and not the latest bleeding-edge tricks viewable by 2% of the browsing public. <br/>The only real requirement we have is that your CSS validates.

    ②页面显示效果

    image.png

    6、无序列表

    ①代码

    1. <ul>
    2. <li>Apple</li>
    3. <li>Banana</li>
    4. <li>Grape</li>
    5. </ul>

    ②页面显示效果

    image.png

    7、图片

    ①准备图片文件

    image.png

    ②代码

    src属性用来指定图片文件的路径,这里同样按我们前面说的使用『绝对路径』
    1. <img src="/aaa/pro01-HTML/./images/mi.jpg"/>

    ③页面显示效果

    image.png

    8、块

    『块』并不是为了显示文章内容的,而是为了方便结合CSS对页面进行布局。块有两种,div是前后有换行的块,span是前后没有换行的块。
    把下面代码粘贴到HTML文件中查看他们的区别: ```html
    This is a div block
    This is a div block

This is a span block This is a span block

  1. 页面显示效果为:<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/26803611/1652544657774-51a34b74-4260-481d-be4d-ce6ab86142f8.png#clientId=u4b34b2fa-a207-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=udd706fbb&margin=%5Bobject%20Object%5D&name=image.png&originHeight=238&originWidth=318&originalType=url&ratio=1&rotation=0&showTitle=false&size=4277&status=done&style=none&taskId=uc13496af-c574-4693-950d-9adc9b8b950&title=)
  2. <a name="kp3tU"></a>
  3. ### 9、HTML实体
  4. HTML文件中,<、>等等这样的符号已经被赋予了特定含义,不会作为符号本身显示到页面上,此时如果我们想使用符号本身怎么办呢?那就是使用HTML实体来转义。<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/26803611/1652544657880-b3887ef2-5795-4d9b-894c-3251017f935f.png#clientId=u4b34b2fa-a207-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=ucf3f43a8&margin=%5Bobject%20Object%5D&name=image.png&originHeight=730&originWidth=823&originalType=url&ratio=1&rotation=0&showTitle=false&size=49850&status=done&style=none&taskId=u39cfcc1a-6ceb-473d-933a-e406afca7ba&title=)<br />资料来源:[W3School](https://www.w3school.com.cn/html/html_entities.asp)
  5. <a name="mAMU7"></a>
  6. ## 第四节 使用HTML表单收集数据
  7. <a name="newI5"></a>
  8. ### 1、什么是表单
  9. 在项目开发过程中,凡是需要用户填写的信息都需要用到表单。<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/26803611/1652545263089-138bf9b1-9db1-4ac5-8e78-e774ee541349.png#clientId=u4b34b2fa-a207-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=uc5e90287&margin=%5Bobject%20Object%5D&name=image.png&originHeight=670&originWidth=761&originalType=url&ratio=1&rotation=0&showTitle=false&size=26164&status=done&style=none&taskId=ue5f2bb1f-72c3-4a7b-9a57-56179f6e008&title=)
  10. <a name="zKHrW"></a>
  11. ### 2、form标签
  12. HTML中我们使用form标签来定义一个表单。而对于form标签来说有两个最重要的属性:actionmethod
  13. ```html
  14. <form action="/aaa/pro01-HTML/page05-form-target.html" method="post">
  15. </form>

①action属性

用户在表单里填写的信息需要发送到服务器端,对于Java项目来说就是交给Java代码来处理。那么在页面上我们就必须正确填写服务器端的能够接收表单数据的地址。
这个地址要写在form标签的action属性中。但是现在暂时我们还没有服务器端环境,所以先借用一个HTML页面来当作服务器端地址使用。

②method属性

『method』这个单词的意思是『方式、方法』,在form标签中method属性用来定义提交表单的『请求方式』。method属性只有两个可选值:get或post,没有极特殊情况的话使用post即可。

什么是『请求方式』? 浏览器和服务器之间在互相通信时有大量的『数据』需要传输。但是不论是浏览器还是服务器都有很多不同厂商提供的不同产品。 常见的浏览器有:

  • Chrome
  • Firefox
  • Safari
  • Opera
  • Edge

常见的Java服务器有:

  • Tomcat
  • Weblogic
  • WebSphere
  • Glassfish
  • Jetty

这么多不同厂商各自开发的应用程序怎么能保证它们彼此之间传输的『数据』能够被对方正确理解呢? 很简单,我们给这些数据设定『格式』,发送端按照格式发送数据,接收端按照格式解析数据,这样就能够实现数据的『跨平台传输』了。 而这里定义的『数据格式』就是应用程序之间的『通信协议』。 在JavaSE阶段的网络编程章节我们接触过TCP/IP、UDP这样的协议,而我们现在使用的『HTTP协议』的底层就是TCP/IP协议。 HTTP1.1中共定义了八种请求方式:

  • GET
  • POST
  • PUT
  • DELETE
  • HEAD
  • CONNECT
  • OPTIONS
  • TRACE

但是在HTML标签中,点击超链接是GET方式的请求,提交一个表单可以通过form标签的method属性指定GET或POST请求,其他请求方式无法通过HTML标签实现。除了GET、POST之外的其他请求方式暂时我们不需要涉及(到我们学习SpringMVC时会用到PUT和DELETE)。至于GET请求和POST请求的区别我们会在讲HTTP协议的时候详细介绍,现在大家可以从表面现象来观察一下。

3、name和value

在用户使用一个软件系统时,需要一次性提交很多数据是非常正常的现象。我们肯定不能要求用户一个数据一个数据的提交,而肯定是所有数据填好后一起提交。那就带来一个问题,服务器怎么从众多数据中识别出来收货人、所在地区、详细地址、手机号码……?
很简单,给每个数据都起一个『名字』,发送数据时用『名字』携带对应的数据,接收数据时通过『名字』获取对应的数据。
在各个具体的表单标签中,我们通过『name属性』来给数据起『名字』,通过『value属性』来保存要发送给服务器的『值』
但是名字和值之间既有可能是『一个名字对应一个值』,也有可能是『一个名字对应多个值』
这么看来这样的关系很像我们Java中的Map,而事实上在服务器端就是使用Map类型来接收请求参数的。具体的是类型是:Map
name属性就是Map的键,value属性就是Map的值。
有了上面介绍的基础知识,下面我们就可以来看具体的表单标签了。

4、单行文本框

①代码

  1. 个性签名:<input type="text" name="signal"/><br/>

②显示效果

image.png

5、密码框

①代码

  1. 密码:<input type="password" name="secret"/><br/>

②显示效果

image.png

6、单选框

①代码

  1. 你最喜欢的季节是:
  2. <input type="radio" name="season" value="spring" />春天
  3. <input type="radio" name="season" value="summer" checked="checked" />夏天
  4. <input type="radio" name="season" value="autumn" />秋天
  5. <input type="radio" name="season" value="winter" />冬天
  6. <br/><br/>
  7. 你最喜欢的动物是:
  8. <input type="radio" name="animal" value="tiger" />路虎
  9. <input type="radio" name="animal" value="horse" checked="checked" />宝马
  10. <input type="radio" name="animal" value="cheetah" />捷豹

②效果

image.png

③说明

  • name属性相同的radio为一组,组内互斥
  • 当用户选择了一个radio并提交表单,这个radio的name属性和value属性组成一个键值对发送给服务器
  • 设置checked=”checked”属性设置默认被选中的radio

    7、多选框

    ①代码

    1. 你最喜欢的球队是:
    2. <input type="checkbox" name="team" value="Brazil"/>巴西
    3. <input type="checkbox" name="team" value="German" checked="checked"/>德国
    4. <input type="checkbox" name="team" value="France"/>法国
    5. <input type="checkbox" name="team" value="China" checked="checked"/>中国
    6. <input type="checkbox" name="team" value="Italian"/>意大利

    ②效果

    image.png

    8、下拉列表

    ①代码

    1. 你喜欢的运动是:
    2. <select name="interesting">
    3. <option value="swimming">游泳</option>
    4. <option value="running">跑步</option>
    5. <option value="shooting" selected="selected">射击</option>
    6. <option value="skating">溜冰</option>
    7. </select>

    ②效果

    image.png

    ③说明

  • 下拉列表用到了两种标签,其中select标签用来定义下拉列表,而option标签设置列表项。

  • name属性在select标签中设置。
  • value属性在option标签中设置。
  • option标签的标签体是显示出来给用户看的,提交到服务器的是value属性的值。
  • 通过在option标签中设置selected=”selected”属性实现默认选中的效果。

    9、按钮

    ①代码

    1. <button type="button">普通按钮</button>
    2. <button type="reset">重置按钮</button>
    3. <button type="submit">提交按钮</button>

    ②效果

    image.png

    ③说明

    | 类型 | 功能 | | —- | —- | | 普通按钮 | 点击后无效果,需要通过JavaScript绑定单击响应函数 | | 重置按钮 | 点击后将表单内的所有表单项都恢复为默认值 | | 提交按钮 | 点击后提交表单 |

10、表单隐藏域

①代码

  1. <input type="hidden" name="userId" value="2233"/>

②说明

通过表单隐藏域设置的表单项不会显示到页面上,用户看不到。但是提交表单时会一起被提交。用来设置一些需要和表单一起提交但是不希望用户看到的数据,例如:用户id等等。

11、多行文本框

①代码

  1. 自我介绍:<textarea name="desc"></textarea>

②效果

image.png

③说明

textarea没有value属性,如果要设置默认值需要写在开始和结束标签之间。

第五节 CSS的简单应用

1、CSS效果示例

未使用CSS样式
使用第一套CSS样式
使用第二套CSS样式
使用第三套CSS样式
使用第四套CSS样式

2、设置CSS样式的三种方式

①在HTML标签内设置

仅对当前标签有效

  1. <div style="border: 1px solid black;width: 100px; height: 100px;">&nbsp;</div>

image.png

②在head标签内设置

对当前页面有效

  1. <head>
  2. <meta charset="UTF-8">
  3. <title>Title</title>
  4. <style type="text/css">
  5. .one {
  6. border: 1px solid black;
  7. width: 100px;
  8. height: 100px;
  9. background-color: lightgreen;
  10. margin-top: 5px;
  11. }
  12. </style>
  13. </head>
  14. <body>
  15. <div style="border: 1px solid black;width: 100px; height: 100px;">&nbsp;</div>
  16. <div class="one">&nbsp;</div>
  17. <div class="one">&nbsp;</div>
  18. <div class="one">&nbsp;</div>
  19. </body>

image.png

③引入外部CSS样式文件

[1]创建CSS文件

image.png

[2]编辑CSS文件
  1. .two {
  2. border: 1px solid black;
  3. width: 100px;
  4. height: 100px;
  5. background-color: yellow;
  6. margin-top: 5px;
  7. }

[3]引入外部CSS文件

在需要使用这个CSS文件的HTML页面的head标签内加入:

  1. <link rel="stylesheet" type="text/css" href="/aaa/pro01-HTML/style/example.css" />

于是下面HTML代码的显示效果是:

  1. <div class="two">&nbsp;</div>
  2. <div class="two">&nbsp;</div>
  3. <div class="two">&nbsp;</div>

image.png

3、CSS代码语法

  • CSS样式由选择器和声明组成,而声明又由属性和值组成。
  • 属性和值之间用冒号隔开。
  • 多条声明之间用分号隔开。
  • 使用//声明注释。

image.png

4、CSS选择器

①标签选择器

HTML代码:

  1. <p>Hello, this is a p tag.</p>
  2. <p>Hello, this is a p tag.</p>
  3. <p>Hello, this is a p tag.</p>
  4. <p>Hello, this is a p tag.</p>
  5. <p>Hello, this is a p tag.</p>

CSS代码:

  1. p {
  2. color: blue;
  3. font-weight: bold;
  4. }

image.png

②id选择器

HTML代码:

  1. <p>Hello, this is a p tag.</p>
  2. <p>Hello, this is a p tag.</p>
  3. <p id="special">Hello, this is a p tag.</p>
  4. <p>Hello, this is a p tag.</p>
  5. <p>Hello, this is a p tag.</p>

CSS代码:

  1. #special {
  2. font-size: 20px;
  3. background-color: aqua;
  4. }

image.png

#③类选择器

HTML代码:

  1. <div class="one">&nbsp;</div>
  2. <div class="one">&nbsp;</div>
  3. <div class="one">&nbsp;</div>

CSS代码:

  1. .one {
  2. border: 1px solid black;
  3. width: 100px;
  4. height: 100px;
  5. background-color: lightgreen;
  6. margin-top: 5px;
  7. }

image.png

第三章 JavaScript

第一节 JavaScript简介

1、起源

1995年时,由Netscape公司的Brendan Eich,在网景导航者浏览器上首次设计实现而成。Netscape在最初将其脚本语言命名为LiveScript,因为Netscape与Sun合作,网景公司管理层希望它外观看起来像Java,因此取名为JavaScript。

2、特性

①脚本语言

JavaScript是一种解释型的脚本语言。不同于C、C++、Java等语言先编译后执行, JavaScript不会产生编译出来的字节码文件,而是在程序的运行过程中对源文件逐行进行解释。

②基于对象

JavaScript是一种基于对象的脚本语言,它不仅可以创建对象,也能使用现有的对象。但是面向对象的三大特性:『封装』、『继承』、『多态』中,JavaScript能够实现封装,可以模拟继承,不支持多态,所以它不是一门面向对象的编程语言。

③弱类型

JavaScript中也有明确的数据类型,但是声明一个变量后它可以接收任何类型的数据,并且会在程序执行过程中根据上下文自动转换类型。

④事件驱动

JavaScript是一种采用事件驱动的脚本语言,它不需要经过Web服务器就可以对用户的输入做出响应。

⑤跨平台性

JavaScript脚本语言不依赖于操作系统,仅需要浏览器的支持。因此一个JavaScript脚本在编写后可以带到任意机器上使用,前提是机器上的浏览器支持JavaScript脚本语言。目前JavaScript已被大多数的浏览器所支持。

第二节 HelloWorld

1、功能效果图

image.png

2、代码实现

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title>HelloWorld</title>
  6. </head>
  7. <body>
  8. <!-- 在HTML代码中定义一个按钮 -->
  9. <button type="button" id="helloBtn">SayHello</button>
  10. </body>
  11. <!-- 在script标签中编写JavaScript代码 -->
  12. <script type="text/javascript">
  13. // document对象代表整个HTML文档
  14. // document对象调用getElementById()方法表示根据id查找对应的元素对象
  15. var btnElement = document.getElementById("helloBtn");
  16. // 给按钮元素对象绑定单击响应函数
  17. btnElement.onclick = function(){
  18. // 弹出警告框
  19. alert("hello");
  20. };
  21. </script>
  22. </html>

第三节 JavaScript基本语法

1、JavaScript代码嵌入方式

①HTML文档内

  • JavaScript代码要写在script标签内
  • script标签可以写在文档内的任意位置
  • 为了能够方便查询或操作HTML标签(元素)script标签可以写在body标签后面

可以参考简化版的HelloWorld

  1. <!-- 在HBuilderX中,script标签通过打字“sc”两个字母就可以直接完整生成 -->
  2. <script type="text/javascript">
  3. // 下面是同样实现HelloWorld功能的简化版代码
  4. document.getElementById("helloBtn").onclick = function() {
  5. alert("Hello simple");
  6. };
  7. </script>

②引入外部JavaScript文档

在script标签内通过src属性指定外部xxx.js文件的路径即可。但是要注意以下两点:

  • 引用外部JavaScript文件的script标签里面不能写JavaScript代码
  • 先引入,再使用
  • script标签不能写成单标签

image.png
引入方式如下:

  1. <body>
  2. </body>
  3. <!-- 使用script标签的src属性引用外部JavaScript文件,和Java中的import语句类似 -->
  4. <!-- 引用外部JavaScript文件的script标签里面不能写JavaScript代码 -->
  5. <!-- 引用外部JavaScript文件的script标签不能改成单标签 -->
  6. <!-- 外部JavaScript文件一定要先引入再使用 -->
  7. <script src="/pro02-JavaScript/scripts/outter.js" type="text/javascript" charset="utf-8"></script>
  8. <script type="text/javascript">
  9. // 调用外部JavaScript文件中声明的方法
  10. showMessage();
  11. </script>

2、声明和使用变量

①JavaScript数据类型

  • 基本数据类型

    • 数值型:JavaScript不区分整数、小数
    • 字符串:JavaScript不区分字符、字符串;单引号、双引号意思一样。
    • 布尔型:true、false
      • 在JavaScript中,其他类型和布尔类型的自动转换。
      • true:非零的数值,非空字符串,非空对象
      • false:零,空字符串,null,undefined
      • 例如:”false”放在if判断中
        1. // "false"是一个非空字符串,直接放在if判断中会被当作『真』处理
        2. if("false"){
        3. alert("true");
        4. }else{
        5. alert("false");
        6. }
  • 引用类型

    • 所有new出来的对象
    • 用[]声明的数组
    • 用{}声明的对象

      ②变量

  • 关键字:var

  • 数据类型:JavaScript变量可以接收任意类型的数据
  • 标识符:严格区分大小写
  • 变量使用规则

    • 如果使用了一个没有声明的变量,那么会在运行时报错Uncaught ReferenceError: b is not defined
    • 如果声明一个变量没有初始化,那么这个变量的值就是undefined

      3、函数

      ①内置函数

      内置函数:系统已经声明好了可以直接使用的函数。

      [1]弹出警告框
      1. alert("警告框内容");

      [2]弹出确认框

      用户点击『确定』返回true,点击『取消』返回false

      1. var result = confirm("老板,你真的不加个钟吗?");
      2. if(result) {
      3. console.log("老板点了确定,表示要加钟");
      4. }else{
      5. console.log("老板点了确定,表示不加钟");
      6. }

      [3]在控制台打印日志
      1. console.log("日志内容");

      image.png

      ②声明函数

      写法1:

      1. function sum(a, b) {
      2. return a+b;
      3. }

      写法2:

      1. var total = function() {
      2. return a+b;
      3. };

      写法2可以这样解读:声明一个函数,相当于创建了一个『函数对象』,将这个对象的『引用』赋值给变量total。最后加的分号不是给函数声明加的,而是给整体的赋值语句加的分号。

      ③调用函数

      JavaScript中函数本身就是一种对象,函数名就是这个『对象』『引用』。而调用函数的格式是:函数引用()

      1. function sum(a, b) {
      2. return a+b;
      3. }
      4. var result = sum(2, 3);
      5. console.log("result="+result);

      或:

      1. var total = function() {
      2. return a+b;
      3. }
      4. var totalResult = total(3,6);
      5. console.log("totalResult="+totalResult);

      4、对象

      JavaScript中没有『类』的概念,对于系统内置的对象可以直接创建使用。

      ①使用new关键字创建对象

      1. // 创建对象
      2. var obj01 = new Object();
      3. // 给对象设置属性和属性值
      4. obj01.stuName = "tom";
      5. obj01.stuAge = 20;
      6. obj01.stuSubject = "java";
      7. // 在控制台输出对象
      8. console.log(obj01);

      ②使用{}创建对象

      1. // 创建对象
      2. var obj02 = {
      3. "soldierName":"john",
      4. "soldierAge":35,
      5. "soldierWeapon":"gun"
      6. };
      7. // 在控制台输出对象
      8. console.log(obj02);

      ③给对象设置函数属性

      ```javascript // 创建对象 var obj01 = new Object();

// 给对象设置属性和属性值 obj01.stuName = “tom”; obj01.stuAge = 20; obj01.stuSubject = “java”;

obj01.study = function() { console.log(this.stuName + “ is studying”); };

// 在控制台输出对象 console.log(obj01); // 调用函数 obj01.study();

  1. 或:
  2. ```javascript
  3. // 创建对象
  4. var obj02 = {
  5. "soldierName":"john",
  6. "soldierAge":35,
  7. "soldierWeapon":"gun",
  8. "soldierShoot":function(){
  9. console.log(this.soldierName + " is using " + this.soldierWeapon);
  10. }
  11. };
  12. // 在控制台输出对象
  13. console.log(obj02);
  14. // 调用函数
  15. obj02.soldierShoot();

④this关键字

this关键字只有两种情况:

  • 在函数外面:this关键字指向window对象(代表当前浏览器窗口)
  • 在函数里面:this关键字指向调用函数的对象 ```javascript // 直接打印this console.log(this);

// 函数中的this // 1.声明函数 function getName() { console.log(this.name); }

// 2.创建对象 var obj01 = { “name”:”tom”, “getName”:getName }; var obj02 = { “name”:”jerry”, “getName”:getName };

// 3.调用函数 obj01.getName(); obj02.getName();

  1. <a name="QLVTb"></a>
  2. ### 5、数组
  3. <a name="j3GcY"></a>
  4. #### ①使用new关键字创建数组
  5. ```javascript
  6. // 1.创建数组对象
  7. var arr01 = new Array();
  8. // 2.压入数据
  9. arr01.push("apple");
  10. arr01.push("orange");
  11. arr01.push("banana");
  12. arr01.push("grape");
  13. // 3.遍历数组
  14. for (var i = 0; i < arr01.length; i++) {
  15. console.log(arr01[i]);
  16. }
  17. // 4.数组元素反序
  18. arr01.reverse();
  19. for (var i = 0; i < arr01.length; i++) {
  20. console.log(arr01[i]);
  21. }
  22. // 5.数组元素拼接成字符串
  23. var arrStr = arr01.join(",");
  24. console.log(arrStr);
  25. // 6.字符串拆分成数组
  26. var arr02 = arrStr.split(",");
  27. for (var i = 0; i < arr02.length; i++) {
  28. console.log(arr02[i]);
  29. }
  30. // 7.弹出数组中最后一个元素
  31. var ele = arr01.pop();
  32. console.log(ele);

②使用[]创建数组

  1. // 8.使用[]创建数组
  2. var arr03 = ["cat","dog","tiger"];
  3. console.log(arr03);

6、JSON

①JSON格式的用途

在开发中凡是涉及到『跨平台数据传输』,JSON格式一定是首选。

②JSON格式的说明

  • JSON数据两端要么是{},要么是[]
  • {}定义JSON对象
  • []定义JSON数组
  • JSON对象的格式是:

    1. {key:value,key:value,...,key:value}
  • JOSN数组的格式是:

    1. [value,value,...,value]
  • key的类型固定是字符串

  • value的类型可以是:
    • 基本数据类型
    • 引用类型:JSON对象或JSON数组

正因为JSON格式中value部分还可以继续使用JSON对象或JSON数组,所以JSON格式是可以『多层嵌套』的,所以JSON格式不论多么复杂的数据类型都可以表达。

  1. {
  2. "stuId":556,
  3. "stuName":"carl",
  4. "school":{
  5. "schoolId":339,
  6. "schoolName":"atguigu"
  7. },
  8. "subjectList":[
  9. {
  10. "subjectName":"java",
  11. "subjectScore":50
  12. },
  13. {
  14. "subjectName":"PHP",
  15. "subjectScore":35
  16. },
  17. {
  18. "subjectName":"python",
  19. "subjectScore":24
  20. }
  21. ],
  22. "teacherMap":{
  23. "aaa":{
  24. "teacherName":"zhangsan",
  25. "teacherAge":20
  26. },
  27. "bbb":{
  28. "teacherName":"zhangsanfeng",
  29. "teacherAge":108
  30. },
  31. "ccc":{
  32. "teacherName":"zhangwuji",
  33. "teacherAge":25
  34. }
  35. }
  36. }

③JSON对象和JSON字符串互转

[1]JSON对象转JSON字符串
  1. var jsonObj = {"stuName":"tom","stuAge":20};
  2. var jsonStr = JSON.stringify(jsonObj);
  3. console.log(typeof jsonObj); // object
  4. console.log(typeof jsonStr); // string

[2]JSON字符串转JSON对象
  1. jsonObj = JSON.parse(jsonStr);
  2. console.log(jsonObj); // {stuName: "tom", stuAge: 20}

第四节 DOM

1、概念

①名词解释

DOM是Document Object Model的缩写,意思是『文档对象模型』——将HTML文档抽象成模型,再封装成对象方便用程序操作。
这是一种非常常用的编程思想:将现实世界的事物抽象成模型,这样就非常容易使用对象来量化的描述现实事物,从而把生活中的问题转化成一个程序问题,最终实现用应用软件协助解决现实问题。而在这其中『模型』就是那个连通现实世界和代码世界的桥梁。

②DOM树

浏览器把HTML文档从服务器上下载下来之后就开始按照『从上到下』的顺序『读取HTML标签』。每一个标签都会被封装成一个『对象』
而第一个读取到的肯定是根标签html,然后是它的子标签head,再然后是head标签里的子标签……所以从html标签开始,整个文档中的所有标签都会根据它们之间的『父子关系』被放到一个『树形结构』的对象中。
image.png
这个包含了所有标签对象的整个树形结构对象就是JavaScript中的一个可以直接使用的内置对象document
例如,下面的标签结构:
image.png
会被解析为:
image.png

2、具体概念

①各个组成部分的类型

整个文档中的一切都可以看做Node。各个具体组成部分的具体类型可以看做Node类型的子类。
其实严格来说,JavaScript并不支持真正意义上的『继承』,这里我们借用Java中的『继承』概念,从逻辑上来帮助我们理解各个类型之间的关系。

组成部分 节点类型 具体类型
整个文档 文档节点 Document
HTML标签 元素节点 Element
HTML标签内的文本 文本节点 Text
HTML标签内的属性 属性节点 Attr
注释 注释节点 Comment

②父子关系

image.png

③先辈后代关系

image.png

3、DOM操作

由于实际开发时基本上都是使用JavaScript的各种框架来操作,而框架中的操作方式和我们现在看到的原生操作完全不同,所以下面罗列的API仅供参考,不做要求。

①在整个文档范围内查询元素节点

功能 API 返回值
根据id值查询 document.getElementById(“id值”) 一个具体的元素节
根据标签名查询 document.getElementsByTagName(“标签名”) 元素节点数组
根据name属性值查询 document.getElementsByName(“name值”) 元素节点数组

②在具体元素节点范围内查找子节点

功能 API 返回值
查找全部子节点 element.childNodes
【W3C考虑换行,IE≤8不考虑】
节点数组
查找第一个子节点 element.firstChild
【W3C考虑换行,IE≤8不考虑】
节点对象
查找最后一个子节点 element.lastChild
【W3C考虑换行,IE≤8不考虑】
节点对象

③查找指定元素节点的父节点

功能 API 返回值
查找指定元素节点的父节点 element.parentNode 节点对象

④查找指定元素节点的兄弟节点

功能 API 返回值
查找前一个兄弟节点 node.previousSibling
【W3C考虑换行,IE≤8不考虑】
节点对象
查找后一个兄弟节点 node.nextSibling
【W3C考虑换行,IE≤8不考虑】
节点对象

⑤属性操作

需求 操作方式
读取属性值 元素对象.属性名
修改属性值 元素对象.属性名=新的属性值

⑥文本操作

需求 操作方式
读取文本值 element.firstChild.nodeValue
修改文本值 element.firstChild.nodeValue=新文本值

⑦DOM增删改操作

API 功能
document.createElement(“标签名”) 创建元素节点并返回,但不会自动添加到文档中
document.createTextNode(“文本值”) 创建文本节点并返回,但不会自动添加到文档中
element.appendChild(ele) 将ele添加到element所有子节点后面
parentEle.insertBefore(newEle,targetEle) 将newEle插入到targetEle前面
parentEle.replaceChild(newEle, oldEle) 用新节点替换原有的旧子节点
parentEle.removeChild(childNode) 删除指定的子节点
element.innerHTML 读写HTML代码

第五节 JavaScript事件驱动

image.png

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title></title>
  6. <style type="text/css">
  7. #eventArea {
  8. border: 1px solid black;
  9. width: 100px;
  10. height: 100px;
  11. }
  12. </style>
  13. </head>
  14. <body>
  15. <!-- 用div作为鼠标移动区域 -->
  16. <div id="eventArea"></div>
  17. <!-- 在p标签内显示鼠标坐标 -->
  18. <p id="showData"></p>
  19. </body>
  20. <script type="text/javascript">
  21. // 根据id找到div标签对应的元素对象
  22. var divEle = document.getElementById("eventArea");
  23. // 根据id找到p标签对应的元素对象
  24. var pEle = document.getElementById("showData");
  25. // 声明事件响应函数
  26. function whenMouseMove(event){
  27. pEle.innerText = event.clientX + " " + event.clientY;
  28. }
  29. // 将事件响应函数赋值给对应的事件属性
  30. // onmousemove表示在鼠标移动的时候
  31. divEle.onmousemove = whenMouseMove;
  32. </script>
  33. </html>

下面是简化的代码:

  1. document.getElementById("eventArea").onmousemove = function (event){
  2. document.getElementById("showData").innerText = event.clientX + " " + event.clientY;
  3. };

第四章 Vue.js

第一节 Vue.js简介

1、框架

任何编程语言在最初的时候都是没有框架的,后来随着在实际开发过程中不断总结『经验』,积累『最佳实践』,慢慢的人们发现很多『特定场景』下的『特定问题』总是可以『套用固定解决方案』
于是有人把成熟的『固定解决方案』收集起来,整合在一起,就成了『框架』
在使用框架的过程中,我们往往只需要告诉框架『做什么(声明)』,而不需要关心框架『怎么做(编程)』
对于Java程序来说,我们使用框架就是导入那些封装了『固定解决方案』的jar包,然后通过『配置文件』告诉框架做什么,就能够大大简化编码,提高开发效率。我们使用过的junit其实就是一款单元测试框架。
而对于JavaScript程序来说,我们使用框架就是导入那些封装了『固定解决方案』『js文件』,然后在框架的基础上编码。

用洗衣服来类比框架: 典型应用场景:洗衣服 输入数据:衣服、洗衣液、水 不使用框架:手洗 使用框架:使用洗衣机,对人来说,只需要按键,具体操作是洗衣机完成的。人只是告诉洗衣机做什么,具体的操作是洗衣机完成的。

实际开发中使用框架时,我们也主要是告诉框架要做什么,具体操作是框架完成的。

2、Vue.js

①Vue.js的作者

在为AngularJS工作之后,Vue的作者尤雨溪开发了Vue.js。他声称自己的思路是提取Angular中自己喜欢的部分,构建出一款相当轻量的框架。
image.png
Vue最早发布于2014年2月。作者在Hacker News、Echo JS与 Reddit的JavaScript版块发布了最早的版本。一天之内,Vue 就登上了这三个网站的首页。
Vue是Github上最受欢迎的开源项目之一。同时,在JavaScript框架/函数库中, Vue所获得的星标数已超过React,并高于Backbone.js、Angular 2、jQuery等项目。

②Vue.js的官网介绍

Vue (读音 /vjuː/,类似于view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链(opens new window)以及各种支持类库(opens new window)结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
Vue.js官网网址

第二节 准备Vue.js环境

1、开发中的最佳实践

『最佳实践』是实际开发中,针对特定问题提炼出来的最好的解决方案。把『最佳实践』抽取出来,封装到各自编程语言的程序包中,就是框架的基础。

  • Java语言的程序包:jar包
  • JavaScript语言的程序包:外部js文件

对于Java程序来说,框架=jar包+配置文件。对于Vue来说,导入Vue的外部js文件就能够使用Vue框架了。

2、Vue框架的js文件获取

官网提供的下载地址:https://cdn.jsdelivr.net/npm/vue/dist/vue.js

3、本地创建vue.js文件

第一步:在HBuilderX中创建工程
第二步:在工程目录下创建script目录用来存放vue.js文件
第三步:创建空vue.js文件
image.png
第四步:将官网提供的vue.js文件的内容复制粘贴到本地vue.js文件中

4、创建HTML文档并引入vue.js

image.png

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title></title>
  6. </head>
  7. <body>
  8. </body>
  9. <script src="/pro03-vue/script/vue.js" type="text/javascript" charset="utf-8"></script>
  10. <script type="text/javascript">
  11. </script>
  12. </html>

第三节 Vue.js基本语法:声明式渲染

1、概念

①声明式

『声明式』是相对于『编程式』而言的。

  • 声明式:告诉框架做什么,具体操作由框架完成
  • 编程式:自己编写代码完成具体操作

    ②渲染

    image.png
    上图含义解释:

  • 蓝色方框:HTML标签

  • 红色圆形:动态、尚未确定的数据
  • 蓝色圆形:经过程序运算以后,计算得到的具体的,可以直接在页面上显示的数据、
  • 渲染:程序计算动态数据得到具体数据的过程

    2、demo

    ①HTML代码

    1. <!-- 使用{{}}格式,指定要被渲染的数据 -->
    2. <div id="app">{{message}}</div>

    ②vue代码

    ```javascript // 1.创建一个JSON对象,作为new Vue时要使用的参数 var argumentJson = {

    // el用于指定Vue对象要关联的HTML元素。el就是element的缩写 // 通过id属性值指定HTML元素时,语法格式是:#id “el”:”#app”,

    // data属性设置了Vue对象中保存的数据 “data”:{ “message”:”Hello Vue!” } };

// 2.创建Vue对象,传入上面准备好的参数 var app = new Vue(argumentJson);

  1. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/26803611/1652586480985-1825f0fe-e089-445f-b949-4e959056839b.png#clientId=u137d0ba7-1d50-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u6c26c7a7&margin=%5Bobject%20Object%5D&name=image.png&originHeight=575&originWidth=941&originalType=url&ratio=1&rotation=0&showTitle=false&size=37708&status=done&style=none&taskId=ubea0ea8e-cb8f-43c9-a204-fa8bfce40fe&title=)
  2. <a name="ue8PX"></a>
  3. ### 3、查看声明式渲染的响应式效果
  4. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/26803611/1652586481015-57fa2781-90e8-4f6c-a296-d1e8c0d43946.png#clientId=u137d0ba7-1d50-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u624411ed&margin=%5Bobject%20Object%5D&name=image.png&originHeight=491&originWidth=542&originalType=url&ratio=1&rotation=0&showTitle=false&size=40930&status=done&style=none&taskId=u5a97afd0-63ed-47c4-81c7-28d81cb16ad&title=)<br />通过验证Vue对象的『响应式』效果,我们看到Vue对象和页面上的HTML标签确实是始终保持着关联的关系,同时看到Vue在背后确实是做了大量的工作。
  5. <a name="fzvKO"></a>
  6. ## 第四节 Vue.js基本语法:绑定元素属性
  7. <a name="B7QAc"></a>
  8. ### 1、基本语法
  9. v-bind:HTML标签的原始属性名
  10. <a name="bipkX"></a>
  11. ### 2、demo
  12. <a name="hbGyy"></a>
  13. #### ①HTML代码
  14. ```html
  15. <div id="app">
  16. <!-- v-bind:value表示将value属性交给Vue来进行管理,也就是绑定到Vue对象 -->
  17. <!-- vueValue是一个用来渲染属性值的表达式,相当于标签体中加{{}}的表达式 -->
  18. <input type="text" v-bind:value="vueValue" />
  19. <!-- 同样的表达式,在标签体内通过{{}}告诉Vue这里需要渲染; -->
  20. <!-- 在HTML标签的属性中,通过v-bind:属性名="表达式"的方式告诉Vue这里要渲染 -->
  21. <p>{{vueValue}}</p>
  22. </div>

②Vue代码

  1. // 创建Vue对象,挂载#app这个div标签
  2. var app = new Vue({
  3. "el":"#app",
  4. "data":{
  5. "vueValue":"太阳当空照"
  6. }
  7. });

3、小结

本质上,v-bind:属性名=”表达式”它们都是用Vue对象来渲染页面。只不过:

  • 文本标签体:使用形式
  • 属性:使用v-bind:属性名=”表达式”形式

    第五节 Vue.js基本语法:双向数据绑定

    1、提出问题

    image.png
    而使用了双向绑定后,就可以实现:页面上数据被修改后,Vue对象中的数据属性也跟着被修改。

    2、demo

    ①HTML代码

    1. <div id="app">
    2. <!-- v-bind:属性名 效果是从Vue对象渲染到页面 -->
    3. <!-- v-model:属性名 效果不仅是从Vue对象渲染到页面,而且能够在页面上数据修改后反向修改Vue对象中的数据属性 -->
    4. <input type="text" v-model:value="vueValue" />
    5. <p>{{vueValue}}</p>
    6. </div>

    ②Vue代码

    1. // 创建Vue对象,挂载#app这个div标签
    2. var app = new Vue({
    3. "el":"#app",
    4. "data":{
    5. "vueValue":"太阳当空照"
    6. }
    7. });

    ③页面效果

    p标签内的数据能够和文本框中的数据实现同步修改:
    image.png

    3、去除前后空格

    ①:value可以省略

    1. <input type="text" v-model="vueValue" />

    ②.trim修饰符

    实际开发中,要考虑到用户在输入数据时,有可能会包含前后空格。而这些前后的空格对我们程序运行来说都是干扰因素,要去掉。在v-model后面加上.trim修饰符即可实现。

    1. <input type="text" v-model.trim="vueValue" />

    Vue会帮助我们在文本框失去焦点时自动去除前后空格。

    第六节 Vue.js基本语法:条件渲染

    根据Vue对象中,数据属性的值来判断是否对HTML页面内容进行渲染。

    1、v-if

    ①HTML代码

    1. <div id="app">
    2. <h3>if</h3>
    3. <img v-if="flag" src="/pro03-vue/./images/one.jpg" />
    4. <img v-if="!flag" src="/pro03-vue/./images/two.jpg" />
    5. </div>

    ②Vue代码

    1. var app = new Vue({
    2. "el":"#app",
    3. "data":{
    4. "flag":true
    5. }
    6. });

    2、v-if和v-else

    ①HTML代码

    1. <div id="app02">
    2. <h3>if/else</h3>
    3. <img v-if="flag" src="/pro03-vue/./images/one.jpg" />
    4. <img v-else="flag" src="/pro03-vue/./images/two.jpg" />
    5. </div>

    ②Vue代码

    1. var app02 = new Vue({
    2. "el":"#app02",
    3. "data":{
    4. "flag":true
    5. }
    6. });

    3、v-show

    ①HTML代码

    1. <div id="app03">
    2. <h3>v-show</h3>
    3. <img v-show="flag" src="/pro03-vue/./images/mi.jpg" />
    4. </div>

    ②Vue代码

    1. var app03 = new Vue({
    2. "el":"#app03",
    3. "data":{
    4. "flag":true
    5. }
    6. });

    第七节 Vue.js基本语法:列表渲染

    1、迭代一个简单的数组

    ①HTML代码

    1. <div id="app01">
    2. <ul>
    3. <!-- 使用v-for语法遍历数组 -->
    4. <!-- v-for的值是语法格式是:引用数组元素的变量名 in Vue对象中的数组属性名 -->
    5. <!-- 在文本标签体中使用{{引用数组元素的变量名}}渲染每一个数组元素 -->
    6. <li v-for="fruit in fruitList">{{fruit}}</li>
    7. </ul>
    8. </div>

    ②Vue代码

    1. var app01 = new Vue({
    2. "el":"#app01",
    3. "data":{
    4. "fruitList": [
    5. "apple",
    6. "banana",
    7. "orange",
    8. "grape",
    9. "dragonfruit"
    10. ]
    11. }
    12. });

    2、迭代一个对象数组

    ①HTML代码

    1. <div id="app">
    2. <table>
    3. <tr>
    4. <th>编号</th>
    5. <th>姓名</th>
    6. <th>年龄</th>
    7. <th>专业</th>
    8. </tr>
    9. <tr v-for="employee in employeeList">
    10. <td>{{employee.empId}}</td>
    11. <td>{{employee.empName}}</td>
    12. <td>{{employee.empAge}}</td>
    13. <td>{{employee.empSubject}}</td>
    14. </tr>
    15. </table>
    16. </div>

    ②Vue代码

    1. var app = new Vue({
    2. "el":"#app",
    3. "data":{
    4. "employeeList":[
    5. {
    6. "empId":11,
    7. "empName":"tom",
    8. "empAge":111,
    9. "empSubject":"java"
    10. },
    11. {
    12. "empId":22,
    13. "empName":"jerry",
    14. "empAge":222,
    15. "empSubject":"php"
    16. },
    17. {
    18. "empId":33,
    19. "empName":"bob",
    20. "empAge":333,
    21. "empSubject":"python"
    22. }
    23. ]
    24. }
    25. });

    第八节 Vue.js基本语法:事件驱动

    1、demo:字符串顺序反转

    ①HTML代码

    1. <div id="app">
    2. <p>{{message}}</p>
    3. <!-- v-on:事件类型="事件响应函数的函数名" -->
    4. <button v-on:click="reverseMessage">Click me,reverse message</button>
    5. </div>

    ②Vue代码

    1. var app = new Vue({
    2. "el":"#app",
    3. "data":{
    4. "message":"Hello Vue!"
    5. },
    6. "methods":{
    7. "reverseMessage":function(){
    8. this.message = this.message.split("").reverse().join("");
    9. }
    10. }
    11. });

    2、demo:获取鼠标移动时的坐标信息

    ①HTML代码

    1. <div id="app">
    2. <div id="area" v-on:mousemove="recordPosition"></div>
    3. <p id="showPosition">{{position}}</p>
    4. </div>

    ②Vue代码

    1. var app = new Vue({
    2. "el":"#app",
    3. "data":{
    4. "position":"暂时没有获取到鼠标的位置信息"
    5. },
    6. "methods":{
    7. "recordPosition":function(event){
    8. this.position = event.clientX + " " + event.clientY;
    9. }
    10. }
    11. });

    第九节 Vue.js基本语法:侦听属性

    1、提出需求

    1. <div id="app">
    2. <p>尊姓:{{firstName}}</p>
    3. <p>大名:{{lastName}}</p>
    4. 尊姓:<input type="text" v-model="firstName" /><br/>
    5. 大名:<input type="text" v-model="lastName" /><br/>
    6. <p>全名:{{fullName}}</p>
    7. </div>

    在上面代码的基础上,我们希望firstName或lastName属性发生变化时,修改fullName属性。此时需要对firstName或lastName属性进行『侦听』
    具体来说,所谓『侦听』就是对message属性进行监控,当firstName或lastName属性的值发生变化时,调用我们准备好的函数。

    2、Vue代码

    在watch属性中声明对firstName和lastName属性进行『侦听』的函数:

    1. var app = new Vue({
    2. "el":"#app",
    3. "data":{
    4. "firstName":"jim",
    5. "lastName":"green",
    6. "fullName":"jim green"
    7. },
    8. "watch":{
    9. "firstName":function(inputValue){
    10. this.fullName = inputValue + " " + this.lastName;
    11. },
    12. "lastName":function(inputValue){
    13. this.fullName = this.firstName + " " + inputValue;
    14. }
    15. }
    16. });

    第十节 Vue.js基本语法:简化写法

    1、v-bind的简化写法

    正常写法:

    1. <input type="text" v-bind:value="message" />

    简化以后:

    1. <input type="text" :value="message" />

    2、v-on的简化写法

    正常写法:

    1. <button v-on:click="sayHello">SayHello</button>

    简化以后:

    1. <button @click="sayHello">SayHello</button>

    第十一节 练习

    1、功能效果演示

    image.png

    2、任务拆解

  • 第一步:显示表格

  • 第二步:显示四个文本框
  • 第三步:创建一个p标签用来显示用户在文本框中实时输入的内容
  • 第四步:点击添加记录按钮实现记录的添加

    3、第一步:显示表格

    ①HTML标签

    1. <table id="appTable">
    2. <tr>
    3. <th>编号</th>
    4. <th>姓名</th>
    5. <th>年龄</th>
    6. <th>专业</th>
    7. </tr>
    8. <tr v-for="employee in employeeList">
    9. <td>{{employee.empId}}</td>
    10. <td>{{employee.empName}}</td>
    11. <td>{{employee.empAge}}</td>
    12. <td>{{employee.empSubject}}</td>
    13. </tr>
    14. </table>

    ②Vue代码

    1. var appTable = new Vue({
    2. "el": "#appTable",
    3. "data": {
    4. "employeeList": [{
    5. "empId": 1,
    6. "empName": "tom",
    7. "empAge": 11,
    8. "empSubject": "java"
    9. },
    10. {
    11. "empId": 2,
    12. "empName": "jerry",
    13. "empAge": 22,
    14. "empSubject": "php"
    15. },
    16. {
    17. "empId": 3,
    18. "empName": "peter",
    19. "empAge": 33,
    20. "empSubject": "python"
    21. }
    22. ]
    23. }
    24. });

    4、第二步:显示四个文本框

    ①HTML标签

    ```html

    编号:
    姓名:
    年龄:
    专业:

  1. <a name="vbGEl"></a>
  2. #### ②Vue代码
  3. ```javascript
  4. var appDiv = new Vue({
  5. "el":"#appDiv",
  6. "data":{
  7. // 初始值设置空字符串即可
  8. "empId":"",
  9. "empName":"",
  10. "empAge":"",
  11. "empSubject":""
  12. }
  13. });

测试是否正确的方式是:在控制台尝试修改Vue对象的数据属性值:
image.png

5、第三步:创建一个p标签

HTML标签:

  1. <!-- 四个文本框、显示收集到的文本框数据的p标签、按钮这三个部分需要共享数据,所以要放在同一个app中 -->
  2. <div id="appDiv">
  3. <!-- 第一部分:四个文本框 -->
  4. 编号:<input type="text" v-model="empId" placeholder="请输入编号" /><br/>
  5. 姓名:<input type="text" v-model="empName" placeholder="请输入姓名" /><br/>
  6. 年龄:<input type="text" v-model="empAge" placeholder="请输入年龄" /><br/>
  7. 专业:<input type="text" v-model="empSubject" placeholder="请输入专业" /><br/>
  8. <!-- 第二部分:显示收集到的文本框数据的p标签 -->
  9. <p>{{empId}} {{empName}} {{empAge}} {{empSubject}}</p>
  10. <!-- 第三部分:按钮 -->
  11. </div>

6、第四步:点击添加记录按钮

①第一小步:给按钮设置事件驱动

[1]HTML标签
  1. <!-- 四个文本框、显示收集到的文本框数据的p标签、按钮这三个部分需要共享数据,所以要放在同一个app中 -->
  2. <div id="appDiv">
  3. <!-- 第一部分:四个文本框 -->
  4. 编号:<input type="text" v-model="empId" placeholder="请输入编号" /><br/>
  5. 姓名:<input type="text" v-model="empName" placeholder="请输入姓名" /><br/>
  6. 年龄:<input type="text" v-model="empAge" placeholder="请输入年龄" /><br/>
  7. 专业:<input type="text" v-model="empSubject" placeholder="请输入专业" /><br/>
  8. <!-- 第二部分:显示收集到的文本框数据的p标签 -->
  9. <p>{{empId}} {{empName}} {{empAge}} {{empSubject}}</p>
  10. <!-- 第三部分:按钮 -->
  11. <button type="button" v-on:click="addRecord">添加记录</button>
  12. </div>

[2]Vue代码
  1. var appDiv = new Vue({
  2. "el":"#appDiv",
  3. "data":{
  4. // 初始值设置空字符串即可
  5. "empId":"",
  6. "empName":"",
  7. "empAge":"",
  8. "empSubject":""
  9. },
  10. "methods":{
  11. "addRecord":function(){
  12. console.log("我点击了那个按钮……");
  13. }
  14. }
  15. });

image.png

②第二小步:打印一下文本框输入的数据

  1. var appDiv = new Vue({
  2. "el":"#appDiv",
  3. "data":{
  4. // 初始值设置空字符串即可
  5. "empId":"",
  6. "empName":"",
  7. "empAge":"",
  8. "empSubject":""
  9. },
  10. "methods":{
  11. "addRecord":function(){
  12. console.log("我点击了那个按钮……");
  13. console.log(this.empId);
  14. console.log(this.empName);
  15. console.log(this.empAge);
  16. console.log(this.empSubject);
  17. }
  18. }
  19. });

image.png

③第三小步:将收集到的数据添加到表格中

  1. "addRecord":function(){
  2. // 确认单击事件是否绑定成功
  3. console.log("我点击了那个按钮……");
  4. // 确认是否能够正确收集到文本框数据
  5. console.log(this.empId);
  6. console.log(this.empName);
  7. console.log(this.empAge);
  8. console.log(this.empSubject);
  9. // 将收集到的文本框数据封装为一个对象
  10. var employee = {
  11. "empId":this.empId,
  12. "empName":this.empName,
  13. "empAge":this.empAge,
  14. "empSubject":this.empSubject
  15. };
  16. // 将上面的对象压入表格数据的employeeList数组
  17. appTable.employeeList.push(employee);
  18. }

image.png

第十二节 Vue对象生命周期

1、概念

在我们各种语言的编程领域中,『生命周期』都是一个非常常见的概念。一个对象从创建、初始化、工作再到释放、清理和销毁,会经历很多环节的演变。比如我们在JavaSE阶段学习过线程的生命周期,今天学习Vue对象的生命周期,将来还要学习Servlet、Filter等Web组件的生命周期。

2、Vue对象的生命周期

image.png

3、生命周期钩子函数

Vue允许我们在特定的生命周期环节中通过钩子函数来加入我们的代码。

  1. <div id="app">
  2. <p id="content">{{message}}</p>
  3. <button @click="changeValue">点我</button>
  4. </div>
  1. new Vue({
  2. "el":"#app",
  3. "data":{
  4. "message":"hello"
  5. },
  6. "methods":{
  7. "changeValue":function(){
  8. this.message = "new hello";
  9. }
  10. },
  11. // 1.实例创建之前
  12. "beforeCreate":function(){
  13. console.log("beforeCreate:"+this.message);
  14. },
  15. // 2.实例创建完成
  16. "created":function(){
  17. console.log("created:"+this.message);
  18. },
  19. // 3.数据挂载前
  20. "beforeMount":function(){
  21. console.log("beforeMount:"+document.getElementById("content").innerText);
  22. },
  23. // 4.数据已经挂载
  24. "mounted":function(){
  25. console.log("mounted:"+document.getElementById("content").innerText);
  26. },
  27. // 5.数据更新前
  28. "beforeUpdate":function(){
  29. console.log("beforeUpdate:"+document.getElementById("content").innerText);
  30. },
  31. // 6.数据更新之后
  32. "updated":function(){
  33. console.log("updated:"+document.getElementById("content").innerText);
  34. }
  35. });

第五章 Tomcat

第一节 配置文件

1、配置

①设置

所谓设置其实就是通过修改一个一个的参数,告诉应用软件它该怎么工作
image.png

②配置

本质上配置和设置是一样的,只是对象和形式不同:

配置 设置
对象 开发中使用的应用程序或框架 应用软件
形式 特定格式的配置文件 应用软件的友好界面

2、配置文件

①XML

先来个例子:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
  5. version="4.0">
  6. <!-- 配置SpringMVC前端控制器 -->
  7. <servlet>
  8. <servlet-name>dispatcherServlet</servlet-name>
  9. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  10. <!-- 在初始化参数中指定SpringMVC配置文件位置 -->
  11. <init-param>
  12. <param-name>contextConfigLocation</param-name>
  13. <param-value>classpath:spring-mvc.xml</param-value>
  14. </init-param>
  15. <!-- 设置当前Servlet创建对象的时机是在Web应用启动时 -->
  16. <load-on-startup>1</load-on-startup>
  17. </servlet>
  18. <servlet-mapping>
  19. <servlet-name>dispatcherServlet</servlet-name>
  20. <!-- url-pattern配置斜杠表示匹配所有请求 -->
  21. <!-- 两种可选的配置方式:
  22. 1、斜杠开头:/
  23. 2、包含星号:*.atguigu
  24. 不允许的配置方式:前面有斜杠,中间有星号
  25. /*.app
  26. -->
  27. <url-pattern>/</url-pattern>
  28. </servlet-mapping>
  29. </web-app>

[1]名词解释

XML是eXtensible Markup Language的缩写,翻译过来就是可扩展标记语言。所以很明显,XML和HTML一样都是标记语言,也就是说它们的基本语法都是标签。

[2]可扩展

可扩展三个字表面上的意思是XML允许自定义格式。但是别美,这不代表可以随便写
image.png
在XML基本语法规范的基础上,你使用的那些第三方应用程序、框架会通过设计『XML约束』的方式『强制规定』配置文件中可以写什么和怎么写,规定之外的都不可以写。
XML基本语法这个知识点的定位是:我们不需要从零开始,从头到尾的一行一行编写XML文档,而是在第三方应用程序、框架已提供的配置文件的基础上修改。要改成什么样取决于你的需求,而怎么改取决于XML基本语法具体的XML约束

[3]XML基本语法
  • XML声明

这部分基本上就是固定格式,大家知道就好

  1. <?xml version="1.0" encoding="UTF-8"?>
  • 根标签

根标签有且只能有一个。

  • 标签关闭
    • 双标签:开始标签和结束标签必须成对出现。
    • 单标签:单标签在标签内关闭。
  • 标签嵌套
    • 可以嵌套,但是不能交叉嵌套。
  • 注释不能嵌套
  • 标签名、属性名建议使用小写字母
  • 属性
    • 属性必须有值
    • 属性值必须加引号,单双都行

看到这里大家一定会发现XML的基本语法和HTML的基本语法简直如出一辙。其实这不是偶然的,XML基本语法+HTML约束=HTML语法。在逻辑上HTML确实是XML的子集。

  1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
  2. "http://www.w3.org/TR/html4/loose.dtd">

从HTML4.01版本的文档类型声明中可以看出,这里使用的DTD类型的XML约束。也就是说http://www.w3.org/TR/html4/loose.dtd这个文件定义了HTML文档中可以写哪些标签,标签内可以写哪些属性,某个标签可以有什么样的子标签。

[4]XML约束

将来我们主要就是根据XML约束中的规定来编写XML配置文件。而XML约束主要包括DTD和Schema两种。如果XML配置文件使用的是DTD,那么对我们几乎没有影响。如果是Schema约束,需要我们稍微参与一点点。

  • DTD

将来在IDEA中有代码提示的协助,在DTD文档的约束下进行配置非常简单。

  • Schema

我们将来使用SSM框架中的Spring、SpringMVC框架时,会涉及到一点点对Schema约束的设置。不过不必紧张,有IDEA的支持操作会非常简单,我们现在只需要理解基本概念即可。
首先我们要理解一个概念:『名称空间』,英文:name space
image.png
Schema约束要求我们一个XML文档中,所有标签,所有属性都必须在约束中有明确的定义。
下面我们以web.xml的约束声明为例来做个说明:

  1. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
  4. version="4.0">
属性名 作用
xmlns 指出当前XML文档约束规则的名称空间在哪里
我们就是通过这个属性来引用一个具体的名称空间
xmlns:xsi 指出xmlns这个属性是在哪个约束文档中被定义的
xsi:schemaLocation 语法格式:在xsi名称空间下引用schemaLocation属性
配置含义:指定当前XML文档中所用到的约束文档本身的文件的地址

xmlns和xsi:schemaLocation对应关系如下图:
image.png
引入多个名称空间的例子如下:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:mvc="http://www.springframework.org/schema/mvc"
  6. xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
  7. http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  8. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
  9. <context:component-scan base-package="com.atguigu.crud.component"/>
  10. <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  11. <property name="prefix" value="/WEB-INF/pages/"/>
  12. <property name="suffix" value=".jsp"/>
  13. </bean>
  14. <mvc:default-servlet-handler/>
  15. <mvc:annotation-driven/>
  16. </beans>

image.png
看到这么复杂,这么长的名称空间字符串,我们会觉得很担心,根本记不住。但是其实不需要记,在IDEA中编写配置文件时,IDEA会协助我们导入,会有提示。我们理解各个部分的含义能够调整即可。

②属性文件

以properties作为扩展名的文件。

  1. atguigu.jdbc.url=jdbc:mysql://192.168.198.100:3306/mybatis1026
  2. atguigu.jdbc.driver=com.mysql.jdbc.Driver
  3. atguigu.jdbc.username=root
  4. atguigu.jdbc.password=atguigu
  • 由键值对组成
  • 键和值之间的符号是等号
  • 每一行都必须顶格写,前面不能有空格之类的其他符号

也有人叫资源文件。

③其他形式

YAML语言的配置文件:在SpringBoot中使用。

  1. spring:
  2. profiles:
  3. active: fc
  4. datasource:
  5. name: mydb
  6. type: com.alibaba.druid.pool.DruidDataSource
  7. url: jdbc:mysql://192.168.41.100:3306/spring_boot?serverTimezone=UTC
  8. username: root
  9. password: atguigu
  10. driver-class-name: com.mysql.cj.jdbc.Driver
  11. mybatis:
  12. mapper-locations: classpath*:/mybatis-mapper/*Mapper.xml
  13. logging:
  14. level:
  15. com.atguigu.function.compute.mapper: debug

JSON格式的配置文件:一般是前端使用。

第二节 Tomcat的部署和启动

1、Tomcat扮演的角色

①对外:Web服务器

image.png

②对内:Servlet容器

image.png

2、部署

①前提

Tomcat本身是一个Java程序,所以当前系统中必须正确配置了JAVA_HOME环境变量。我们可以通过下面的命令检测:

C:\Users\Administrator>java -version java version “1.8.0_141” Java(TM) SE Runtime Environment (build 1.8.0_141-b15) Java HotSpot(TM) 64-Bit Server VM (build 25.141-b15, mixed mode) C:\Users\Administrator>echo %JAVA_HOME% D:\software\Java C:\Users\Administrator>echo %PATH% D:\software\xftp;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0;D:\software\Java\bin;D:\software\apache-maven-3.5.4\bin;C:\Users\Administrator\AppData\Local\Microsoft\WindowsApps;

②解压

将Tomcat压缩包解压到一个非中文无空格的目录下。
image.png
image.png

③Tomcat目录结构

image.png

④启动Tomcat并访问首页

启动Tomcat:运行Tomcat解压后根目录下\bin\startup.bat即可,例如:
D:\software\apache-tomcat-7.0.79\bin\startup.bat
image.png
如果需要停止Tomcat,则运行shutdown.bat程序,例如:
D:\software\apache-tomcat-7.0.79\bin\shutdown.bat
小提示:将来我们在IDEA中启动Tomcat,如果IDEA卡死强关,Tomcat不会正常退出。下次再启动Tomcat会因为残留进程仍然占用8080端口,导致新的进程无法启动。此时可以使用shutdown.bat结束残留进程。

⑤部署一个war包并启动Tomcat

image.png
在Tomcat启动过程中,会打印启动日志,其中我们能看到hello.war被部署的日志信息:
信息: Deployment of web application archive D:\software\apache-tomcat-7.0.79\webapps\hello.war has finished in 1,150 ms
此时,我们回到webapps目录,能看到hello.war被解压了:
image.png

⑥访问刚才部署的Web应用

image.png

⑦关于Tomcat端口号

image.png
配置文件节选:

  1. ……
  2. <!-- 22 -->
  3. <Server port="8005" shutdown="SHUTDOWN">
  4. ……
  5. <!-- 71 -->
  6. <Connector port="8080" protocol="HTTP/1.1"
  7. connectionTimeout="20000"
  8. redirectPort="8443" />
  9. ……
  10. <!-- 93 -->
  11. <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

三个位置共定义了3个端口号,Tomcat启动后,这个三个端口号都会被占用。

第三节 在IDEA中关联Tomcat

image.png
image.png
image.png
image.png
image.png
image.png
image.png

第四节 创建动态Web工程并部署运行

1、第一步

创建empty project

2、第二步

以下操作基于IDEA2018.3
image.png
创建动态Web工程:
image.png
image.png
创建好的工程如下所示:
image.png
注意:src目录应该是蓝色的,web目录的图标上有个蓝色的圆点。

3、动态Web工程目录结构介绍

目录或文件名 功能
src目录 存放Java源文件
web目录 存放Web开发相关资源
web/WEB-INF目录 存放web.xml文件、classes目录、lib目录
web/WEB-INF/web.xml文件 别名:部署描述符deployment descriptor
作用:Web工程的核心配置文件
web/WEB-INF/classes目录 存放编译得到的*.class字节码文件
web/WEB-INF/lib目录 存放第三方jar包

4、创建用来部署Web工程的Tomcat实例

image.png
image.png
image.png
image.png
通常我们会觉得现在这个Application context太长了,改简短一些会更方便。
image.png
image.png
创建好的效果如下:
image.png

5、编写简单的测试代码

①Java代码

image.png

  1. public class Hello {
  2. public String getMessage() {
  3. return "年少不知软饭香";
  4. }
  5. }

②JSP代码

image.png

  1. <%=new Hello().getMessage() %>

③启动专门为这个工程创建的Tomcat实例

image.png

④错误提示

image.png
提示信息的含义:未指定编译结果的保存目录。
错误原因:

  • 父工程只是一个empty project
  • 当前模块继承父工程的编译输出路径

为了解决这个问题我们可以在父工程中设置输出路径:
image.png
image.png
image.png
image.png

6、IDEA运行程序时界面布局

image.png

第五节 在IDEA中重新部署运行

1、为什么需要重新部署?

对于已经运行过的Web项目,如果我们增加了目录和文件,那么部署目录有可能不会自动同步过来,从而造成实际运行的效果和我们期望的不同。
如下图中,我们在工程目录已经新增了./images目录和两个图片:
image.png
但是在部署目录并没有出现:
image.png
哪怕我们执行了重新部署也没有起作用。

2、如何重新部署?

①清理部署目录

image.png
image.png

②构建

image.png

③效果

image.png

第六节 导入别人的web module

如果你想把老师发给你的module导入自己的project中运行起来,可以参考下面的操作:
做下面操作前,需要把要导入的module复制到project目录下。
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png

第六章 HTTP协议

1、介绍

HTTP:Hyper Text Transfer Protocol超文本传输协议。HTTP最大的作用就是确定了请求和响应数据的格式。浏览器发送给服务器的数据:请求报文;服务器返回给浏览器的数据:响应报文。

2、请求报文

①在开发者工具中浏览报文源码

image.png

②请求报文的三个部分

image.png

③请求行

作用:展示当前请求的最基本信息
POST /dynamic/target.jsp HTTP/1.1

  • 请求方式
  • 访问地址
  • HTTP协议的版本

    ④请求消息头

    作用:通过具体的参数对本次请求进行详细的说明
    格式:键值对,键和值之间使用冒号隔开
    相对比较重要的请求消息头:
名称 功能
Host 服务器的主机地址
Accept 声明当前请求能够接受的『媒体类型』
Referer 当前请求来源页面的地址
Content-Length 请求体内容的长度
Content-Type 请求体的内容类型,这一项的具体值是媒体类型中的某一种
Cookie 浏览器访问服务器时携带的Cookie数据

⑤请求体

作用:作为请求的主体,发送数据给服务器。具体来说其实就是POST请求方式下的请求参数。
格式:

[1]form data

含义:当前请求体是一个表单提交的请求参数。
image.png
查看源码后,发现格式如下:
username=tom&password=123456

  • 每一组请求参数是一个键值对
  • 键和值中间是等号
  • 键值对之间是&号

    [2]Request Payload

    含义:整个请求体以某种特定格式来组织数据,例如JSON格式。
    image.png

    3、请求方式

    ①HTTP协议已定义的请求方式

    HTTP1.1中共定义了八种请求方式:

  • GET:从服务器端获取数据

  • POST:将数据保存到服务器端
  • PUT:命令服务器对数据执行更新
  • DELETE:命令服务器删除数据
  • HEAD
  • CONNECT
  • OPTIONS
  • TRACE

    ②GET请求

  • 特征1:没有请求体

  • 特征2:请求参数附着在URL地址后面
  • 特征3:请求参数在浏览器地址栏能够直接被看到,存在安全隐患
  • 特征4:在URL地址后面携带请求参数,数据容量非常有限。如果数据量大,那么超出容量的数据会丢失
  • 特征5:从报文角度分析,请求参数是在请求行中携带的,因为访问地址在请求行

    ③POST请求

  • 特征1:有请求体

  • 特征2:请求参数放在请求体中
  • 特征3:请求体发送数据的空间没有限制
  • 特征4:可以发送各种不同类型的数据
  • 特征5:从报文角度分析,请求参数是在请求体中携带的
  • 特征6:由于请求参数是放在请求体中,所以浏览器地址栏看不到

    4、媒体类型

    ①HTTP协议中的MIME类型

    Multipurpose Internet Mail Extensions

    ②用途

    为了让用户通过浏览器和服务器端交互的过程中有更好、更丰富的体验,HTTP协议需要支持丰富的数据类型。

    ③MIME类型定义参考

    我们可以通过查看Tomcat解压目录下conf/web.xml配置文件,了解HTTP协议中定义的MIME类型。
    1. <mime-mapping>
    2. <extension>mp4</extension>
    3. <mime-type>video/mp4</mime-type>
    4. </mime-mapping>
    5. <mime-mapping>
    6. <extension>doc</extension>
    7. <mime-type>application/msword</mime-type>
    8. </mime-mapping>
    9. <mime-mapping>
    10. <extension>json</extension>
    11. <mime-type>application/json</mime-type>
    12. </mime-mapping>
    13. <mime-mapping>
    14. <extension>html</extension>
    15. <mime-type>text/html</mime-type>
    16. </mime-mapping>
    从上面的例子中可以看出:MIME的基本格式是

    大类/具体类型

MIME类型在HTTP报文中对应的是内容类型:Content-type

5、响应报文

image.png

①响应状态行

HTTP/1.1 200 OK

  • HTTP协议版本
  • 响应状态码
  • 响应状态的说明文字

    ②响应消息头

  • 响应体的说明书。

  • 服务器端对浏览器端设置数据,例如:服务器端返回Cookie信息。 | 名称 | 功能 | | —- | —- | | Content-Type | 响应体的内容类型 | | Content-Length | 响应体的内容长度 | | Set-Cookie | 服务器返回新的Cookie信息给浏览器 | | location | 在重定向的情况下,告诉浏览器访问下一个资源的地址 |

③响应体

服务器返回的数据主体,有可能是各种数据类型。

  • HTML页面
  • 图片
  • 视频
  • 以下载形式返回的文件
  • CSS文件
  • JavaScript文件

    ④响应状态码

    作用:以编码的形式告诉浏览器当前请求处理的结果
状态码 含义
200 服务器成功处理了当前请求,成功返回响应
302 重定向
400 [SpringMVC特定环境]请求参数问题
403 没有权限
404 找不到目标资源
405 请求方式和服务器端对应的处理方式不一致
406 [SpringMVC特定环境]请求扩展名和实际返回的响应体类型不一致
50X 服务器端内部错误,通常都是服务器端抛异常了

404产生的具体原因:

  • 访问地址写错了,确实是没有这个资源
  • 访问了WEB-INF目录下的资源
  • Web应用启动的时候,控制台已经抛出异常,导致整个Web应用不可用,访问任何资源都是404
  • 服务器端缓存

    第七章 Servlet

    第一节 Servlet概述

    1、Servlet名字

    Servlet=Server+applet
    Server:服务器
    applet:小程序
    Servlet含义是服务器端的小程序

    2、Servlet在整个Web应用中起到的作用

    ①生活中的例子

    image.png

    ②对应Web应用

    image.png

    ③具体细节

    image.png

    ④Servlet扮演角色

    image.png
    在整个Web应用中,Servlet主要负责处理请求、协调调度功能。我们可以把Servlet称为Web应用中的『控制器』

    第二节 Servlet HelloWorld

    1、HelloWorld分析

    ①目标

    在页面上点击超链接,由Servlet处理这个请求,并返回一个响应字符串:Hello,I am Servlet

    ②思路

    image.png

    2、具体操作

    ①第一步:创建动态Web module

    image.png

    ②第二步:创建超链接

    1. <!-- /Web应用地址/Servlet地址 -->
    2. <a href="/app/helloServlet">Servlet Hello World</a>

    ③第三步:创建HelloServlet的Java类

    1. public class HelloServlet implements Servlet {
    2. @Override
    3. public void init(ServletConfig servletConfig) throws ServletException {
    4. }
    5. @Override
    6. public ServletConfig getServletConfig() {
    7. return null;
    8. }
    9. @Override
    10. public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
    11. // 控制台打印,证明这个方法被调用了
    12. System.out.println("我是HelloServlet,我执行了!");
    13. // 返回响应字符串
    14. // 1、获取能够返回响应数据的字符流对象
    15. PrintWriter writer = servletResponse.getWriter();
    16. // 2、向字符流对象写入数据
    17. writer.write("Hello,I am Servlet");
    18. }
    19. @Override
    20. public String getServletInfo() {
    21. return null;
    22. }
    23. @Override
    24. public void destroy() {
    25. }
    26. }

    ④第四步:配置HelloServlet

    配置文件位置:WEB-INF/web.xml
    image.png ```xml

    HelloServlet com.atguigu.servlet.HelloServlet
HelloServlet /helloServlet **『虚拟路径』**:Servlet并**不是**文件系统中**实际存在**的**目录或文件**,所以为了方便浏览器访问,我们创建了**虚拟**出来的路径来访问它。 <a name="vD2Ea"></a> #### ⑤小结 - 需求:在浏览器上点超链接能够访问Java程序 ![image.png](https://cdn.nlark.com/yuque/0/2022/png/26803611/1652612166751-a348efff-65cc-437b-a240-a85f7a9bca74.png#clientId=u80833343-5aeb-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=uc5428929&margin=%5Bobject%20Object%5D&name=image.png&originHeight=524&originWidth=1301&originalType=url&ratio=1&rotation=0&showTitle=false&size=58234&status=done&style=none&taskId=u5da51d60-06f9-4de9-9873-b05732b6c20&title=) <a name="pFPGw"></a> ### 3、梳理概念 <a name="B2jPn"></a> #### ①原生Tomcat ![image.png](https://cdn.nlark.com/yuque/0/2022/png/26803611/1652612166737-fb6b3d35-a892-4840-8cd0-698dd5f3a528.png#clientId=u80833343-5aeb-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u18637157&margin=%5Bobject%20Object%5D&name=image.png&originHeight=307&originWidth=632&originalType=url&ratio=1&rotation=0&showTitle=false&size=29209&status=done&style=none&taskId=u0a5895e2-765d-4d1b-b1b3-017cc4b4027&title=) <a name="yVA7F"></a> #### ②IDEA中的Tomcat实例 ![image.png](https://cdn.nlark.com/yuque/0/2022/png/26803611/1652612166876-8b59253f-187b-41e0-9d71-a605f63d53ea.png#clientId=u80833343-5aeb-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u774f6bf6&margin=%5Bobject%20Object%5D&name=image.png&originHeight=88&originWidth=136&originalType=url&ratio=1&rotation=0&showTitle=false&size=2950&status=done&style=none&taskId=u56664cdf-3f31-4f36-91ae-a177fdacc9c&title=) <a name="HUiyv"></a> #### ③IDEA中的Web工程 ![image.png](https://cdn.nlark.com/yuque/0/2022/png/26803611/1652612166964-aee41763-9097-45dc-8863-3286c6e565ae.png#clientId=u80833343-5aeb-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u3cedb034&margin=%5Bobject%20Object%5D&name=image.png&originHeight=172&originWidth=185&originalType=url&ratio=1&rotation=0&showTitle=false&size=3945&status=done&style=none&taskId=u25634cea-aaeb-4428-ad18-ef41358e563&title=) <a name="diMx5"></a> #### ④根据Web工程生成的war包 ![image.png](https://cdn.nlark.com/yuque/0/2022/png/26803611/1652612167179-e4a20678-7a64-4057-8c5d-3c2e8dbec02e.png#clientId=u80833343-5aeb-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=ua9bdf287&margin=%5Bobject%20Object%5D&name=image.png&originHeight=167&originWidth=493&originalType=url&ratio=1&rotation=0&showTitle=false&size=6915&status=done&style=none&taskId=ub7819b07-ac3a-44e0-926d-7d4e12e74a6&title=) <a name="M9NI1"></a> #### ⑤Web工程中的资源 <a name="IkSsQ"></a> ##### [1]静态资源 - HTML文件 - CSS文件 - JavaScript文件 - 图片文件 <a name="qobik"></a> ##### [2]动态资源 - Servlet <a name="AOSVO"></a> #### ⑥访问资源的地址 <a name="MwYGk"></a> ##### [1]静态资源 > /Web应用名称/静态资源本身的路径 <a name="oM9jL"></a> ##### [2]动态资源 > /Web应用名称/虚拟路径 <a name="zAN3W"></a> #### ⑦Web应用名称 ![image.png](https://cdn.nlark.com/yuque/0/2022/png/26803611/1652612167395-d4f0b1ac-1c75-45d3-8099-5d6241a8f9c2.png#clientId=u80833343-5aeb-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u9ee42d67&margin=%5Bobject%20Object%5D&name=image.png&originHeight=110&originWidth=255&originalType=url&ratio=1&rotation=0&showTitle=false&size=1563&status=done&style=none&taskId=u1ec45e20-04bb-4f3c-b728-9e3e5671c37&title=) <a name="UIzTh"></a> #### ⑧总体的逻辑结构 ![image.png](https://cdn.nlark.com/yuque/0/2022/png/26803611/1652612167729-8252cf7e-6aca-4509-8f36-41928e22a50c.png#clientId=u80833343-5aeb-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=uee9025c7&margin=%5Bobject%20Object%5D&name=image.png&originHeight=507&originWidth=1118&originalType=url&ratio=1&rotation=0&showTitle=false&size=38216&status=done&style=none&taskId=u1d7b8ebb-616a-4992-af5b-754c387efd2&title=) <a name="AcE4J"></a> ## 第三节 Servlet 生命周期 <a name="ZZxwR"></a> ### 1、从Servlet接口说起 ![image.png](https://cdn.nlark.com/yuque/0/2022/png/26803611/1652675124006-983d4821-3979-4725-85fe-2c22975407a5.png#clientId=u2bc35acf-8bf3-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u1c1fd0e7&margin=%5Bobject%20Object%5D&name=image.png&originHeight=133&originWidth=338&originalType=url&ratio=1&rotation=0&showTitle=false&size=3464&status=done&style=none&taskId=u1f66a40b-661f-4796-b610-fc5cd0bfa3e&title=) <a name="jmTbm"></a> ### 2、Servlet创建对象的时机 <a name="ViXw0"></a> #### ①验证方式 在HelloServlet的构造器中执行控制台打印java public HelloServlet(){ System.out.println(“我来了!HelloServlet对象创建!”); } <a name="JFrU1"></a> #### ②打印结果 > 我来了!HelloServlet对象创建! 我是HelloServlet,我执行了! 我是HelloServlet,我执行了! 我是HelloServlet,我执行了! 我是HelloServlet,我执行了! 我是HelloServlet,我执行了! 我是HelloServlet,我执行了! <a name="U3ANg"></a> #### ③结论 - 默认情况下:Servlet在**第一次接收到请求**的时候才创建对象 - 创建对象后,所有的URL地址匹配的请求都由这同一个对象来处理 - Tomcat中,每一个请求会被分配一个线程来处理,所以可以说:Servlet是**单实例,多线程**方式运行的。 - 既然Servlet是多线程方式运行,所以有线程安全方面的可能性,所以不能在处理请求的方法中修改公共属性。 <a name="pbffV"></a> #### ④修改Servlet创建对象的时机 修改web.xml中Servlet的配置:xml HelloServlet com.atguigu.servlet.HelloServlet 1 效果:Web应用启动的时候创建Servlet对象<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/26803611/1652675124007-ec755e58-7e72-4b65-bb9d-96f2a4982de9.png#clientId=u2bc35acf-8bf3-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=ue2e5e97a&margin=%5Bobject%20Object%5D&name=image.png&originHeight=157&originWidth=327&originalType=url&ratio=1&rotation=0&showTitle=false&size=9011&status=done&style=none&taskId=uc2d04d83-7097-4af8-80c8-e89f7aba85b&title=)<br />友情提示:将来配置SpringMVC的时候会看到这样的配置。 <a name="CYQ4q"></a> ### 3、其他环节 ![image.png](https://cdn.nlark.com/yuque/0/2022/png/26803611/1652675125203-acd92c26-82ed-4c5f-9f9e-dbf6f993382f.png#clientId=u2bc35acf-8bf3-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u44c89569&margin=%5Bobject%20Object%5D&name=image.png&originHeight=271&originWidth=410&originalType=url&ratio=1&rotation=0&showTitle=false&size=29184&status=done&style=none&taskId=udade7220-b7ed-4140-8078-c496fb7c326&title=) <a name="XaL3W"></a> ### 4、Servlet容器 <a name="EeNMY"></a> #### ①容器 在开发使用的各种技术中,经常会有很多对象会放在容器中。 <a name="FVpOl"></a> #### ②容器提供的功能 容器会管理内部对象的整个生命周期。对象在容器中才能够正常的工作,得到来自容器的全方位的支持。 - 创建对象 - 初始化 - 工作 - 清理 <a name="XRWxi"></a> #### ③容器本身也是对象 - 特点1:往往是非常大的对象 - 特点2:通常的单例的 <a name="tAIUp"></a> #### ④典型Servlet容器产品举例 - Tomcat - jetty - jboss - Weblogic - WebSphere - glassfish <a name="wVlpV"></a> ### 5、总结 | 名称 | 时机 | 次数 | | --- | --- | --- | | 创建对象 | 默认情况:接收到第一次请求<br />修改启动顺序后:Web应用启动过程中 | 一次 | | 初始化操作 | 创建对象之后 | 一次 | | 处理请求 | 接收到请求 | 多次 | | 销毁操作 | Web应用卸载之前 | 一次 | > 小提示: > 我们学习任何一章的知识,通常都包括两类: > - 现在用得上的——优先级高 > - 以后才用的——优先级低 > 生命周期部分就属于以后才用的知识。 <a name="zRatZ"></a> ## 第四节 ServletConfig和ServletContext <a name="I9p4o"></a> ### 1、类比 ![image.png](https://cdn.nlark.com/yuque/0/2022/png/26803611/1652675242058-b1d68d43-6cc6-40f2-848f-e34771e2153f.png#clientId=u2bc35acf-8bf3-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u58cb644e&margin=%5Bobject%20Object%5D&name=image.png&originHeight=148&originWidth=853&originalType=url&ratio=1&rotation=0&showTitle=false&size=13547&status=done&style=none&taskId=ue4fd5444-3fb2-4e78-9bfb-065fa861922&title=) <a name="wVGe8"></a> ### 2、ServletConfig接口 <a name="UARFS"></a> #### ①接口概览 ![image.png](https://cdn.nlark.com/yuque/0/2022/png/26803611/1652675241014-bea03bff-366b-4c12-a16a-bb761b70fd53.png#clientId=u2bc35acf-8bf3-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=ude59eb24&margin=%5Bobject%20Object%5D&name=image.png&originHeight=115&originWidth=347&originalType=url&ratio=1&rotation=0&showTitle=false&size=3345&status=done&style=none&taskId=u10868aa7-aa0b-4860-91d6-d913f45d25c&title=) <a name="QrqRA"></a> #### ②接口方法介绍 | 方法名 | 作用 | | --- | --- | | getServletName() | 获取<servlet-name>HelloServlet</servlet-name>定义的Servlet名称 | | **getServletContext()** | 获取ServletContext对象 | | getInitParameter() | 获取配置Servlet时设置的『初始化参数』,根据名字获取值 | | getInitParameterNames() | 获取所有初始化参数名组成的Enumeration对象 | <a name="rY9L7"></a> #### ③初始化参数举例xml HelloServlet com.atguigu.servlet.HelloServlet goodMan me 1 <a name="FNxjN"></a> #### ④体验 在HelloServlet中增加代码:java public class HelloServlet implements Servlet { // 声明一个成员变量,用来接收init()方法传入的servletConfig对象 private ServletConfig servletConfig; public HelloServlet(){ System.out.println(“我来了!HelloServlet对象创建!”); } @Override public void init(ServletConfig servletConfig) throws ServletException { System.out.println(“HelloServlet对象初始化”); // 将Tomcat调用init()方法时传入的servletConfig对象赋值给成员变量 this.servletConfig = servletConfig; } @Override public ServletConfig getServletConfig() { // 返回成员变量servletConfig,方便使用 return this.servletConfig; } @Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { // 控制台打印,证明这个方法被调用了 System.out.println(“我是HelloServlet,我执行了!”); // 返回响应字符串 // 1、获取能够返回响应数据的字符流对象 PrintWriter writer = servletResponse.getWriter(); // 2、向字符流对象写入数据 writer.write(“Hello,I am Servlet”); // =============分割线=============== // 测试ServletConfig对象的使用 // 1.获取ServletConfig对象:在init()方法中完成 System.out.println(“servletConfig = “ + servletConfig.getClass().getName()); // 2.通过servletConfig对象获取ServletContext对象 ServletContext servletContext = this.servletConfig.getServletContext(); System.out.println(“servletContext = “ + servletContext.getClass().getName()); // 3.通过servletConfig对象获取初始化参数 Enumeration enumeration = this.servletConfig.getInitParameterNames(); while (enumeration.hasMoreElements()) { String name = enumeration.nextElement(); System.out.println(“name = “ + name); String value = this.servletConfig.getInitParameter(name); System.out.println(“value = “ + value); } } @Override public String getServletInfo() { return null; } @Override public void destroy() { System.out.println(“HelloServlet对象即将销毁,现在执行清理操作”); } } 打印效果: > 我是HelloServlet,我执行了! servletConfig = org.apache.catalina.core.StandardWrapperFacade servletContext = org.apache.catalina.core.ApplicationContextFacade name = goodMan value = me 引申: - 广义Servlet:javax.servlet包下的一系列接口定义的一组**Web开发标准**。遵循这一套标准,不同的Servlet容器提供了不同的实现。 - 狭义Servlet:javax.servlet.Servlet接口和它的实现类,也就是实际开发时使用的具体的Servlet。 Servlet标准和JDBC标准对比: | Servlet标准 | JDBC标准 | | --- | --- | | javax.servlet包下的一系列接口 | javax.sql包下的一系列接口 | | Servlet容器厂商提供的具体实现类 | 数据库厂商提供的实现类(数据库驱动) | 同样都体现了**面向接口编程**的思想,同时也体现了**解耦**的思想:只要接口不变,下层方法有任何变化,都不会影响上层方法。<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/26803611/1652675241020-dccfde73-4d67-41da-bffe-d27d335ff56d.png#clientId=u2bc35acf-8bf3-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=uc6b13d2e&margin=%5Bobject%20Object%5D&name=image.png&originHeight=225&originWidth=239&originalType=url&ratio=1&rotation=0&showTitle=false&size=4629&status=done&style=none&taskId=uf3bfbc78-18db-4bf2-a019-4efa76e28c4&title=) <a name="Ofvfw"></a> ### 3、ServletContext接口 <a name="QLfff"></a> #### ①简介 - 代表:整个Web应用 - 是否单例:是 - 典型的功能: - 获取某个资源的真实路径:getRealPath() - 获取整个Web应用级别的初始化参数:getInitParameter() - 作为Web应用范围的域对象 - 存入数据:setAttribute() - 取出数据:getAttribute() <a name="wY9Ss"></a> #### ②体验 <a name="GjwGw"></a> ##### [1]配置Web应用级别的初始化参数xml handsomeMan alsoMe <a name="wVT8b"></a> ##### [2]获取参数java String handsomeMan = servletContext.getInitParameter(“handsomeMan”); System.out.println(“handsomeMan = “ + handsomeMan); <a name="yjemC"></a> ## 第五节 使用IDEA创建Servlet ![image.png](https://cdn.nlark.com/yuque/0/2022/png/26803611/1652682613912-428611a4-2577-4965-b888-b3604f775cdc.png#clientId=u2bc35acf-8bf3-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=uf0d3ee5e&margin=%5Bobject%20Object%5D&name=image.png&originHeight=868&originWidth=847&originalType=url&ratio=1&rotation=0&showTitle=false&size=105536&status=done&style=none&taskId=u52c7941c-077f-48ce-969b-eb434db21ae&title=)<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/26803611/1652682614246-d83fdd0a-4624-41a5-ad90-ed884f400e83.png#clientId=u2bc35acf-8bf3-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u1e96bc8b&margin=%5Bobject%20Object%5D&name=image.png&originHeight=285&originWidth=380&originalType=url&ratio=1&rotation=0&showTitle=false&size=21564&status=done&style=none&taskId=uedfc9f04-4fa6-4bbc-af84-877edaa28d7&title=)xml QuickServlet com.atguigu.servlet.QuickServlet

QuickServlet /QuickServlet

  1. IDEA生成的Servlet自动继承了HttpServlet
  2. ```java
  3. public class QuickServlet extends HttpServlet {
  4. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  5. }
  6. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  7. }
  8. }

下面是测试代码:

  1. public class QuickServlet extends HttpServlet {
  2. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  3. response.getWriter().write("doPost method processed");
  4. }
  5. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  6. response.getWriter().write("doGet method processed");
  7. }
  8. }
  1. <form action="/app/QuickServlet" method="get">
  2. <button type="submit">发送GET请求</button>
  3. </form>
  4. <br/>
  5. <form action="/app/QuickServlet" method="post">
  6. <button type="submit">发送POST请求</button>
  7. </form>

第六节 Servlet 继承关系[了解]

1、提出问题

  • 为什么IDEA创建Servlet之后不再实现Servlet接口而是继承HttpServlet类
  • Servlet接口HttpServlet类有什么关系?
  • doXxx()方法service()方法有什么关系?

    2、类型关系

    image.png

    3、方法关系

    image.png

    第七节 动态Web工程内编写路径

    1、为什么要写路径?

    JavaWeb入门 - 图147

  • 整个系统要根据功能拆分成许许多多独立资源

  • 资源之间既要完成自身的功能又要和其他资源配合
  • 写路径就是为了从一个资源跳转到下一个资源

    2、为什么写路径这事有点复杂?

    ①先开发再部署

    image.png

  • 工程目录:我们写代码的地方,但是在服务器上运行的不是这个。

  • 部署目录:经过Java源文件编译目录重组后,IDEA就替我们准备好了可以在服务器上运行的部署目录。
  • 区别:因为从工程目录到部署目录经过了目录重组,所以它们的目录结构是不同的。
  • 基准:用户通过浏览器访问服务器,而服务器上运行的是部署目录,所以写路径的时候参考部署目录而不是工程目录。
  • 对应关系工程目录下的web目录对应部署目录的根目录,同时部署目录的根目录也是路径中的Web应用根目录

image.png

②路径的各个组成部分

从最前面一直到Web应用名称这里都是固定写法,到资源名这里要看具体是什么资源。
image.png

[1]具体文件

我们写代码的时候都是在工程目录下操作,所以参照工程目录来说最方便。按照工程目录的目录结构来说,从web目录开始按照实际目录结构写就好了(不包括web目录本身)。
image.png

[2]Servlet

访问Servlet的路径是我们在web.xml中配置的,大家可能注意到了,url-pattern里面的路径我们也是斜杠开头的,但是这个开头的斜杠代表Web应用根目录
image.png
同样是开头的斜杠,超链接路径中的开头斜杠代表服务器根目录,Servlet地址开头的斜杠,代表Web应用根目录,怎么记呢?请看下面的准则:

3、准则

用通俗的大白话来解释:一个路径由谁来解析,其实就是这个路径是谁来用。

路径类型 解析方式
由浏览器解析的路径 开头斜杠代表服务器根目录
由服务器解析的路径 开头斜杠代表Web应用根目录

image.png
那么具体来说,哪些路径是浏览器解析的,哪些路径是服务器解析的呢?

  • 浏览器解析的路径举例:
    • 所有HTML标签中的路径
    • 重定向过程中指定的路径
  • 服务器解析的路径举例:
    • 所有web.xml中配置的路径
    • 请求转发过程中指定的路径

      4、写路径的步骤

      image.png

      5、动态获取上下文路径

      ①上下文路径的概念

      上下文路径(context path)=/Web应用名称

      ②动态获取

      由于项目部署的时候,上下文路径是可以变化的,所以写死有可能发生错误。此时我们通过request对象动态获取上下文路径就不用担心这个问题了。调用下面这个方法,每一次获取的都是当前环境下实际的上下文路径的值。
      1. request.getContextPath()

      如果本节让你感觉很复杂,建议你放慢节奏,尝试下面的步骤:

      • 第一步:先弄清楚每个『名词概念』,清楚的知道我们提到的每一个名词指的是什么。
      • 第二步:弄清楚底层运行原理,其实就是工程目录和部署目录的区别。
      • 第三步:按照我们介绍的步骤一步一步慢慢写,写一步想一想,弄清楚各个部分的对应关系。

第八节 请求转发和重定向

1、接力

发一个请求给Servlet,接力棒就传递到了Servlet手中。而绝大部分情况下,Servlet不能独自完成一切,需要把接力棒继续传递下去,此时我们就需要请求的『转发』『重定向』

2、转发

本质:转交
完整定义:在请求的处理过程中,Servlet完成了自己的任务,需要把请求转交给下一个资源继续处理。
image.png
代码:

  1. request.getRequestDispatcher("/fruit/apple/red/sweet/big.html").forward(request, response);

类比:

代码 类比
request 小货车
getRequestDispatcher(“转发地址”) 告诉司机要去哪
forward(request, response) 出发

关键:由于转发操作的核心部分是在服务器端完成的,所以浏览器感知不到,整个过程中浏览器只发送一次请求
image.png

3、重定向

本质:一种特殊的响应
完整定义:在请求的处理过程中,Servlet完成了自己的任务,然后以一个响应的方式告诉浏览器:“要完成这个任务还需要你另外再访问下一个资源”。
image.png
代码:

  1. response.sendRedirect("/app/fruit/apple/red/sweet/big.html");

image.png
关键:由于重定向操作的核心部分是在浏览器端完成的,所以整个过程中浏览器共发送两次请求

4、对比

转发 重定向
一次请求 两次请求
浏览器地址栏显示的是第一个资源的地址 浏览器地址栏显示的是第二个资源的地址
全程使用的是同一个request对象 全程使用的是不同的request对象
在服务器端完成 在浏览器端完成
目标资源地址由服务器解析 目标资源地址由浏览器解析
目标资源可以在WEB-INF目录下 目标资源不能在WEB-INF目录下
目标资源仅限于本应用内部 目标资源可以是外部资源

5、转发和重定向的应用场景

可以简单的判断:能用转发的先用转发,如果转发不行,再使用重定向。

  • 需要通过同一个request对象把数据携带到目标资源:只能用转发
  • 如果希望前往下一个资源之后,浏览器刷新访问的是第二个资源:只能用重定向

    第九节 获取请求参数

    1、请求参数的概念

    浏览器在给服务器发送请求的同时,携带的参数数据。

    2、浏览器端发送请求参数的基本形式

  • URL地址后面附着的请求参数

    /orange/CharacterServlet?username=汤姆

  • 表单

  • Ajax请求(将来会学到)

    3、服务器端对请求参数的封装

    总体上来说,服务器端将请求参数封装为Map

  • 键:请求参数的名字

  • 值:请求参数的值组成的数组

    4、获取请求参数的方法

    | 方法名 | 返回值类型 | | —- | —- | | request.getParameterMap() | Map | | request.getParameter(“请求参数的名字”) | String | | request.getParameterValues(“请求参数的名字”) | String [] | | request.getParameterNames() | Enumeration |

5、测试

①HTML代码

  1. <!-- 测试请求参数的表单 -->
  2. <form action="/orange/ParamServlet" method="post">
  3. <!-- 单行文本框 -->
  4. <!-- input标签配合type="text"属性生成单行文本框 -->
  5. <!-- name属性定义的是请求参数的名字 -->
  6. <!-- 如果设置了value属性,那么这个值就是单行文本框的默认值 -->
  7. 个性签名:<input type="text" name="signal" value="单行文本框的默认值" /><br/>
  8. <!-- 密码框 -->
  9. <!-- input标签配合type="password"属性生成密码框 -->
  10. <!-- 用户在密码框中填写的内容不会被一明文形式显示 -->
  11. 密码:<input type="password" name="secret" /><br/>
  12. <!-- 单选框 -->
  13. <!-- input标签配合type="radio"属性生成单选框 -->
  14. <!-- name属性一致的radio会被浏览器识别为同一组单选框,同一组内只能选择一个 -->
  15. <!-- 提交表单后,真正发送给服务器的是name属性和value属性的值 -->
  16. <!-- 使用checked="checked"属性设置默认被选中 -->
  17. 请选择你最喜欢的季节:
  18. <input type="radio" name="season" value="spring" />春天
  19. <input type="radio" name="season" value="summer" checked="checked" />夏天
  20. <input type="radio" name="season" value="autumn" />秋天
  21. <input type="radio" name="season" value="winter" />冬天
  22. <br/><br/>
  23. 你最喜欢的动物是:
  24. <input type="radio" name="animal" value="tiger" />路虎
  25. <input type="radio" name="animal" value="horse" checked="checked" />宝马
  26. <input type="radio" name="animal" value="cheetah" />捷豹
  27. <br/>
  28. <!-- 多选框 -->
  29. <!-- input标签和type="checkbox"配合生成多选框 -->
  30. <!-- 多选框被用户选择多个并提交表单后会产生『一个名字携带多个值』的情况 -->
  31. 你最喜欢的球队是:
  32. <input type="checkbox" name="team" value="Brazil"/>巴西
  33. <input type="checkbox" name="team" value="German" checked="checked"/>德国
  34. <input type="checkbox" name="team" value="France"/>法国
  35. <input type="checkbox" name="team" value="China" checked="checked"/>中国
  36. <input type="checkbox" name="team" value="Italian"/>意大利
  37. <br/>
  38. <!-- 下拉列表 -->
  39. <!-- 使用select标签定义下拉列表整体,在select标签内设置name属性 -->
  40. 你最喜欢的运动是:
  41. <select name="sport">
  42. <!-- 使用option属性定义下拉列表的列表项 -->
  43. <!-- 使用option标签的value属性设置提交给服务器的值,在option标签的标签体中设置给用户看的值 -->
  44. <option value="swimming">游泳</option>
  45. <option value="running">跑步</option>
  46. <!-- 使用option标签的selected="selected"属性设置这个列表项默认被选中 -->
  47. <option value="shooting" selected="selected">射击</option>
  48. <option value="skating">溜冰</option>
  49. </select>
  50. <br/>
  51. <br/><br/>
  52. <!-- 表单隐藏域 -->
  53. <!-- input标签和type="hidden"配合生成表单隐藏域 -->
  54. <!-- 表单隐藏域在页面上不会有任何显示,用来保存要提交到服务器但是又不想让用户看到的数据 -->
  55. <input type="hidden" name="userId" value="234654745" />
  56. <!-- 多行文本框 -->
  57. 自我介绍:<textarea name="desc">多行文本框的默认值</textarea>
  58. <br/>
  59. <!-- 普通按钮 -->
  60. <button type="button">普通按钮</button>
  61. <!-- 重置按钮 -->
  62. <button type="reset">重置按钮</button>
  63. <!-- 表单提交按钮 -->
  64. <button type="submit">提交按钮</button>
  65. </form>

②Java代码

  1. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  2. // 获取包含全部请求参数的Map
  3. Map<String, String[]> parameterMap = request.getParameterMap();
  4. // 遍历这个包含全部请求参数的Map
  5. Set<String> keySet = parameterMap.keySet();
  6. for (String key : keySet) {
  7. String[] values = parameterMap.get(key);
  8. System.out.println(key + "=" + Arrays.asList(values));
  9. }
  10. System.out.println("---------------------------");
  11. // 根据请求参数名称获取指定的请求参数值
  12. // getParameter()方法:获取单选框的请求参数
  13. String season = request.getParameter("season");
  14. System.out.println("season = " + season);
  15. // getParameter()方法:获取多选框的请求参数
  16. // 只能获取到多个值中的第一个
  17. String team = request.getParameter("team");
  18. System.out.println("team = " + team);
  19. // getParameterValues()方法:取单选框的请求参数
  20. String[] seasons = request.getParameterValues("season");
  21. System.out.println("Arrays.asList(seasons) = " + Arrays.asList(seasons));
  22. // getParameterValues()方法:取多选框的请求参数
  23. String[] teams = request.getParameterValues("team");
  24. System.out.println("Arrays.asList(teams) = " + Arrays.asList(teams));
  25. }

第十节 请求响应设置字符集

1、请求

①GET请求

[1]设置之前

发送请求参数:

  1. <a href="/orange/CharacterServlet?username=汤姆">测试请求字符集设置:GET请求</a>

接收到的数据:

username = ?±¤?§?

[2]设置方式

image.png
在server.xml第71行的Connector标签中增加URIEncoding属性:

  1. <Connector port="8080" protocol="HTTP/1.1"
  2. connectionTimeout="20000"
  3. redirectPort="8443"
  4. URIEncoding="UTF-8"
  5. />

重启Tomcat实例即可。再重新测试的结果是:

username = 汤姆

②POST请求

[1]设置之前

发送请求参数:

  1. <!-- 测试请求字符集设置:POST请求 -->
  2. <form action="/orange/CharacterServlet" method="post">
  3. 用户名:<input type="text" name="username" /><br/>
  4. <button type="submit">保存</button>
  5. </form>

接收到的数据:

username = ?????????

[2]设置方式
  1. // 使用request对象设置字符集
  2. request.setCharacterEncoding("UTF-8");
  3. // 获取请求参数
  4. String username = request.getParameter("username");
  5. System.out.println("username = " + username);

接收到的数据:

username = 林志玲

[3]需要注意的问题

不能在设置字符集之前获取请求参数!下面是错误的示范

  1. // 获取请求参数(先获取请求参数会导致设置字符集失效)
  2. String username = request.getParameter("username");
  3. // 使用request对象设置字符集
  4. request.setCharacterEncoding("UTF-8");
  5. System.out.println("username = " + username);

2、响应

①设置之前

服务器端代码:

  1. PrintWriter writer = response.getWriter();
  2. writer.write("志玲姐姐你好!");

浏览器显示:

???????

#②设置方式一

编码字符集和解码字符集一致

  1. // 设置服务器端的编码字符集
  2. response.setCharacterEncoding("UTF-8");
  3. PrintWriter writer = response.getWriter();
  4. writer.write("<!DOCTYPE html> ");
  5. writer.write("<html> ");
  6. writer.write("<head> ");
  7. writer.write("<!-- 设置浏览器端的解码字符集 -->");
  8. writer.write(" <meta charset='UTF-8'> ");
  9. writer.write(" <title>Title</title> ");
  10. writer.write("</head> ");
  11. writer.write("<body> ");
  12. writer.write("<p>志玲姐姐你好!</p> ");
  13. writer.write("</body> ");
  14. writer.write("</html> ");

③设置方式二

  1. response.setContentType("text/html;charset=UTF-8");

④需要注意的问题

response.getWriter()不能出现在设置字符集操作的前面(两种方式都不行)。

第八章 Thymeleaf

第一节 MVC

1、提出问题

image.png
我们对HTML的新的期待:既能够正常显示页面,又能在页面中包含动态数据部分。而动态数据单靠HTML本身是无法做到的,所以此时我们需要引入服务器端动态视图模板技术。

2、从三层结构到MVC

①MVC概念

M:Model模型
V:View视图
C:Controller控制器
MVC是在表述层开发中运用的一种设计理念。主张把封装数据的『模型』显示用户界面的『视图』协调调度的『控制器』分开。
好处:

  • 进一步实现各个组件之间的解耦
  • 让各个组件可以单独维护
  • 将视图分离出来以后,我们后端工程师和前端工程师的对接更方便

    ②MVC和三层架构之间关系

    image.png

    3、前后端工程师对接方式

  • 服务器端渲染:前端工程师把前端页面一整套做好交给后端工程师

  • 前后端分离:开会商量JSON格式,然后分头开发。在后端程序尚不可用时,前端工程师会使用Mock.js生成假数据使用,在后端程序可用后再连接实际后端程序获取真实数据。

查看详细内容(opens new window)
image.png

第二节 Thymeleaf简介

1、Thymeleaf的同行

JSP、Freemarker、Velocity等等,它们有一个共同的名字:服务器端模板技术

2、Thymeleaf官方文档中的介绍

官网地址(opens new window)
官方文档

Thymeleaf is a modern server-side Java template engine for both web and standalone environments, capable of processing HTML, XML, JavaScript, CSS and even plain text. The main goal of Thymeleaf is to provide an elegant and highly-maintainable way of creating templates. To achieve this, it builds on the concept of Natural Templates to inject its logic into template files in a way that doesn’t affect the template from being used as a design prototype. This improves communication of design and bridges the gap between design and development teams. Thymeleaf has also been designed from the beginning with Web Standards in mind – especially HTML5 – allowing you to create fully validating templates if that is a need for you.

3、Thymeleaf优势

  • SpringBoot官方推荐使用的视图模板技术,和SpringBoot完美整合。
  • 不经过服务器运算仍然可以直接查看原始值,对前端工程师更友好。 ```html <!DOCTYPE html>

    Original Value

  1. <a name="kDayW"></a>
  2. ### 4、物理视图和逻辑视图
  3. <a name="paobE"></a>
  4. #### ①物理视图
  5. 在Servlet中,将请求转发到一个HTML页面文件时,使用的完整的转发路径就是**物理视图**。![image.png](https://cdn.nlark.com/yuque/0/2022/png/26803611/1652716604765-138bc62d-0567-452b-b85d-ef5c01e97e64.png#clientId=u2bc35acf-8bf3-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=uee05e622&margin=%5Bobject%20Object%5D&name=image.png&originHeight=213&originWidth=209&originalType=url&ratio=1&rotation=0&showTitle=false&size=3685&status=done&style=none&taskId=u961eb7c4-6981-47f5-9308-0af26618fab&title=)
  6. > /pages/user/login_success.html
  7. 如果我们把所有的HTML页面都放在某个统一的目录下,那么转发地址就会呈现出明显的规律:
  8. > /pages/user/login.html /pages/user/login_success.html /pages/user/regist.html /pages/user/regist_success.html
  9. > ……
  10. 路径的开头都是:/pages/user/<br />路径的结尾都是:.html<br />所以,路径开头的部分我们称之为**视图前缀**,路径结尾的部分我们称之为**视图后缀**。
  11. <a name="C5ERu"></a>
  12. #### ②逻辑视图
  13. 物理视图=视图前缀+逻辑视图+视图后缀<br />上面的例子中:
  14. | 视图前缀 | 逻辑视图 | 视图后缀 | 物理视图 |
  15. | --- | --- | --- | --- |
  16. | /pages/user/ | login | .html | /pages/user/login.html |
  17. | /pages/user/ | login_success | .html | /pages/user/login_success.html |
  18. <a name="SjkxP"></a>
  19. ## 第三节 在服务器端引入Thymeleaf环境
  20. <a name="YBXgG"></a>
  21. ### 1、加入jar包
  22. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/26803611/1652716715879-0d732ecc-907e-459f-844d-e45f2afd36af.png#clientId=u2bc35acf-8bf3-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=uc64714c2&margin=%5Bobject%20Object%5D&name=image.png&originHeight=354&originWidth=281&originalType=url&ratio=1&rotation=0&showTitle=false&size=8512&status=done&style=none&taskId=u71c291fd-2926-4868-92bc-58b02c429c1&title=)
  23. <a name="ZOVmm"></a>
  24. ### 2、配置上下文参数
  25. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/26803611/1652716715880-308827c3-90ef-4ec1-8800-00047a1791c3.png#clientId=u2bc35acf-8bf3-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u9acd27a6&margin=%5Bobject%20Object%5D&name=image.png&originHeight=148&originWidth=168&originalType=url&ratio=1&rotation=0&showTitle=false&size=3415&status=done&style=none&taskId=ud81f159e-616d-47e1-8c51-069162f274d&title=)<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/26803611/1652716715893-167a0131-9028-4f8f-afe0-5d877e2c2773.png#clientId=u2bc35acf-8bf3-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=ubf4af646&margin=%5Bobject%20Object%5D&name=image.png&originHeight=170&originWidth=181&originalType=url&ratio=1&rotation=0&showTitle=false&size=3700&status=done&style=none&taskId=uc95b3aa8-ae9e-4a74-9e57-dda7bdb8fe0&title=)<br />物理视图=视图前缀+逻辑视图+视图后缀
  26. ```xml
  27. <!-- 在上下文参数中配置视图前缀和视图后缀 -->
  28. <context-param>
  29. <param-name>view-prefix</param-name>
  30. <param-value>/WEB-INF/view/</param-value>
  31. </context-param>
  32. <context-param>
  33. <param-name>view-suffix</param-name>
  34. <param-value>.html</param-value>
  35. </context-param>

说明:param-value中设置的前缀、后缀的值不是必须叫这个名字,可以根据实际情况和需求进行修改。

为什么要放在WEB-INF目录下? 原因:WEB-INF目录不允许浏览器直接访问,所以我们的视图模板文件放在这个目录下,是一种保护。以免外界可以随意访问视图模板文件。 访问WEB-INF目录下的页面,都必须通过Servlet转发过来,简单说就是:不经过Servlet访问不了。 这样就方便我们在Servlet中检查当前用户是否有权限访问。 那放在WEB-INF目录下之后,重定向进不去怎么办? 重定向到Servlet,再通过Servlet转发到WEB-INF下。

3、创建Servlet基类

这个类大家直接复制粘贴即可,将来使用框架后,这些代码都将被取代。

  1. import org.thymeleaf.TemplateEngine;
  2. import org.thymeleaf.context.WebContext;
  3. import org.thymeleaf.templatemode.TemplateMode;
  4. import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
  5. import javax.servlet.ServletContext;
  6. import javax.servlet.ServletException;
  7. import javax.servlet.http.HttpServlet;
  8. import javax.servlet.http.HttpServletRequest;
  9. import javax.servlet.http.HttpServletResponse;
  10. import java.io.IOException;
  11. public class ViewBaseServlet extends HttpServlet {
  12. private TemplateEngine templateEngine;
  13. @Override
  14. public void init() throws ServletException {
  15. // 1.获取ServletContext对象
  16. ServletContext servletContext = this.getServletContext();
  17. // 2.创建Thymeleaf解析器对象
  18. ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(servletContext);
  19. // 3.给解析器对象设置参数
  20. // ①HTML是默认模式,明确设置是为了代码更容易理解
  21. templateResolver.setTemplateMode(TemplateMode.HTML);
  22. // ②设置前缀
  23. String viewPrefix = servletContext.getInitParameter("view-prefix");
  24. templateResolver.setPrefix(viewPrefix);
  25. // ③设置后缀
  26. String viewSuffix = servletContext.getInitParameter("view-suffix");
  27. templateResolver.setSuffix(viewSuffix);
  28. // ④设置缓存过期时间(毫秒)
  29. templateResolver.setCacheTTLMs(60000L);
  30. // ⑤设置是否缓存
  31. templateResolver.setCacheable(true);
  32. // ⑥设置服务器端编码方式
  33. templateResolver.setCharacterEncoding("utf-8");
  34. // 4.创建模板引擎对象
  35. templateEngine = new TemplateEngine();
  36. // 5.给模板引擎对象设置模板解析器
  37. templateEngine.setTemplateResolver(templateResolver);
  38. }
  39. protected void processTemplate(String templateName, HttpServletRequest req, HttpServletResponse resp) throws IOException {
  40. // 1.设置响应体内容类型和字符集
  41. resp.setContentType("text/html;charset=UTF-8");
  42. // 2.创建WebContext对象
  43. WebContext webContext = new WebContext(req, resp, getServletContext());
  44. // 3.处理模板数据
  45. templateEngine.process(templateName, webContext, resp.getWriter());
  46. }
  47. }

4、HelloWorld

①创建index.html

image.png

②在index.html编写超链接

  1. <a href="/view/TestThymeleafServlet">初步测试Thymeleaf</a>

③创建Servlet

image.png

  1. <servlet>
  2. <servlet-name>TestThymeleafServlet</servlet-name>
  3. <servlet-class>com.atguigu.thymeleaf.servlet.TestThymeleafServlet</servlet-class>
  4. </servlet>
  5. <servlet-mapping>
  6. <servlet-name>TestThymeleafServlet</servlet-name>
  7. <url-pattern>/TestThymeleafServlet</url-pattern>
  8. </servlet-mapping>

④修改Servlet继承的父类

image.png

⑤在doGet()方法中跳转到Thymeleaf页面

  1. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  2. // 1.声明当前请求要前往的视图名称
  3. String viewName = "target";
  4. // 2.调用ViewBaseServlet父类中的解析视图模板的方法
  5. super.processTemplate(viewName, request, response);
  6. }

⑥Thymeleaf页面

  1. <!DOCTYPE html>
  2. <!-- 在html标签内加入Thymeleaf名称空间的声明 -->
  3. <html lang="en" xmlns:th="http://www.thymeleaf.org">
  4. <head>
  5. <meta charset="UTF-8">
  6. <title>Title</title>
  7. </head>
  8. <body>
  9. <!-- 在p标签的基础上,使用Thymeleaf的表达式,解析了一个URL地址 -->
  10. <p th:text="@{'/aaa/bbb/ccc'}">Thymeleaf将在这里显示一个解析出来的URL地址</p>
  11. </body>
  12. </html>

第四节 基本语法:th名称空间

image.png

第五节 基本语法:表达式语法

1、修改标签文本值

代码示例:

  1. <p th:text="标签体新值">标签体原始值</p>

①th:text作用

  • 不经过服务器解析,直接用浏览器打开HTML文件,看到的是『标签体原始值』
  • 经过服务器解析,Thymeleaf引擎根据th:text属性指定的『标签体新值』去替换『标签体原始值』

    ②字面量

    『字面量』是一个经常会遇到的概念,我们可以对照『变量』来理解它的含义。

    1. // a是变量,100是字面量
    2. int a = 100;
    3. System.out.println("a = " + a);
  • 变量:变量名字符串本身不是它的值,它指向的才是它的值

  • 字面量:它就是字面上的含义,我们从『字面』上看到的直接就是它的值

现在我们在th:text属性中使用的就是『字面量』,它不指代任何其他值

2、修改指定属性值

代码示例:

  1. <input type="text" name="username" th:value="文本框新值" value="文本框旧值" />

语法:任何HTML标签原有的属性,前面加上『th:』就都可以通过Thymeleaf来设定新值。

3、解析URL地址

①基本语法

代码示例:

  1. <p th:text="@{/aaa/bbb/ccc}">标签体原始值</p>

经过解析后得到:

/view/aaa/bbb/ccc

所以@{}的作用是在字符串前附加『上下文路径』

这个语法的好处是:实际开发过程中,项目在不同环境部署时,Web应用的名字有可能发生变化。所以上下文路径不能写死。而通过@{}动态获取上下文路径后,不管怎么变都不怕啦!

②首页使用URL地址解析

image.png
如果我们直接访问index.html本身,那么index.html是不需要通过Servlet,当然也不经过模板引擎,所以index.html上的Thymeleaf的任何表达式都不会被解析。
解决办法:通过Servlet访问index.html,这样就可以让模板引擎渲染页面了:
image.png

进一步的好处: 通过上面的例子我们看到,所有和业务功能相关的请求都能够确保它们通过Servlet来处理,这样就方便我们统一对这些请求进行特定规则的限定。

③给URL地址后面附加请求参数

参照官方文档说明:
image.png

4、直接执行表达式

Servlet代码:

  1. request.setAttribute("reqAttrName", "<span>hello-value</span>");

页面代码:

  1. <p>有转义效果:[[${reqAttrName}]]</p>
  2. <p>无转义效果:[(${reqAttrName})]</p>

执行效果:

  1. <p>有转义效果:&lt;span&gt;hello-value&lt;/span&gt;</p>
  2. <p>无转义效果:<span>hello-value</span></p>

第六节 基本语法:访问域对象

1、域对象

①请求域

在请求转发的场景下,我们可以借助HttpServletRequest对象内部给我们提供的存储空间,帮助我们携带数据,把数据发送给转发的目标资源。
请求域:HttpServletRequest对象内部给我们提供的存储空间
image.png

②会话域

image.png

③应用域

image.png

PS:在我们使用的视图是JSP的时候,域对象有4个

  • pageContext
  • request:请求域
  • session:会话域
  • application:应用域

所以在JSP的使用背景下,我们可以说域对象有4个,现在使用Thymeleaf了,没有pageContext。

2、在Servlet中将数据存入属性域

①操作请求域

Servlet中代码:

  1. String requestAttrName = "helloRequestAttr";
  2. String requestAttrValue = "helloRequestAttr-VALUE";
  3. request.setAttribute(requestAttrName, requestAttrValue);

Thymeleaf表达式:

  1. <p th:text="${helloRequestAttr}">request field value</p>

②操作会话域

Servlet中代码:

  1. // ①通过request对象获取session对象
  2. HttpSession session = request.getSession();
  3. // ②存入数据
  4. session.setAttribute("helloSessionAttr", "helloSessionAttr-VALUE");

Thymeleaf表达式:

  1. <p th:text="${session.helloSessionAttr}">这里显示会话域数据</p>

③操作应用域

Servlet中代码:

  1. // ①通过调用父类的方法获取ServletContext对象
  2. ServletContext servletContext = getServletContext();
  3. // ②存入数据
  4. servletContext.setAttribute("helloAppAttr", "helloAppAttr-VALUE");

Thymeleaf表达式:

  1. <p th:text="${application.helloAppAttr}">这里显示应用域数据</p>

第七节 基本语法:获取请求参数

具体来说,我们这里探讨的是在页面上(模板页面)获取请求参数。底层机制是:
image.png

1、一个名字一个值

页面代码:

  1. <p th:text="${param.username}">这里替换为请求参数的值</p>

页面显示效果:
image.png

2、一个名字多个值

页面代码:

  1. <p th:text="${param.team}">这里替换为请求参数的值</p>

页面显示效果:
image.png
如果想要精确获取某一个值,可以使用数组下标。页面代码:

  1. <p th:text="${param.team[0]}">这里替换为请求参数的值</p>
  2. <p th:text="${param.team[1]}">这里替换为请求参数的值</p>

页面显示效果:
image.png

第八节 基本语法:内置对象

1、概念

所谓内置对象其实就是在表达式中可以直接使用的对象。

2、基本内置对象

image.png
用法举例:

  1. <h3>表达式的基本内置对象</h3>
  2. <p th:text="${#request.getClass().getName()}">这里显示#request对象的全类名</p>
  3. <p th:text="${#request.getContextPath()}">调用#request对象的getContextPath()方法</p>
  4. <p th:text="${#request.getAttribute('helloRequestAttr')}">调用#request对象的getAttribute()方法,读取属性域</p>

基本思路:

  • 如果不清楚这个对象有哪些方法可以使用,那么就通过getClass().getName()获取全类名,再回到Java环境查看这个对象有哪些方法
  • 内置对象的方法可以直接调用
  • 调用方法时需要传参的也可以直接传入参数

    3、公共内置对象

    image.png
    Servlet中将List集合数据存入请求域:

    1. request.setAttribute("aNotEmptyList", Arrays.asList("aaa","bbb","ccc"));
    2. request.setAttribute("anEmptyList", new ArrayList<>());

    页面代码:

    1. <p>#list对象isEmpty方法判断集合整体是否为空aNotEmptyList:<span th:text="${#lists.isEmpty(aNotEmptyList)}">测试#lists</span></p>
    2. <p>#list对象isEmpty方法判断集合整体是否为空anEmptyList:<span th:text="${#lists.isEmpty(anEmptyList)}">测试#lists</span></p>

    公共内置对象对应的源码位置:
    image.png

    第九节 基本语法:${}中的表达式本质是OGNL

    1、OGNL

    OGNL:Object-Graph Navigation Language对象-图 导航语言

    2、对象图

    从根对象触发,通过特定的语法,逐层访问对象的各种属性。
    image.png

    3、OGNL语法

    ①起点

    在Thymeleaf环境下,${}中的表达式可以从下列元素开始:

  • 访问属性域的起点

    • 请求域属性名
    • session
    • application
  • param
  • 内置对象

    • request

    • session

    • lists

    • strings

      ②属性访问语法

  • 访问对象属性:使用getXxx()、setXxx()方法定义的属性

    • 对象.属性名
  • 访问List集合或数组
    • 集合或数组[下标]
  • 访问Map集合

    • Map集合.key
    • Map集合[‘key’]

      第十节 基本语法:分支与迭代

      1、分支

      ①if和unless

      让标记了th:if、th:unless的标签根据条件决定是否显示。
      示例的实体类:

      1. public class Employee {
      2. private Integer empId;
      3. private String empName;
      4. private Double empSalary;

      示例的Servlet代码:

      1. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
      2. // 1.创建ArrayList对象并填充
      3. List<Employee> employeeList = new ArrayList<>();
      4. employeeList.add(new Employee(1, "tom", 500.00));
      5. employeeList.add(new Employee(2, "jerry", 600.00));
      6. employeeList.add(new Employee(3, "harry", 700.00));
      7. // 2.将集合数据存入请求域
      8. request.setAttribute("employeeList", employeeList);
      9. // 3.调用父类方法渲染视图
      10. super.processTemplate("list", request, response);
      11. }

      示例的HTML代码: ```html

      员工编号 员工姓名 员工工资
      抱歉!没有查询到你搜索的数据!
      有数据!
      有数据!
  1. if配合not关键词和unless配合原表达式效果是一样的,看自己的喜好。
  2. <a name="dvOcZ"></a>
  3. #### ②switch
  4. ```html
  5. <h3>测试switch</h3>
  6. <div th:switch="${user.memberLevel}">
  7. <p th:case="level-1">银牌会员</p>
  8. <p th:case="level-2">金牌会员</p>
  9. <p th:case="level-3">白金会员</p>
  10. <p th:case="level-4">钻石会员</p>
  11. </div>

2、迭代

  1. <h3>测试each</h3>
  2. <table>
  3. <thead>
  4. <tr>
  5. <th>员工编号</th>
  6. <th>员工姓名</th>
  7. <th>员工工资</th>
  8. </tr>
  9. </thead>
  10. <tbody th:if="${#lists.isEmpty(employeeList)}">
  11. <tr>
  12. <td colspan="3">抱歉!没有查询到你搜索的数据!</td>
  13. </tr>
  14. </tbody>
  15. <tbody th:if="${not #lists.isEmpty(employeeList)}">
  16. <!-- 遍历出来的每一个元素的名字 : ${要遍历的集合} -->
  17. <tr th:each="employee : ${employeeList}">
  18. <td th:text="${employee.empId}">empId</td>
  19. <td th:text="${employee.empName}">empName</td>
  20. <td th:text="${employee.empSalary}">empSalary</td>
  21. </tr>
  22. </tbody>
  23. </table>

在迭代过程中,可以参考下面的说明使用迭代状态:
image.png

  1. <h3>测试each</h3>
  2. <table>
  3. <thead>
  4. <tr>
  5. <th>员工编号</th>
  6. <th>员工姓名</th>
  7. <th>员工工资</th>
  8. <th>迭代状态</th>
  9. </tr>
  10. </thead>
  11. <tbody th:if="${#lists.isEmpty(employeeList)}">
  12. <tr>
  13. <td colspan="3">抱歉!没有查询到你搜索的数据!</td>
  14. </tr>
  15. </tbody>
  16. <tbody th:if="${not #lists.isEmpty(employeeList)}">
  17. <!-- 遍历出来的每一个元素的名字 : ${要遍历的集合} -->
  18. <tr th:each="employee,empStatus : ${employeeList}">
  19. <td th:text="${employee.empId}">empId</td>
  20. <td th:text="${employee.empName}">empName</td>
  21. <td th:text="${employee.empSalary}">empSalary</td>
  22. <td th:text="${empStatus.count}">count</td>
  23. </tr>
  24. </tbody>
  25. </table>

第十一节 基本语法:包含其他模板文件

1、应用场景

抽取各个页面的公共部分:
image.png

2、创建页面的代码片段

使用th:fragment来给这个片段命名:

  1. <div th:fragment="header">
  2. <p>被抽取出来的头部内容</p>
  3. </div>

3、包含到有需要的页面

语法 效果
th:insert 把目标的代码片段整个插入到当前标签内部
th:replace 用目标的代码替换当前标签
th:include 把目标的代码片段去除最外层标签,然后再插入到当前标签内部

页面代码举例:

  1. <!-- 代码片段所在页面的逻辑视图 :: 代码片段的名称 -->
  2. <div id="badBoy" th:insert="segment :: header">
  3. div标签的原始内容
  4. </div>
  5. <div id="worseBoy" th:replace="segment :: header">
  6. div标签的原始内容
  7. </div>
  8. <div id="worstBoy" th:include="segment :: header">
  9. div标签的原始内容
  10. </div>

加餐 最凝练的CRUD

1、建模

①物理建模

  1. CREATE DATABASE `view-demo`CHARACTER SET utf8;
  2. USE `view-demo`;
  3. CREATE TABLE t_soldier(
  4. soldier_id INT PRIMARY KEY AUTO_INCREMENT,
  5. soldier_name CHAR(100),
  6. soldier_weapon CHAR(100)
  7. );

②逻辑建模

  1. public class Soldier {
  2. private Integer soldierId;
  3. private String soldierName;
  4. private String soldierWeapon;

2、总体架构

image.png

3、搭建持久化层所需环境

①导入jar包

commons-dbutils-1.6.jar druid-1.1.9.jar hamcrest-core-1.3.jar junit-4.12.jar mysql-connector-java-5.1.37-bin.jar

②创建jdbc.properties

维护基本连接信息

  1. driverClassName=com.mysql.jdbc.Driver
  2. url=jdbc:mysql://192.168.198.100:3306/view-demo
  3. username=root
  4. password=atguigu
  5. initialSize=10
  6. maxActive=20
  7. maxWait=10000

③创建JDBCUtils工具类

  1. import com.alibaba.druid.pool.DruidDataSourceFactory;
  2. import javax.sql.DataSource;
  3. import java.io.InputStream;
  4. import java.sql.Connection;
  5. import java.sql.SQLException;
  6. import java.util.Properties;
  7. public class JDBCUtil {
  8. // 将数据源对象设置为静态属性,保证大对象的单一实例
  9. private static DataSource dataSource;
  10. static {
  11. // 1.创建一个用于存储外部属性文件信息的Properties对象
  12. Properties properties = new Properties();
  13. // 2.使用当前类的类加载器加载外部属性文件:jdbc.properties
  14. InputStream inputStream = JDBCUtil.class.getClassLoader().getResourceAsStream("jdbc.properties");
  15. try {
  16. // 3.将外部属性文件jdbc.properties中的数据加载到properties对象中
  17. properties.load(inputStream);
  18. // 4.创建数据源对象
  19. dataSource = DruidDataSourceFactory.createDataSource(properties);
  20. } catch (Exception e) {
  21. e.printStackTrace();
  22. }
  23. }
  24. /**
  25. * 从数据源中获取数据库连接
  26. * @return 数据库连接对象
  27. */
  28. public static Connection getConnection() {
  29. Connection connection = null;
  30. try {
  31. connection = dataSource.getConnection();
  32. } catch (SQLException e) {
  33. e.printStackTrace();
  34. throw new RuntimeException(e);
  35. }
  36. return connection;
  37. }
  38. /**
  39. * 释放数据库连接
  40. * @param connection 要执行释放操作的连接对象
  41. */
  42. public static void releaseConnection(Connection connection) {
  43. if (connection != null) {
  44. try {
  45. connection.close();
  46. } catch (SQLException e) {
  47. e.printStackTrace();
  48. throw new RuntimeException(e);
  49. }
  50. }
  51. }
  52. }

测试能否正常连接数据库:

  1. public class DemoTest {
  2. @Test
  3. public void testConnection() {
  4. Connection connection = JDBCUtil.getConnection();
  5. System.out.println("connection = " + connection);
  6. }
  7. }

④BaseDao

  1. public class BaseDao<T> {
  2. private QueryRunner queryRunner = new QueryRunner();
  3. /**
  4. * 通用的增删改方法
  5. * @param sql
  6. * @param param
  7. * @return
  8. */
  9. public int update(String sql, Object ... param) {
  10. Connection connection = JDBCUtil.getConnection();
  11. int count = 0;
  12. try {
  13. count = queryRunner.update(connection, sql, param);
  14. } catch (SQLException e) {
  15. e.printStackTrace();
  16. throw new RuntimeException(e);
  17. } finally {
  18. // 关闭数据库连接
  19. JDBCUtil.releaseConnection(connection);
  20. }
  21. return count;
  22. }
  23. /**
  24. * 查询单个对象的通用方法
  25. * @param clazz
  26. * @param sql
  27. * @param param
  28. * @return
  29. */
  30. public T getBean(Class<T> clazz, String sql, Object ... param) {
  31. Connection connection = JDBCUtil.getConnection();
  32. T bean = null;
  33. try {
  34. bean = queryRunner.query(connection, sql, new BeanHandler<>(clazz), param);
  35. } catch (SQLException e) {
  36. e.printStackTrace();
  37. throw new RuntimeException(e);
  38. } finally {
  39. // 关闭数据库连接
  40. JDBCUtil.releaseConnection(connection);
  41. }
  42. return bean;
  43. }
  44. /**
  45. * 查询集合对象的通用方法
  46. * @param clazz
  47. * @param sql
  48. * @param param
  49. * @return
  50. */
  51. public List<T> getBeanList(Class<T> clazz, String sql, Object ... param) {
  52. Connection connection = JDBCUtil.getConnection();
  53. List<T> beanList = null;
  54. try {
  55. beanList = queryRunner.query(connection, sql, new BeanListHandler<>(clazz), param);
  56. } catch (SQLException e) {
  57. e.printStackTrace();
  58. throw new RuntimeException(e);
  59. } finally {
  60. // 关闭数据库连接
  61. JDBCUtil.releaseConnection(connection);
  62. }
  63. return beanList;
  64. }
  65. }

4、搭建表述层所需环境

本质上就是Thymeleaf所需要的环境

①导入jar包

attoparser-2.0.5.RELEASE.jar javassist-3.20.0-GA.jar log4j-1.2.15.jar ognl-3.1.26.jar slf4j-api-1.7.25.jar slf4j-log4j12-1.7.25.jar thymeleaf-3.0.12.RELEASE.jar unbescape-1.1.6.RELEASE.jar

②创建ViewBaseServlet

  1. import org.thymeleaf.TemplateEngine;
  2. import org.thymeleaf.context.WebContext;
  3. import org.thymeleaf.templatemode.TemplateMode;
  4. import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
  5. import javax.servlet.ServletContext;
  6. import javax.servlet.ServletException;
  7. import javax.servlet.http.HttpServlet;
  8. import javax.servlet.http.HttpServletRequest;
  9. import javax.servlet.http.HttpServletResponse;
  10. import java.io.IOException;
  11. public class ViewBaseServlet extends HttpServlet {
  12. private TemplateEngine templateEngine;
  13. @Override
  14. public void init() throws ServletException {
  15. // 1.获取ServletContext对象
  16. ServletContext servletContext = this.getServletContext();
  17. // 2.创建Thymeleaf解析器对象
  18. ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(servletContext);
  19. // 3.给解析器对象设置参数
  20. // ①HTML是默认模式,明确设置是为了代码更容易理解
  21. templateResolver.setTemplateMode(TemplateMode.HTML);
  22. // ②设置前缀
  23. String viewPrefix = servletContext.getInitParameter("view-prefix");
  24. templateResolver.setPrefix(viewPrefix);
  25. // ③设置后缀
  26. String viewSuffix = servletContext.getInitParameter("view-suffix");
  27. templateResolver.setSuffix(viewSuffix);
  28. // ④设置缓存过期时间(毫秒)
  29. templateResolver.setCacheTTLMs(60000L);
  30. // ⑤设置是否缓存
  31. templateResolver.setCacheable(true);
  32. // ⑥设置服务器端编码方式
  33. templateResolver.setCharacterEncoding("utf-8");
  34. // 4.创建模板引擎对象
  35. templateEngine = new TemplateEngine();
  36. // 5.给模板引擎对象设置模板解析器
  37. templateEngine.setTemplateResolver(templateResolver);
  38. }
  39. protected void processTemplate(String templateName, HttpServletRequest req, HttpServletResponse resp) throws IOException {
  40. // 1.设置响应体内容类型和字符集
  41. resp.setContentType("text/html;charset=UTF-8");
  42. // 2.创建WebContext对象
  43. WebContext webContext = new WebContext(req, resp, getServletContext());
  44. // 3.处理模板数据
  45. templateEngine.process(templateName, webContext, resp.getWriter());
  46. }
  47. }

③配置web.xml

  1. <!-- 在上下文参数中配置视图前缀和视图后缀 -->
  2. <context-param>
  3. <param-name>view-prefix</param-name>
  4. <param-value>/WEB-INF/view/</param-value>
  5. </context-param>
  6. <context-param>
  7. <param-name>view-suffix</param-name>
  8. <param-value>.html</param-value>
  9. </context-param>

④创建view目录

image.png

5、功能清单

  • 显示首页:浏览器通过index.html访问首页Servlet,然后再解析对应的模板视图
  • 显示列表:在首页点击超链接,跳转到目标页面把所有士兵的信息列表显示出来
  • 删除信息:在列表上点击删除超链接,执行信息的删除操作
  • 新增信息:
    • 在列表页面点击超链接跳转到新增士兵信息的表单页面
    • 在新增信息的表单页面点击提交按钮执行保存
  • 更新信息:

    • 在列表上点击更新超链接,跳转到更新士兵信息的表单页面:表单回显
    • 在更新信息的表单页面点击提交按钮执行更新

      6、显示首页

      ①目标

      浏览器访问index.html,通过首页Servlet,渲染视图,显示首页。

      ②思路

      image.png

      ③代码

      [1]创建PortalServlet
      1. <servlet>
      2. <servlet-name>PortalServlet</servlet-name>
      3. <servlet-class>com.atguigu.demo.servlet.PortalServlet</servlet-class>
      4. </servlet>
      5. <servlet-mapping>
      6. <servlet-name>PortalServlet</servlet-name>
      7. <url-pattern>/index.html</url-pattern>
      8. </servlet-mapping>

      Servlet代码:

      1. public class PortalServlet extends ViewBaseServlet {
      2. @Override
      3. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
      4. String viewName = "portal";
      5. super.processTemplate(viewName, request, response);
      6. }
      7. @Override
      8. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
      9. }
      10. }

      [2]创建portal.html

      image.png ```html <!DOCTYPE html>

      显示士兵信息列表

  1. <a name="ZAFIT"></a>
  2. ### 7、显示列表
  3. <a name="i85Yf"></a>
  4. #### ①目标
  5. 在目标页面显示所有士兵信息,士兵信息是从数据库查询出来的
  6. <a name="UDkl7"></a>
  7. #### ②思路
  8. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/26803611/1652717852880-ba97180b-5692-4bbb-9567-917a0ae63e9f.png#clientId=u2bc35acf-8bf3-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u2b873c53&margin=%5Bobject%20Object%5D&name=image.png&originHeight=403&originWidth=440&originalType=url&ratio=1&rotation=0&showTitle=false&size=18224&status=done&style=none&taskId=u3e6aab4a-c204-4996-9ffc-c95d6ff5112&title=)
  9. <a name="C3IqM"></a>
  10. #### ③代码
  11. <a name="g6SBy"></a>
  12. ##### [1]ModelBaseServlet
  13. 创建这个基类的原因是:我们希望每一个模块能够对应同一个Servlet,这个模块所需要调用的所有方法都集中在同一个Servlet中。如果没有这个ModelBaseServlet基类,我们doGet()、doPost()方法可以用来处理请求,这样一来,每一个方法都需要专门创建一个Servlet(就好比咱们之前的LoginServlet、RegisterServlet其实都应该合并为UserServlet)。
  14. ```java
  15. public class ModelBaseServlet extends ViewBaseServlet {
  16. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  17. // 在doGet()方法中调用doPost()方法,这样就可以在doPost()方法中集中处理所有请求
  18. doPost(request, response);
  19. }
  20. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  21. // 1.从请求参数中获取method对应的数据
  22. String method = request.getParameter("method");
  23. // 2.通过反射调用method对应的方法
  24. // ①获取Class对象
  25. Class<? extends ModelBaseServlet> clazz = this.getClass();
  26. try {
  27. // ②获取method对应的Method对象
  28. Method methodObject = clazz.getDeclaredMethod(method, HttpServletRequest.class, HttpServletResponse.class);
  29. // ③打开访问权限
  30. methodObject.setAccessible(true);
  31. // ④通过Method对象调用目标方法
  32. methodObject.invoke(this, request, response);
  33. } catch (Exception e) {
  34. e.printStackTrace();
  35. throw new RuntimeException(e);
  36. }
  37. }
  38. }

[2]SoldierDao.selectSoldierList()

image.png
image.png
接口方法:

  1. public interface SoldierDao {
  2. List<Soldier> selectSoldierList();
  3. }

实现类方法:

  1. public class SoldierDaoImpl extends BaseDao<Soldier> implements SoldierDao {
  2. @Override
  3. public List<Soldier> selectSoldierList() {
  4. String sql = "select soldier_id soldierId,soldier_name soldierName,soldier_weapon soldierWeapon from t_soldier";
  5. return getBeanList(Soldier.class, sql);
  6. }
  7. }

[3]SoldierService.getSoldierList()

image.png
接口方法:

  1. public interface SoldierService {
  2. List<Soldier> getSoldierList();
  3. }

实现类方法:

  1. public class SoldierServiceImpl implements SoldierService {
  2. private SoldierDao soldierDao = new SoldierDaoImpl();
  3. @Override
  4. public List<Soldier> getSoldierList() {
  5. List<Soldier> soldierList = soldierDao.selectSoldierList();
  6. return soldierList;
  7. }
  8. }

[4]SoldierServlet.showList()
  1. protected void showList(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  2. // 1.调用Service方法获取集合数据
  3. List<Soldier> soldierList = soldierService.getSoldierList();
  4. // 2.将集合数据存入请求域
  5. request.setAttribute("soldierList", soldierList);
  6. // 3.渲染视图(在渲染的过程中,已经包含了转发)
  7. processTemplate("list", request, response);
  8. }

8、删除功能

①目标

点击页面上的超链接,把数据库表中的记录删除。

②思路

[1]先不考虑后续

image.png

[2]加上后续返回响应页面

image.png

③代码

[1]完成删除超链接

image.png

  1. <a th:href="@{/SoldierServlet(soldierId=${soldier.soldierId},method='remove')}">删除</a>

关于@{地址}附加请求参数的语法格式:

  • 只有一个请求参数:@{地址(请求参数名=普通字符串)}或@{地址(请求参数名=${需要解析的表达式})}
  • 多个请求参数:@{地址(名=值,名=值)}

官方文档中的说明如下:
image.png

[2]Servlet方法
  1. protected void remove(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  2. // 1.从请求参数中获取士兵信息的id值
  3. String soldierId = request.getParameter("soldierId");
  4. // 2.调用Service方法执行删除操作
  5. soldierService.remove(soldierId);
  6. // 3.后续……
  7. // 方案一:还是直接前往list.html,需要重新查询soldierList数据,代码重复
  8. // 1.调用Service方法获取集合数据
  9. // List<Soldier> soldierList = soldierService.getSoldierList();
  10. // 2.将集合数据存入请求域
  11. // request.setAttribute("soldierList", soldierList);
  12. // processTemplate("list", request, response);
  13. // 方案二:直接调用隔壁的showList()
  14. // 也能实现需求,但是总感觉这样调用方法破坏了程序的结构
  15. // showList(request, response);
  16. // 方案三:通过请求转发的方式间接调用showList()方法
  17. // request.getRequestDispatcher("/SoldierServlet?method=showList").forward(request, response);
  18. // 方案四:通过请求重定向的方式间接调用showList()方法
  19. response.sendRedirect(request.getContextPath() + "/SoldierServlet?method=showList");
  20. }

[3]Service方法
  1. @Override
  2. public void remove(String soldierId) {
  3. soldierDao.delete(soldierId);
  4. }

[4]Dao方法
  1. @Override
  2. public void delete(String soldierId) {
  3. String sql = "delete from t_soldier where soldier_id=?";
  4. update(sql, soldierId);
  5. }

9、前往新增信息的表单页面

①创建超链接

  1. <a th:href="@{/SoldierServlet?method=toAddPage}">前往新增页面</a>

②Servlet

  1. protected void toAddPage(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  2. processTemplate("add-page", request, response);
  3. }

③创建表单页面

image.png

  1. <form th:action="@{/SoldierServlet}" method="post">
  2. <input type="hidden" name="method" value="saveSoldier" />
  3. 士兵姓名:<input type="text" name="soldierName" /><br/>
  4. 士兵武器:<input type="text" name="soldierWeapon" /><br/>
  5. <button type="submit">保存</button>
  6. </form>

10、执行保存

①目标

提交表单后,将表单数据封装为Soldier对象,然后将Soldier对象保存到数据库。

②思路

image.png

③代码

[1]Servlet方法
  1. protected void saveSoldier(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  2. // 1.获取请求参数
  3. String soldierName = request.getParameter("soldierName");
  4. String soldierWeapon = request.getParameter("soldierWeapon");
  5. // 2.创建Soldier对象
  6. Soldier soldier = new Soldier(null, soldierName, soldierWeapon);
  7. // 3.调用Service方法
  8. soldierService.saveSoldier(soldier);
  9. // 4.重定向请求
  10. response.sendRedirect(request.getContextPath() + "/SoldierServlet?method=showList");
  11. }

[2]Service方法
  1. @Override
  2. public void saveSoldier(Soldier soldier) {
  3. soldierDao.insertSoldier(soldier);
  4. }

[3]Dao方法
  1. @Override
  2. public void insertSoldier(Soldier soldier) {
  3. String sql = "insert into t_soldier(soldier_name,soldier_weapon) values(?,?)";
  4. update(sql, soldier.getSoldierName(), soldier.getSoldierWeapon());
  5. }

11、前往修改信息的表单页面

image.png

①创建超链接

  1. <a th:href="@{/SoldierServlet(soldierId=${soldier.soldierId},method=toEditPage)}">编辑</a>

②Servlet方法

  1. protected void toEditPage(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  2. // 1.从请求参数获取soldierId
  3. String soldierId = request.getParameter("soldierId");
  4. // 2.根据soldierId查询Soldier对象
  5. Soldier soldier = soldierService.getSoldierById(soldierId);
  6. // 3.将Soldier对象存入请求域
  7. request.setAttribute("soldier", soldier);
  8. // 4.前往更新的表单页面
  9. processTemplate("edit-page", request, response);
  10. }

③Service方法

  1. @Override
  2. public Soldier getSoldierById(String soldierId) {
  3. return soldierDao.selectSoldierByPrimaryKey(soldierId);
  4. }

④Dao方法

  1. @Override
  2. public Soldier selectSoldierByPrimaryKey(String soldierId) {
  3. String sql = "select soldier_id soldierId,soldier_name soldierName,soldier_weapon soldierWeapon from t_soldier where soldier_id=?";
  4. return getBean(Soldier.class, sql, soldierId);
  5. }

⑤表单页面

  1. <form th:action="@{/SoldierServlet}" method="post">
  2. <input type="hidden" name="method" value="updateSoldier" />
  3. <input type="hidden" name="soldierId" th:value="${soldier.soldierId}" />
  4. 士兵姓名:<input type="text" name="soldierName" th:value="${soldier.soldierName}" /><br/>
  5. 士兵武器:<input type="text" name="soldierWeapon" th:value="${soldier.soldierWeapon}" /><br/>
  6. <button type="submit">更新</button>
  7. </form>

12、执行更新

image.png

①Servlet方法

  1. protected void updateSoldier(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  2. // 1.获取请求参数
  3. String soldierIdOrigin = request.getParameter("soldierId");
  4. Integer soldierId = Integer.parseInt(soldierIdOrigin);
  5. String soldierName = request.getParameter("soldierName");
  6. String soldierWeapon = request.getParameter("soldierWeapon");
  7. // 2.封装对象
  8. Soldier soldier = new Soldier(soldierId, soldierName, soldierWeapon);
  9. // 3.调用Service方法执行更新
  10. soldierService.updateSoldier(soldier);
  11. // 4.重定向请求
  12. response.sendRedirect(request.getContextPath() + "/SoldierServlet?method=showList");
  13. }

②Service方法

  1. @Override
  2. public void updateSoldier(Soldier soldier) {
  3. soldierDao.updateSoldier(soldier);
  4. }

③Dao方法

  1. @Override
  2. public void updateSoldier(Soldier soldier) {
  3. String sql = "update t_soldier set soldier_name=?,soldier_weapon=? where soldier_id=?";
  4. update(sql, soldier.getSoldierName(), soldier.getSoldierWeapon(), soldier.getSoldierId());
  5. }

13、请求字符集设置

  • 设置请求体字符集需要调用request.setCharacterEncoding(“UTF-8”);
  • request.setCharacterEncoding(“UTF-8”);要求在所有request.getParameter()前面
  • 在执行子类Servlet方法时,其实都是先调用父类ModelBaseServlet的doPost()方法
  • doPost()方法中包含获取method请求参数的操作
  • 所以最前面的request.getParameter()在doPost()方法中
  • 所以request.setCharacterEncoding(“UTF-8”);要放在doPost()方法的request.getParameter()前面

    1. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    2. // 0.在所有request.getParameter()前面设置解析请求体的字符集
    3. request.setCharacterEncoding("UTF-8");
    4. // 1.从请求参数中获取method对应的数据
    5. String method = request.getParameter("method");
    6. // ……

    第九章 会话控制

    第一节 提出问题与解决方案核心代码

    1、提出问题

    image.png
    保持用户登录状态,背后的底层逻辑是:服务器在接收到用户请求的时候,有办法判断这个请求来自于之前的某一个用户。所以保持登录状态,本质上是保持『会话状态』

    2、解决方案

    ①结论

    使用HttpSession对象,将数据存入会话域就能保持会话状态。

    1. HttpSession session = request.getSession();
    2. session.setAttribute("user", user);

    ②demo体验

    [1]准备环境

    参考会话控制demo原始纯净版

    [2]创建将数据存入会话域的Servlet
    1. public class HelloWorldServlet extends ModelBaseServlet {
    2. protected void setValue(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    3. // 1.通过request对象获取session对象
    4. HttpSession session = request.getSession();
    5. // 2.设置数据名称和数据的值
    6. String attrName = "sessionHelloAttrName";
    7. String attrValue = "sessionHelloAttrValue";
    8. // 3.将数据存入会话域
    9. session.setAttribute(attrName, attrValue);
    10. // 4.渲染视图
    11. processTemplate("page-target", request, response);
    12. }
    13. }

    [3]在其他页面从会话域取出数据

    image.png

    1. <div th:if="${#strings.isEmpty(session.sessionHelloAttrName)}">
    2. 没有从会话域获取到数据
    3. </div>
    4. <div th:if="${not #strings.isEmpty(session.sessionHelloAttrName)}">
    5. <p>从会话域读取到:<span th:text="${session.sessionHelloAttrName}"></span></p>
    6. </div>

    [4]操作效果

    image.png

    第二节 Cookie的工作机制

    1、HTTP协议和会话控制

    HTTP协议本身是无状态的。单靠HTTP协议本身无法判断一个请求来自于哪一个浏览器,所以也就没法识别用户的身份状态。
    image.png

    2、Cookie介绍

    ①本质

  • 在浏览器端临时存储数据

  • 键值对
  • 键和值都是字符串类型
  • 数据量很小

    ②Cookie在浏览器和服务器之间的传递

    [1]没有Cookie的状态
    在服务器端没有创建Cookie并返回的情况下,浏览器端不会保存Cookie信息。双方在请求和响应的过程中也不会携带Cookie的数据。
    [2]创建Cookie对象并返回
    ```java // 1.创建Cookie对象 Cookie cookie = new Cookie(“cookie-message”, “hello-cookie”);

// 2.将Cookie对象添加到响应中 response.addCookie(cookie);

// 3.返回响应 processTemplate(“page-target”, request, response);

  1. <a name="YRNRz"></a>
  2. ##### [3]服务器端返回Cookie的响应消息头
  3. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/26803611/1652763774446-f0d894e4-0473-4b5f-9fb0-cdc6cd937595.png#clientId=ud2720a23-2514-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u3f7aded0&margin=%5Bobject%20Object%5D&name=image.png&originHeight=174&originWidth=362&originalType=url&ratio=1&rotation=0&showTitle=false&size=7451&status=done&style=none&taskId=u4a27d60b-fe66-4b19-aa6d-e7682c38d34&title=)
  4. <a name="QdZdj"></a>
  5. ##### [4]浏览器拿到Cookie之后
  6. 浏览器拿到Cookie之后,以后的每一个请求都会携带Cookie信息。<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/26803611/1652763774446-80109978-915f-4592-a35a-8ee04b0d97a5.png#clientId=ud2720a23-2514-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=uab2b9a2b&margin=%5Bobject%20Object%5D&name=image.png&originHeight=194&originWidth=327&originalType=url&ratio=1&rotation=0&showTitle=false&size=7700&status=done&style=none&taskId=u67a7bc85-f287-4e35-b0ec-c3b13b0556c&title=)
  7. <a name="lUEKw"></a>
  8. ##### [5]服务器端读取Cookie的信息
  9. ```java
  10. // 1.通过request对象获取Cookie的数组
  11. Cookie[] cookies = request.getCookies();
  12. // 2.遍历数组
  13. for (Cookie cookie : cookies) {
  14. System.out.println("cookie.getName() = " + cookie.getName());
  15. System.out.println("cookie.getValue() = " + cookie.getValue());
  16. System.out.println();
  17. }

③Cookie时效性

[1]理论
  • 会话级Cookie
    • 服务器端并没有明确指定Cookie的存在时间
    • 在浏览器端,Cookie数据存在于内存中
    • 只要浏览器还开着,Cookie数据就一直都在
    • 浏览器关闭,内存中的Cookie数据就会被释放
  • 持久化Cookie
    • 服务器端明确设置了Cookie的存在时间
    • 在浏览器端,Cookie数据会被保存到硬盘上
    • Cookie在硬盘上存在的时间根据服务器端限定的时间来管控,不受浏览器关闭的影响
    • 持久化Cookie到达了预设的时间会被释放

服务器端返回Cookie时附带过期时间的响应消息头如下:
image.png
服务器通知浏览器删除Cookie时的响应消息头如下:
image.png

[2]代码
  1. // ※给Cookie设置过期时间
  2. // 正数:Cookie的过期时间,以秒为单位
  3. // 负数:表示这个Cookie是会话级的Cookie,浏览器关闭时释放
  4. // 0:通知浏览器立即删除这个Cookie
  5. cookie.setMaxAge(20);

[3]会话和持久化Cookie对比

image.png

④Cookie的domain和path

上网时间长了,本地会保存很多Cookie。对浏览器来说,访问互联网资源时不能每次都把所有Cookie带上。浏览器会使用Cookie的domain和path属性值来和当前访问的地址进行比较,从而决定是否携带这个Cookie。
image.png

第三节 Session的工作机制

1、文字描述

前提:浏览器正常访问服务器

  • 服务器端没调用request.getSession()方法:什么都不会发生
  • 服务器端调用了request.getSession()方法
    • 服务器端检查当前请求中是否携带了JSESSIONID的Cookie
      • 有:根据JSESSIONID在服务器端查找对应的HttpSession对象
        • 能找到:将找到的HttpSession对象作为request.getSession()方法的返回值返回
        • 找不到:服务器端新建一个HttpSession对象作为request.getSession()方法的返回值返回
      • 无:服务器端新建一个HttpSession对象作为request.getSession()方法的返回值返回

        2、流程图描述

        image.png

        3、代码验证

        ```java // 1.调用request对象的方法尝试获取HttpSession对象 HttpSession session = request.getSession();

// 2.调用HttpSession对象的isNew()方法 boolean wetherNew = session.isNew();

// 3.打印HttpSession对象是否为新对象 System.out.println(“wetherNew = “ + (wetherNew?”HttpSession对象是新的”:”HttpSession对象是旧的”));

// 4.调用HttpSession对象的getId()方法 String id = session.getId();

// 5.打印JSESSIONID的值 System.out.println(“JSESSIONID = “ + id);

  1. <a name="NPaM0"></a>
  2. ### 4、时效性
  3. <a name="ns6Zl"></a>
  4. #### ①为什么Session要设置时限
  5. 用户量很大之后,Session对象相应的也要创建很多。如果一味创建不释放,那么服务器端的内存迟早要被耗尽。
  6. <a name="XWCDW"></a>
  7. #### ②设置时限的难点
  8. 从服务器端的角度,很难精确得知类似浏览器关闭的动作。而且即使浏览器一直没有关闭,也不代表用户仍然在使用。
  9. <a name="OOs0U"></a>
  10. #### ③服务器端给Session对象设置最大闲置时间
  11. - 默认值:1800秒
  12. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/26803611/1652789995187-d6e41178-c4b4-4b47-992b-e806400a857e.png#clientId=ud2720a23-2514-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u9a91141b&margin=%5Bobject%20Object%5D&name=image.png&originHeight=496&originWidth=788&originalType=url&ratio=1&rotation=0&showTitle=false&size=49797&status=done&style=none&taskId=uff0d47b8-35fc-45ea-ab39-c4a598a64af&title=)<br />最大闲置时间生效的机制如下:<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/26803611/1652789995154-96c50772-b1f0-494e-a3e6-9d77f06933bf.png#clientId=ud2720a23-2514-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u6b649bb7&margin=%5Bobject%20Object%5D&name=image.png&originHeight=362&originWidth=574&originalType=url&ratio=1&rotation=0&showTitle=false&size=21535&status=done&style=none&taskId=u64d4d71b-568c-43da-a5f8-3d485e1872d&title=)
  13. <a name="jluR6"></a>
  14. #### ④代码验证
  15. ```java
  16. // ※测试时效性
  17. // 获取默认的最大闲置时间
  18. int maxInactiveIntervalSecond = session.getMaxInactiveInterval();
  19. System.out.println("maxInactiveIntervalSecond = " + maxInactiveIntervalSecond);
  20. // 设置默认的最大闲置时间
  21. session.setMaxInactiveInterval(15);

⑤强制Session立即失效

  1. session.invalidate();

第十章 过滤器

第一节 过滤器简介

1、通过类比了解过滤器作用

①坐地铁

image.png

②登录检查

image.png

2、过滤器的三要素

①拦截

过滤器之所以能够对请求进行预处理,关键是对请求进行拦截,把请求拦截下来才能够做后续的操作。而且对于一个具体的过滤器,它必须明确它要拦截的请求,而不是所有请求都拦截。

②过滤

根据业务功能实际的需求,看看在把请求拦截到之后,需要做什么检查或什么操作,写对应的代码即可。

③放行

过滤器完成自己的任务或者是检测到当前请求符合过滤规则,那么可以将请求放行。所谓放行,就是让请求继续去访问它原本要访问的资源。

友情提示:将来学习SpringMVC时,会学习SpringMVC中的『拦截器』,同样具备三要素。

第二节 HelloWorld

1、思路

image.png

2、操作步骤

①准备工作

  • 创建module
  • 加入Thymeleaf环境
  • 完成首页访问功能
  • 创建Target01Servlet以及target01.html
  • 创建SpecialServlet以及special.html

    ②创建Filter

    [1]创建Target01Filter类
  • 要点1:实现javax.servlet.Filter接口

  • 要点2:在doFilter()方法中执行过滤
  • 要点3:如果满足过滤条件使用 chain.doFilter(request, response);放行
  • 要点4:如果不满足过滤条件转发或重定向请求

    • 附带问题:Thymeleaf模板渲染。这里我们选择的解决办法是跳转到一个Servlet,由Servlet负责执行模板渲染返回页面。

      1. public class Target01Filter implements Filter {
      2. @Override
      3. public void init(FilterConfig filterConfig) throws ServletException {
      4. }
      5. @Override
      6. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
      7. // 1.打印一句话表明Filter执行了
      8. System.out.println("过滤器执行:Target01Filter");
      9. // 2.检查是否满足过滤条件
      10. // 人为设定一个过滤条件:请求参数message是否等于monster
      11. // 等于:放行
      12. // 不等于:将请求跳转到另外一个页面
      13. // ①获取请求参数
      14. String message = request.getParameter("message");
      15. // ②检查请求参数是否等于monster
      16. if ("monster".equals(message)) {
      17. // ③执行放行
      18. // FilterChain对象代表过滤器链
      19. // chain.doFilter(request, response)方法效果:将请求放行到下一个Filter,
      20. // 如果当前Filter已经是最后一个Filter了,那么就将请求放行到原本要访问的目标资源
      21. chain.doFilter(request, response);
      22. }else{
      23. // ④跳转页面
      24. request.getRequestDispatcher("/SpecialServlet?method=toSpecialPage").forward(request, response);
      25. }
      26. }
      27. @Override
      28. public void destroy() {
      29. }
      30. }

      [2]配置Target01Filter类

      这一步也可以叫『注册』。

  1. <!-- 配置Target01Filter -->
  2. <filter>
  3. <!-- 配置Filter的友好名称 -->
  4. <filter-name>Target01Filter</filter-name>
  5. <!-- 配置Filter的全类名,便于Servlet容器创建Filter对象 -->
  6. <filter-class>com.atguigu.filter.filter.Target01Filter</filter-class>
  7. </filter>
  8. <!-- 配置Filter要拦截的目标资源 -->
  9. <filter-mapping>
  10. <!-- 指定这个mapping对应的Filter名称 -->
  11. <filter-name>Target01Filter</filter-name>
  12. <!-- 通过请求地址模式来设置要拦截的资源 -->
  13. <url-pattern>/Target01Servlet</url-pattern>
  14. </filter-mapping>

第三节 过滤器生命周期

1、回顾Servlet生命周期

Servlet生命周期(opens new window)

2、Filter生命周期

和Servlet生命周期类比,Filter生命周期的关键区别是:在Web应用启动时创建对象

生命周期阶段 执行时机 执行次数
创建对象 Web应用启动时 一次
初始化 创建对象后 一次
拦截请求 接收到匹配的请求 多次
销毁 Web应用卸载前 一次

第四节 过滤器匹配规则

本节要探讨的是在filter-mapping中如何将Filter同它要拦截的资源关联起来。

1、精确匹配

指定被拦截资源的完整路径:

  1. <!-- 配置Filter要拦截的目标资源 -->
  2. <filter-mapping>
  3. <!-- 指定这个mapping对应的Filter名称 -->
  4. <filter-name>Target01Filter</filter-name>
  5. <!-- 通过请求地址模式来设置要拦截的资源 -->
  6. <url-pattern>/Target01Servlet</url-pattern>
  7. </filter-mapping>

2、模糊匹配

相比较精确匹配,使用模糊匹配可以让我们创建一个Filter就能够覆盖很多目标资源,不必专门为每一个目标资源都创建Filter,提高开发效率。

①前杠后星

在我们配置了url-pattern为/user/*之后,请求地址只要是/user开头的那么就会被匹配。

  1. <filter-mapping>
  2. <filter-name>Target02Filter</filter-name>
  3. <!-- 模糊匹配:前杠后星 -->
  4. <!--
  5. /user/Target02Servlet
  6. /user/Target03Servlet
  7. /user/Target04Servlet
  8. -->
  9. <url-pattern>/user/*</url-pattern>
  10. </filter-mapping>

极端情况:/*匹配所有请求

②前星后缀

下面我们使用png图片来测试后缀拦截的效果,并不是只能拦截png扩展名。

[1]创建一组img标签
  1. <img th:src="@{/./images/img017.png}"/><br/>
  2. <img th:src="@{/./images/img018.png}"/><br/>
  3. <img th:src="@{/./images/img019.png}"/><br/>
  4. <img th:src="@{/./images/img020.png}"/><br/>
  5. <img th:src="@{/./images/img024.png}"/><br/>
  6. <img th:src="@{/./images/img025.png}"/><br/>

[2]创建Filter
  1. <filter>
  2. <filter-name>Target04Filter</filter-name>
  3. <filter-class>com.atguigu.filter.filter.Target04Filter</filter-class>
  4. </filter>
  5. <filter-mapping>
  6. <filter-name>Target04Filter</filter-name>
  7. <url-pattern>*.png</url-pattern>
  8. </filter-mapping>

③前杠后缀,星号在中间

配置方式如下:

  1. <url-pattern>/*.png</url-pattern>

按照这个配置启动Web应用时会抛出异常:

java.lang.IllegalArgumentException: Invalid /*.png in filter mapping

结论:这么配是不允许的!

3、匹配Servlet名称[了解]

  1. <filter-mapping>
  2. <filter-name>Target05Filter</filter-name>
  3. <!-- 根据Servlet名称匹配 -->
  4. <servlet-name>Target01Servlet</servlet-name>
  5. </filter-mapping>

第五节 过滤器链

1、概念

  • 多个Filter的拦截范围如果存在重合部分,那么这些Filter会形成Filter链
  • 浏览器请求重合部分对应的目标资源时,会依次经过Filter链中的每一个Filter。
  • Filter链中每一个Filter执行的顺序是由web.xml中filter-mapping配置的顺序决定的。

image.png

2、测试

①准备工作

创建超链接访问一个普通的Servlet即可。

②创建多个Filter拦截Servlet

  1. <filter-mapping>
  2. <filter-name>TargetChain03Filter</filter-name>
  3. <url-pattern>/Target05Servlet</url-pattern>
  4. </filter-mapping>
  5. <filter-mapping>
  6. <filter-name>TargetChain02Filter</filter-name>
  7. <url-pattern>/Target05Servlet</url-pattern>
  8. </filter-mapping>
  9. <filter-mapping>
  10. <filter-name>TargetChain01Filter</filter-name>
  11. <url-pattern>/Target05Servlet</url-pattern>
  12. </filter-mapping>

控制台打印效果:

过滤器执行:Target03Filter[模糊匹配 前杠后星 /*] 测试Filter链:TargetChain03Filter 测试Filter链:TargetChain02Filter 测试Filter链:TargetChain01Filter

第十一章 监听器

第一节 观察者模式

二十三种设计模式之一:
image.png

  • 观察者:监控『被观察者』的行为,一旦发现『被观察者』触发了事件,就会调用事先准备好的方法执行操作。
  • 被观察者:『被观察者』一旦触发了被监控的事件,就会被『观察者』发现。

    第二节 监听器简介

    1、概念

    监听器:专门用于对其他对象身上发生的事件或状态改变进行监听和相应处理的对象,当被监视的对象发生情况时,立即采取相应的行动。 Servlet监听器:Servlet规范中定义的一种特殊类,它用于监听Web应用程序中的ServletContext,HttpSession 和HttpServletRequest等域对象的创建与销毁事件,以及监听这些域对象中的属性发生修改的事件。

    2、分类

    image.png

  • 域对象监听器

  • 域对象的属性域监听器
  • Session域中数据的监听器

    3、监听器列表

    ①ServletContextListener

    作用:监听ServletContext对象的创建与销毁
方法名 作用
contextInitialized(ServletContextEvent sce) ServletContext创建时调用
contextDestroyed(ServletContextEvent sce) ServletContext销毁时调用

ServletContextEvent对象代表从ServletContext对象身上捕获到的事件,通过这个事件对象我们可以获取到ServletContext对象。

②HttpSessionListener

作用:监听HttpSession对象的创建与销毁

方法名 作用
sessionCreated(HttpSessionEvent hse) HttpSession对象创建时调用
sessionDestroyed(HttpSessionEvent hse) HttpSession对象销毁时调用

HttpSessionEvent对象代表从HttpSession对象身上捕获到的事件,通过这个事件对象我们可以获取到触发事件的HttpSession对象。

③ServletRequestListener

作用:监听ServletRequest对象的创建与销毁

方法名 作用
requestInitialized(ServletRequestEvent sre) ServletRequest对象创建时调用
requestDestroyed(ServletRequestEvent sre) ServletRequest对象销毁时调用

ServletRequestEvent对象代表从HttpServletRequest对象身上捕获到的事件,通过这个事件对象我们可以获取到触发事件的HttpServletRequest对象。另外还有一个方法可以获取到当前Web应用的ServletContext对象。

④ServletContextAttributeListener

作用:监听ServletContext中属性的创建、修改和销毁

方法名 作用
attributeAdded(ServletContextAttributeEvent scab) 向ServletContext中添加属性时调用
attributeRemoved(ServletContextAttributeEvent scab) 从ServletContext中移除属性时调用
attributeReplaced(ServletContextAttributeEvent scab) 当ServletContext中的属性被修改时调用

ServletContextAttributeEvent对象代表属性变化事件,它包含的方法如下:

方法名 作用
getName() 获取修改或添加的属性名
getValue() 获取被修改或添加的属性值
getServletContext() 获取ServletContext对象

⑤HttpSessionAttributeListener

作用:监听HttpSession中属性的创建、修改和销毁

方法名 作用
attributeAdded(HttpSessionBindingEvent se) 向HttpSession中添加属性时调用
attributeRemoved(HttpSessionBindingEvent se) 从HttpSession中移除属性时调用
attributeReplaced(HttpSessionBindingEvent se) 当HttpSession中的属性被修改时调用

HttpSessionBindingEvent对象代表属性变化事件,它包含的方法如下:

方法名 作用
getName() 获取修改或添加的属性名
getValue() 获取被修改或添加的属性值
getSession() 获取触发事件的HttpSession对象

⑥ServletRequestAttributeListener

作用:监听ServletRequest中属性的创建、修改和销毁

方法名 作用
attributeAdded(ServletRequestAttributeEvent srae) 向ServletRequest中添加属性时调用
attributeRemoved(ServletRequestAttributeEvent srae) 从ServletRequest中移除属性时调用
attributeReplaced(ServletRequestAttributeEvent srae) 当ServletRequest中的属性被修改时调用

ServletRequestAttributeEvent对象代表属性变化事件,它包含的方法如下:

方法名 作用
getName() 获取修改或添加的属性名
getValue() 获取被修改或添加的属性值
getServletRequest () 获取触发事件的ServletRequest对象

⑦HttpSessionBindingListener

作用:监听某个对象在Session域中的创建与移除

方法名 作用
valueBound(HttpSessionBindingEvent event) 该类的实例被放到Session域中时调用
valueUnbound(HttpSessionBindingEvent event) 该类的实例从Session中移除时调用

HttpSessionBindingEvent对象代表属性变化事件,它包含的方法如下:

方法名 作用
getName() 获取当前事件涉及的属性名
getValue() 获取当前事件涉及的属性值
getSession() 获取触发事件的HttpSession对象

⑧HttpSessionActivationListener

作用:监听某个对象在Session中的序列化与反序列化。

方法名 作用
sessionWillPassivate(HttpSessionEvent se) 该类实例和Session一起钝化到硬盘时调用
sessionDidActivate(HttpSessionEvent se) 该类实例和Session一起活化到内存时调用

HttpSessionEvent对象代表事件对象,通过getSession()方法获取事件涉及的HttpSession对象。

第三节 ServletContextListener

1、实用性

将来学习SpringMVC的时候,会用到一个ContextLoaderListener,这个监听器就实现了ServletContextListener接口,表示对ServletContext对象本身的生命周期进行监控。

2、具体用法

①创建监听器类

  1. public class AtguiguListener implements ServletContextListener {
  2. @Override
  3. public void contextInitialized(
  4. // Event对象代表本次事件,通过这个对象可以获取ServletContext对象本身
  5. ServletContextEvent sce) {
  6. System.out.println("Hello,我是ServletContext,我出生了!");
  7. ServletContext servletContext = sce.getServletContext();
  8. System.out.println("servletContext = " + servletContext);
  9. }
  10. @Override
  11. public void contextDestroyed(ServletContextEvent sce) {
  12. System.out.println("Hello,我是ServletContext,我打算去休息一会儿!");
  13. }
  14. }

②注册监听器

  1. <!-- 每一个listener标签对应一个监听器配置,若有多个监听器,则配置多个listener标签即可 -->
  2. <listener>
  3. <!-- 配置监听器指定全类名即可 -->
  4. <listener-class>com.atguigu.listener.AtguiguListener</listener-class>
  5. </listener>

事件触发过程中控制台日志的打印:

Connected to server [2021-03-20 04:23:20,982] Artifact pro10-listener:war exploded: Artifact is being deployed, please wait… 三月 20, 2021 4:23:21 下午 org.apache.catalina.deploy.WebXml setVersion 警告: Unknown version string [4.0]. Default version will be used. Hello,我是ServletContext,我出生了! servletContext = org.apache.catalina.core.ApplicationContextFacade@6a66017e [2021-03-20 04:23:21,426] Artifact pro10-listener:war exploded: Artifact is deployed successfully [2021-03-20 04:23:21,426] Artifact pro10-listener:war exploded: Deploy took 444 milliseconds 三月 20, 2021 4:23:30 下午 org.apache.catalina.startup.HostConfig deployDirectory 信息: Deploying web application directory D:\software\apache-tomcat-7.0.57\webapps\manager 三月 20, 2021 4:23:31 下午 org.apache.catalina.startup.HostConfig deployDirectory 信息: Deployment of web application directory D:\software\apache-tomcat-7.0.57\webapps\manager has finished in 124 ms [2021-03-20 04:24:06,422] Artifact pro10-listener:war exploded: Artifact is being deployed, please wait… Hello,我是ServletContext,我打算去休息一会儿! Hello,我是ServletContext,我出生了! servletContext = org.apache.catalina.core.ApplicationContextFacade@2a55374c [2021-03-20 04:24:07,115] Artifact pro10-listener:war exploded: Artifact is deployed successfully [2021-03-20 04:24:07,115] Artifact pro10-listener:war exploded: Deploy took 694 milliseconds

第十二章 Axios Ajax

第一节 Ajax概述

1、服务器端渲染

image.png

2、Ajax渲染(局部更新)

image.png

3、前后端分离

彻底舍弃服务器端渲染,数据全部通过Ajax方式以JSON格式来传递。

4、同步与异步

Ajax本身就是Asynchronous JavaScript And XML的缩写,直译为:异步的JavaScript和XML。在实际应用中Ajax指的是:不刷新浏览器窗口不做页面跳转局部更新页面内容的技术。
『同步』『异步』是一对相对的概念,那么什么是同步,什么是异步呢?

①同步

多个操作按顺序执行,前面的操作没有完成,后面的操作就必须等待。所以同步操作通常是串行的。
image.png

②异步

多个操作相继开始并发执行,即使开始的先后顺序不同,但是由于它们各自是在自己独立的进程或线程中完成,所以互不干扰谁也不用等谁
image.png

5、Axios简介

使用原生的JavaScript程序执行Ajax极其繁琐,所以一定要使用框架来完成。而Axios就是目前最流行的前端Ajax框架。
Axios官网(opens new window)
image.png
使用Axios和使用Vue一样,导入对应的*.js文件即可。官方提供的script标签引入方式为:

  1. <script src="https://unpkg.com/axios/dist/axios.min.js"></script>

我们可以把这个axios.min.js文件下载下来保存到本地来使用。

第二节 Axios基本用法

0、在前端页面引入开发环境

  1. <script type="text/javascript" src="/demo/static/vue.js"></script>
  2. <script type="text/javascript" src="/demo/static/axios.min.js"></script>

1、发送普通请求参数

①前端代码

HTML标签:

  1. <div id="app">
  2. <button @click="commonParam">普通请求参数</button>
  3. </div>

Vue+axios代码:

  1. new Vue({
  2. "el":"#app",
  3. "data":{},
  4. "methods":{
  5. "commonParam":function () {
  6. axios({
  7. "method":"post",
  8. "url":"/demo/AjaxServlet?method=commonParam",
  9. "params":{
  10. "userName":"tom",
  11. "userPwd":"123456"
  12. }
  13. }).then(function (response) {
  14. console.log(response);
  15. }).catch(function (error) {
  16. console.log(error);
  17. });
  18. }
  19. }
  20. });

效果:所有请求参数都被放到URL地址后面了,哪怕我们现在用的是POST请求方式。
image.png

②后端代码

  1. public class AjaxServlet extends ModelBaseServlet {
  2. protected void commonParam(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  3. String userName = request.getParameter("userName");
  4. String userPwd = request.getParameter("userPwd");
  5. System.out.println("userName = " + userName);
  6. System.out.println("userPwd = " + userPwd);
  7. response.setContentType("text/html;charset=UTF-8");
  8. response.getWriter().write("服务器端返回普通文本字符串作为响应");
  9. }
  10. }

P.S.:由于我们不需要Thymeleaf了,所以ModelBaseServlet可以跳过ViewBaseServlet直接继承HttpServlet。

image.png

③axios程序接收到的响应对象结构

image.png

属性名 作用
config 调用axios(config对象)方法时传入的JSON对象
data 服务器端返回的响应体数据
headers 响应消息头
request 原生JavaScript执行Ajax操作时使用的XMLHttpRequest
status 响应状态码
statusText 响应状态码的说明文本

④服务器端处理请求失败后

  1. catch(function (error) { // catch()服务器端处理请求出错后,会调用
  2. console.log(error); // error就是出错时服务器端返回的响应数据
  3. console.log(error.response); // 在服务器端处理请求失败后,获取axios封装的JSON格式的响应数据对象
  4. console.log(error.response.status); // 在服务器端处理请求失败后,获取响应状态码
  5. console.log(error.response.statusText); // 在服务器端处理请求失败后,获取响应状态说明文本
  6. console.log(error.response.data); // 在服务器端处理请求失败后,获取响应体数据
  7. });

在给catch()函数传入的回调函数中,error对象封装了服务器端处理请求失败后相应的错误信息。其中,axios封装的响应数据对象,是error对象的response属性。response属性对象的结构如下图所示:
image.png
可以看到,response对象的结构还是和then()函数传入的回调函数中的response是一样的:
image.png

回调函数:开发人员声明,但是调用时交给系统来调用。像单击响应函数、then()、catch()里面传入的都是回调函数。回调函数是相对于普通函数来说的,普通函数就是开发人员自己声明,自己调用:

  1. function sum(a, b) {
  2. return a+b;
  3. }
  4. var result = sum(3, 2);
  5. console.log("result="+result);

2、发送请求体JSON

①前端代码

HTML代码:

  1. <button @click="requestBodyJSON">请求体JSON</button>

Vue+axios代码:

  1. ……
  2. "methods":{
  3. "requestBodyJSON":function () {
  4. axios({
  5. "method":"post",
  6. "url":"/demo/AjaxServlet?method=requestBodyJSON",
  7. "data":{
  8. "stuId": 55,
  9. "stuName": "tom",
  10. "subjectList": [
  11. {
  12. "subjectName": "java",
  13. "subjectScore": 50.55
  14. },
  15. {
  16. "subjectName": "php",
  17. "subjectScore": 30.26
  18. }
  19. ],
  20. "teacherMap": {
  21. "one": {
  22. "teacherName":"tom",
  23. "tearcherAge":23
  24. },
  25. "two": {
  26. "teacherName":"jerry",
  27. "tearcherAge":31
  28. },
  29. },
  30. "school": {
  31. "schoolId": 23,
  32. "schoolName": "atguigu"
  33. }
  34. }
  35. }).then(function (response) {
  36. console.log(response);
  37. }).catch(function (error) {
  38. console.log(error);
  39. });
  40. }
  41. }
  42. ……

效果:
image.png
P.S.:Chrome浏览器中将『请求负载』显示为英文:『Request Payload』。

②后端代码

[1]加入Gson包

Gson是Google研发的一款非常优秀的JSON数据解析和生成工具,它可以帮助我们将数据在JSON字符串和Java对象之间互相转换。
image.png

[2]Servlet代码
  1. protected void requestBodyJSON(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  2. // 1.由于请求体数据有可能很大,所以Servlet标准在设计API的时候要求我们通过输入流来读取
  3. BufferedReader reader = request.getReader();
  4. // 2.创建StringBuilder对象来累加存储从请求体中读取到的每一行
  5. StringBuilder builder = new StringBuilder();
  6. // 3.声明临时变量
  7. String bufferStr = null;
  8. // 4.循环读取
  9. while((bufferStr = reader.readLine()) != null) {
  10. builder.append(bufferStr);
  11. }
  12. // 5.关闭流
  13. reader.close();
  14. // 6.累加的结果就是整个请求体
  15. String requestBody = builder.toString();
  16. // 7.创建Gson对象用于解析JSON字符串
  17. Gson gson = new Gson();
  18. // 8.将JSON字符串还原为Java对象
  19. Student student = gson.fromJson(requestBody, Student.class);
  20. System.out.println("student = " + student);
  21. System.out.println("requestBody = " + requestBody);
  22. response.setContentType("text/html;charset=UTF-8");
  23. response.getWriter().write("服务器端返回普通文本字符串作为响应");
  24. }

P.S.:看着很麻烦是吧?别担心,将来我们有了SpringMVC之后,一个@RequestBody注解就能够搞定,非常方便!

3、服务器端返回JSON数据

①前端代码

  1. axios({
  2. "method":"post",
  3. "url":"/demo/AjaxServlet?method=responseBodyJSON"
  4. }).then(function (response) {
  5. console.log(response);
  6. }).catch(function (error) {
  7. console.log(error);
  8. });

then()中获取到的response在控制台打印效果如下:我们需要通过data属性获取响应体数据
image.png

②后端代码

[1]加入Gson包

仍然需要Gson支持,不用多说
image.png

[2]Servlet代码
  1. protected void responseBodyJSON(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  2. // 1.准备数据对象
  3. Student student = new Student();
  4. student.setStuId(10);
  5. student.setStuName("tom");
  6. student.setSchool(new School(11,"atguigu"));
  7. student.setSubjectList(Arrays.asList(new Subject("java", 95.5), new Subject("php", 93.3)));
  8. Map<String, Teacher> teacherMap = new HashMap<>();
  9. teacherMap.put("t1", new Teacher("lili", 25));
  10. teacherMap.put("t2", new Teacher("mary", 26));
  11. teacherMap.put("t3", new Teacher("katty", 27));
  12. student.setTeacherMap(teacherMap);
  13. // 2.创建Gson对象
  14. Gson gson = new Gson();
  15. // 3.将Java对象转换为JSON对象
  16. String json = gson.toJson(student);
  17. // 4.设置响应体的内容类型
  18. response.setContentType("application/json;charset=UTF-8");
  19. response.getWriter().write(json);
  20. }

总体技术体系梳理

image.png