# JavaWeb
**Repository Path**: lyxbiu/java-web
## Basic Information
- **Project Name**: JavaWeb
- **Description**: 老杜JavaWeb入门到精通
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 0
- **Created**: 2024-12-30
- **Last Updated**: 2025-06-17
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 需要提前准备的技术
* JavaSE(Java语言的标准版,Java提供的最基本的类库)
* Java环境搭建
* java的基础语法
* Java的面向对象
* 数组
* 常用类
* 异常
* 集合
* 多线程
* IO流
* 反射机制
* ......
- MySQL(数据库)
- 最基本的要求是:能够编写增删改查等简单的SQL语句
* JDBC(Java语言连接数据库)
* 这是一套Java语言连接数据库接口
* WEB前端
* HTML(网页)
* CSS(层叠样式语言,修饰HTML)
* javascript(一种脚本语言,运行在浏览器当中,让浏览器可以增删改,让网页产生更强的交互式效果)
* WEB后端
* Servlet(Server Applet:服务端的java小程序)
* JSP
* AJAX
* JQuery
* MyBatis
* Spring
* SpringMVC
* SpringBoot
* SpringCloud
* ......
# Servlet
## 关于系统架构
1. 系统架构包括什么形式
* C/S架构
* B/S架构
2. C/S架构
* Clinet/Server(客户端/服务器)
* C/S架构的软件或者说系统有哪些?
- QQ
* C/S架构的特点:必须安装特定的客户端软件
* C/S架构的优点和缺点是什么?
- 优点:
- 速度快(软件中的数据大部分都是集成到客户端软件当中,很少的数据从服务端传送过来)
- 体验好
- 界面酷炫
- 服务器压力小
- 安全(大量的数据集成在客户端软件当中,并且客户端有很多个,服务器只有一个,就算服务器受损,问题也不大,数据在客户端上有缓存)
- ......
- 缺点:
- 升级维护比较差劲。(升级维护比较麻烦,每一个客户端都需要升级,有一些软件不是那么安装)
3. B/S架构
- Browser/Server(浏览器/服务器)
- https://www.baidu.com
- https://www.jd.com
- B/S结构的系统是一个特殊的C/S系统
- 实际上B/S结构的系统还是一个C/S系统,只不过这个C比较特殊,这个Client是一个固定不变的浏览器
- B/S结构系统的优缺点
- 优点:
- 升级维护方便,成本低
- 不需要安装特定的客户端软件,用户操作方便
- 缺点:
- 速度慢(不是因为带宽低,是因为所有的数据都在都在服务器上,用户发送的每一个请求都是需要服务端响应数据)
- 体验差(浏览器只支持三个语言:HTML、CSS、JavaScript)
- 不安全(所有数据都在服务器)
- ......
4. C/S和B/S结构的系统哪个好?
- 不同结构的系统在不同的业务下有不同的适应场景
- 娱乐性软件:C/S
- 公司内部使用的业务软件:B/S
5. 开发B/S结构的系统,实际上就是开发网站,其实就是开发一个Web
- 开发一个Web系统需要掌握哪些技术?
- Web前端
- HTML
- CSS
- JavaScript
- Web后端
- Java(Java做Web开发我们称为JavaWEB开发,JavaWEB最核心的规范:Servlet【Server Applet:服务器的Java小程序】)
- c
- python
- ......
6. JavaEE是什么?
- Java包括三大块
- JavaSE
- Java标准版(一套类库,别人写好的一套类库,只不过这个类库是标准类库,走EE,或者走ME,这个SE一定是基础,先学)
- JavaEE
- Java企业版(一套类库,别人写好的一套类库,只不过这个类库可以帮助我们完成企业级项目的开发,专门为企业内部提供解决方案的一套(多套)类库)
- 别人写好的,可以用来开发企业级项目
- 可以做WEB开发
- Java比较火爆的原因就是这个JavaEE方向
- JavaME
- Java微型版(一套类库,只不过这个类库帮助我们进行电子卫星设备内核程序的开发)
- 机顶盒内核程序、吸尘器内核程序、电冰箱内核程序......
- JavaEE实际上包括很多规范,13种规范,其中Servlet即是JavaEE规范之一。学Servlet其实还是学Java语言
## B/S通信原理
- WEB系统的访问过程
- 第一步:打开浏览器
- 第二步:找到地址栏
- 第三步:输入一个网址
- 第四步:回车
- 第五步:在浏览器上会显示响应的结果
- 关于域名
- https://www.baidu.com
- www.baidu.com是一个域名
- 在浏览器上输入域名,回车后,域名解释器会将域名解释出来一个具体的IP地址和端口号等
- IP地址是什么?
- 计算机在网络当中的一个身份证,在同一个网络当中,IP地址是唯一的
- A计算机要想和计算机B通信,首先你需要知道计算机B的IP地址,有了IP地址才能建立连接
- 端口号是什么?
- 一个端口代表一个软件(一个端口代表一个应用,一个应用仅代表一个服务)
- 每一个软件启动之后都有一个端口号
- 在同一台计算机上,端口具有唯一性
- 一个WEB系统的通信原理?
- 第一步:用户输入网址(URL)
- 第二部:域名解析器进行域名解析:http://110.242.68.3:80//index.html
- 第三步:浏览器软件在网络中搜索110.242.68.3这台主机,找到这台主机
- 第四步:定位110.242.68.3这台主机上的服务器软件,因为是80端口,可以很轻松的定位到80端口对应的服务器软件
- 第五步:80端口对应的服务器软件得知浏览器想要的资源名是:index.html
- 第六步:服务器软件找到index.html文件,并将index.html文件的内容直接输出响应到浏览器上
- 第七步:浏览器接收到来自服务器的代码(HTML、CSS、JavaScript)
- 第八步:浏览器渲染,执行HTML、CSS、JS代码、展示效果
- 什么是URL?
- 统一资源定位符(http://www.baidu.com)
- 什么是请求?什么是响应?
- 请求和响应实际上说的是数据的流向不同
- 从Browser端发送数据到Server端,我们称为请求。英文名:request
- 从Server端向浏览器Browser端发送数据,我们称为响应。英文名:response
- B->S(请求request)
- S->B(响应response)
## 关于WEB服务器软件
- WEB服务器软件有哪些?
- Tomcat(WEB服务器)
- jetty(WEB服务器)
- JBOSS(应用服务器)
- WebLogic(应用服务器)
- WebSphere(应用服务器)
- 应用服务器和WEB服务器的关系?
- 应用服务器实现了JavaEE的所有规范(JavaEE有13个不同的规范)
- WEB服务器只实现了JavaEE的Servlet+JSP两个核心
- JBOSS服务器内嵌了Tomcat服务器
- 下载Tomcat
- apache官网:https://www.apache.org/
- tomcat官网:https://tomcat.apache.org/
- tocmat免费开源的轻量级WEB服务器
- tomcat还有一个名称: catalina( catalina是美国一个岛屿,据说作者是在这个小岛上开发了一个轻量级的WEB服务器、体积小、运行速度快、因此tomcat又称为catalina)
- tomcat是Java语言写的
- tomcat要想运行,必须先有jre(Java运行环境)
- 配置Java运行环境
- JAVA_HOME:jdk根目录 D:\Program Files\Java\jdk-16.0.1
- PATH:%JAVA_HOME%\bin
- Tomcat的安装
- 绿色版本的安装非常简单,直接zip解压即可,解压就是安装
- 启动Tomcat
- bin目录下有一个文件:startup.bat,通过它可以启动Tomcat
- xxx.bat是个什么文件?bat文件是windows操作系统专用的。bat文件是批处理文件,这种文件可以编写大量的windows的dos命令,然后执行bat文件就相当于批量的执行dos命令
- startup.sh,这个文件在windows当中无法执行,在Linux环境当中可以使用,在Linux环境下可以执行的是shell命令,大量的shell命令编写在shell文件当中,然后执行shell命令就可以批量的执行shell命令
- tomcat服务器提供了bat和sh文件,说明了tomcat服务器的通用性
- 分析startup.bat文件得出,执行这个命令,实际上最后是执行:catalina.bat文件
- catalina文件中有这杨一行配置:MAINCLASS=org.apache.catalina.startup.Bootstrap(这个类就是main方法所在的类)
- tomcat服务器就是用Java语言写的,既然是java语言写的,那么启动tomcat就是指定main方法
- 我们尝试启动tomcat,dos命令窗口中输入startup.bat来启动tomcat服务器
- 启动Tocmat服务器只配置path对应的bin目录是不行的(启动bin中的.bat)。有两个环境变量需要配置:
- JAVA_HOME:jdk的根目录
- CATALINA_HOME:Tomcat的根目录
- 关于Tomcat的目录
- bin:Tomcat服务器的命令文件存在的目录,比如:启动Tomcat、关闭Tomcat等
- conf:Tomcat配置文件存放的的目录。(server.xml文件或者可以配置端口号,默认端口号是8080)
- lib:Tomcat核心程序目录,因为Tomcat服务器是Java写的,这里的jar包里面都是class文件
- temp:Tomcat服务器的临时目录,存放临时文件
- webapps:存放大量的webapp(web application:web应用)
- work:存放JSP文件翻译之后的Java文件以及翻译之后的class文件
- 配置Tomcat环境变量需要哪些变量
- JAVA_HOME:jdk的根目录
- CATALINA_HOME::Tomcat的根目录
- PATH:%CATALINA_HOME%\bin;%JAVA_HOME%\bin
- 启动Tomcat:startup.bat
- 关闭Tomcat:shutdown.bat(shutdown文件可以重命名,shutdown和window关机命令同名)
- 怎么测试Tomcat服务器有没有启动成功?
- 打开浏览器,在浏览器上输入URL即可
- http://ip地址:端口号
- ip地址是什么
- 本机的ip地址是:127.0.0.1,或者是localhost
- 端口号
- Tomcat默认端口号:8080
## 实现一个最基本的web应用
- 第一步:找到CATALINA_HOME\webapps目录
- 因为所有的webapp要放在webapps目录下。(没有为什么,这是Tomcat服务器要求的。如果不放在这里,Tomcat服务器找不到你的应用)
- 第二步:在CATALINA_HOME\webapps目录下新建一个子目录,起名:xxx
- 这个目录xxx就是你这个webapp的名字
- 第三步:在xxx目录下新建资源文件,例如:index.html
- 编写index.html文件的内容
- 第四步:启动tomcat服务器
- 第五步:打开浏览器,在浏览器地址栏输入URL
- URL:http://localhost:8080/xxx/index.html
- 我们在浏览器输入地址和我们点超链接效果一致吗?:
- 点击超链接相当于直接在浏览器地址栏数输入URL,既然效果一样,我们可以直接使用超链接
```html
login
d page
```
## B/S结构系统的角色和协议

- 有哪些角色(在整个BS结构的系统当中,有哪些人参与进去了)
- 浏览器软件的开发团队(谷歌浏览器、火狐浏览器等)
- WEB Server的开发团队(Tocmat、Jertty,WebLogic等)
- DB Server的开发团队(Oracle、MySQL等)
- webapp的开发团队(WEB应用是我们JavaWEB程序员开发的)
- 角色和角色之间需要遵守哪些规范,哪些协议
- webapp的开发团队 和 WEB Server的开发团队 之间有一套规范:JavaEE规范之一Servlet规范
- Servlet规范的作用是什么?
- WEB Server 和 webapp解耦合
- Browser 和 Web Server 之间有一套传输协议:HTTP协议(超文本传输协议)
- webapp开发团队 和 DB Server的开发团队之间有一套规范:JDBC规范

## 模拟Servlet本质
- 充当SUN公司,指定Servlet规范
- javax.servlet.Servlet接口
- 充当Tomcat服务器的开发者
- Tomcat
- 充当Webapp的开发者
- BankServlet implements Servlet
- UserListServlet implements Servlet
- UserLoginServlet implements Servlet
- 通过我们分析:
- 对于我们Javaweb程序员来说,我们只要做两件事:
- 编写一个类实现Servlet接口
- 将编写的类写到配置文件当中,在配置文件中:指定请求路径 和 类名的关系
- 注意
- 这个配置文件的文件名和文件路径都是SUN公司制定好的,不能随便起和随便放
- 严格意义上来说Servlet其实并不是一个简单的接口
- Servlet规范中规定
- 一个合格的webapp应该是一个怎样的目录结构
- 一个合格的webapp应该有一个怎样的配置文件
- 一个合格的webapp配置文件路径应该放在哪里
- 一个合格的webapp中java程序放在哪里
- 这些都是Servlet规范中规定好的
- Tomcat服务器要遵循Servlet规范,Javaweb程序员也要遵循这个Servlet规范,这样Tomcat服务器和webapp才能解耦合。遵循Servlet规范的webapp,这个webapp就可以放在不同的WEB服务器中运行
## 开发一个带有Servlet的webapp
- 开发步骤是怎么样的
- 第一步:在webapps下新建一个目录,起名crm(这个crm就是webapp的名字)
- 注意:crm就是这个webapp的根
- 第二步:在webapp的根下新建一个目录:WEB-INF
- 注意:这个目录的名字是Servlet规范中规定的。必须全部大写
- 第三步:在WEB-INF目录下新建一个目录:classes
- 注意:这个目录的名字是Servlet规范中规定的,必须全部小写。另外这个目录下一定存放的是java程序编译之后的classes文件(这个存放的是字节码文件)
- 第四步:在WEB-INF目录下新建一个目录:lib
- 注意:这个目录不是必须的。但是如果一个webapp需要第三方jar包的话,这个jar包要放在lib目录下,这个目录的名字是Servlet规范中规定的,必须全部小写。这也是Servlet规范的
- 第五步:在WEB-INF目录下新建一个文件:web.xml
- 注意:这个文件是必须的,这个文件是Servlet规范中规定的。一个合法webapp,web.xml文件是必须的,这个web.xml文件是一个配置文件,这个配置文件中描述了请求路径和Servlet类之间的对照关系
- 这个文件最好从其他webapp文件中拷贝,最好别手写,没必要
- ```xml
```
- 第六步:编写一个java小程序,这个Java小程序不能随便写,这个Java小程序必须实现Servlet接口
- 这个Servlet接口不在JDK当中
- Servlet接口是Oracle提供的(最原始是SUN公司提供的)
- Servlet接口是JavaEE的规范中的一员
- Tomcat服务器实现了Servlet规范,所以Tomcat服务器也需要Servlet接口。Tomcat服务器当中有这个接口。Tomcat服务器的CATALINA_HOME\lib下有一个servlet-api.jar,解压之后会发现里面有一个Servlet.class文件
- 重点:从javaEE9开始Servlet接口全名就变了:jakarta.servlet.Servlet
- 注意:编写java小程序的时候,Java源代码放在哪里都随意,只需将编译之后的class文件放入classes目录下即可
- 第七步:编写我们的HelloServlet
- 重点:你怎么让你写的HelloWord编译通过呢?配置环境变量CLASSPATH
- CLASSPATH:“,;D:\apache-tomcat-10.0.23\apache-tomcat-10.0.23\lib\servlet-api.jar”
- 思考一个问题,以上配置CLASSPATH和Tomcat服务器运行有没有关系?
- 没有任何关系,以上配置这个变量只是为了让你的HelloWord能够正常编译生成class文件
- 第八步:将编译生成的HelloServlet.class文件拷贝到WEB-INF\classes目录下
- 第九步:在web.xml文件中编写配置信息,让“请求路径”和“Servlet类名”关联在一起
- 这一步用专业术语描述:在web.xml文件中注册Servlet类
- ```xml
fsdsjsjsjsj
com.bjpowernode.servlet.HelloServlet
fsdsjsjsjsj
/fdsa/fd/saf/d/sa/fd/sa/fd
```
- 第十步:启动Tomcat服务器
- 第十一步:打开浏览器,在浏览器地址栏输入url,这个url必须是:
- http://localhost:8888/crm/fdsa/fd/saf/d/sa/fd/sa/fd
- 重点:浏览器上的请求路径不能随便写,必须和web.xml文件中的url-pattern一致
- 浏览器上的请求路径和web.xml文件中的url-pattern的唯一区别就是:浏览器上的请求路径带项目名:/crm
- 浏览器上编写的路径太复杂,可以使用超链接(**非常重要:html文件只能放在WEB-INF目录外面**)
- 以后我们不需要编写main方法,tomcat服务器负责调用main方法,Tomcat服务器启动的时候执行的就是main方法。我们Javaweb程序员只需要编写servlet接口的实现类,然后将其注册到web.xml文件中即可
- 总结一下一个合法的webapp目录结构应该是怎么样的?
```
webapp-root
|------WEB-INF
|------classes
|------lib
|------web.xml
|------html
|------css
|------js
|------image
...
```
- 浏览器发送请求,最终服务器调用Servlet中的方法,这是怎样的过程?
- 用户输入url,或者点击连接:http://localhost:8888/crm/fdsa/fd/saf/d/sa/fd/sa/fd
- 然后Tomcat服务器接收到请求,截取路径:/crm/fdsa/fd/saf/d/sa/fd/sa/fd
- Tomcat服务器找到crm项目
- Tomcat服务器在web.xml文件中查找/fdsa/fd/saf/d/sa/fd/sa/fd对应的servlet是:com.bjpowernode.servlet.HelloServlet
- Tomcat服务器通过反射机制,创建:com.bjpowernode.servlet.HelloServlet的对象
- Tomcat服务器调用com.bjpowernode.servlet.HelloServlet对象的service方法
## 关于JavaEE版本
- JavaEE被Oracle捐献了,Oracle将JavaEE规范捐献给了Apache
- Apache将JavaEE换名了,以后叫jakartaEE
- JavaEE8版本升级之后的JavaEE9,不再是JavaEE9,叫做jakarta9
- JavaEE8的时候对应的Servlet类名是:javax.servlet.Servlet
- jakartaEE9对应的Servlet类名是:jakarta.servlet.Servlet(包名换了)
- 如果你之前的项目还是使用javax.servlet.Servlet,那么你的项目无法直接部署到Tomcat10+版本上。你只能部署到Tomcat9-版本上,在Tomcat9以及Tomcat9之前的版本中还是能够识别javax.servlet这个包
## 解决Tomcat服务器启动时DOS命令窗口乱码问题(控制台乱码)
- 将CATALINA_HOME/conf/logging.properties文件中内容修改如下:
- java.util.logging.ConsoleHandler.encoding = GBK
## 向浏览器中响应一段HTML代码
```java
public void service(ServletRequest request,ServletResponse response)throws ServletException,IOException{
response.setContentType("text/html");
PrintWriter out=response.getWriter();
out.print("
Servlet
");
}
```
## 在servlet中连接数据库
- servlet是用Java代码写的,所以在servlet中完全可以直接编写JDBC代码连接数据库
- 在一个webapp中去连接数据库,需要将jar包放在WEB-INF/lib目录下
## 使用集成开发环境开发Servlet程序
- 集成开发工具很多,目前使用最多的是:
- Intellij IDEA(这个最多,IDEA在提示功能方面强于Eclipse,JetBrain公司开发的,收费)
- Eclipse(这个少一些),Eclipse是IBM公司开发的,Eclipse寓意”日食“,表示将太阳吃掉,太阳是SUN公司。IBM团队开发Eclipse寓意是吞并SUN公司,但是2009年SUN公司被Oracle公司并购了,IBM没有成功并购SUN公司。
- 使用IDEA集成开发Servlet
- 第一步:New Project(可以先创建一个Empty Project【空工程】,然后在空工程下新建Module【模块】,这个不是必须,也可以直接建立非空的Project)
- 第二步:新建模块【File->new->Module】
- 这里新建的是一个普通的JavaSE模块(这里不要新建Java Enterprise模块)
- 这个Module自动会放在javaWeb的Project下面
- 这个Module起名:servlet01
- 第三步:让Module变成JavaEE的模块
- 在Module右键:Add framework Support...(添加支持框架)
- 弹出的窗口中选择:Web Application (选择的是webapp的支持)
- 选择了webapp支持后,IDEA会自动给你生成一个符合Servlet规范的webapp目录结构
- **重点:需要注意的是,在IDEA工具根据Web Application模板生成的目录中有一个web目录,这个目录就代表webapp的根**
- 第四步:(非必须):根据Web Application生成的资源中有index.jsp,这里我选择删除这个文件
- 第五步:编写Servlet(StudentServlet)
- Class StudentServlet implements Servlet
- 这个时候发现Servlet.class文件没有,怎么办?将CATALINA_HOME/lib/servlet-api.jar和jsp-api.jar添加到classpath当中(这里的classpath说的是IDEA的classpath)
- File->Project Structrue->Modules->Dependecies->+加号->Add jars...
- 实现jakarta.servlet.Servlet中五个方法
- 第六步:在Servlet当中的service方法中编写业务逻辑代码(这里我们连接数据库)
- 第七步:在WEB-INF目录下新建一个子目录:lib(这个目录名不可以随便写,必须全部小写),并且将数据库驱动jar包放到lib目录下
- 第八步:在web.xml文件中完成StudentServlet类的注册(请求路径和servlet对应起来)
```xml
studentServlet
com.bjpowernode.javaWeb.servlet.StudentServlet
studentServlet
/servlet/student
```
- 第九步:给一个html页面,在HTML页面中编写一个超链接,用户点击这个超链接,发送请求,Tomcat执行后台的StudentServlet
- student.html
- 这个文件不能放到WEB-INF目录里面,只能放到WEB-INF外面
- student.html文件内容
```html
student page
student list
```
- 第十步:让IDEA工具关联Tomcat服务器,关联的过程中将webapp部署到Tomcat服务器中
- IDEA工具右上角,绿色小锤子右边有一个:Add Configuration
- 左上角加号,点击Tomcat Server->local
- 在弹出的界面设置服务器Server的参数(基本上不用)
- 在当前窗口中有一个Deployment(点击这个用来部署webapp),继续点击加号,部署即可
- 修改Application Context为:/xmm
- 第十一步:启动Tomcat服务器
- 右上角有绿色小虫子,点击绿色小虫子可以采用debug模式启动Tomcat服务器
- 我们开发者建议使用debug模式启动Tomcat
- 第十二步:打开浏览器,在浏览器中输入:http://localhost:8888/xmm/servlet/student
## Servlet对象的生命周期
- 什么是Servlet对象生命周期
- Servlet对象什么时候被创建
- Servlet对象什么时候被销毁
- Servlet对象创建了几个
- Servlet对象的生命周期表示:一个Servlet对象从出生到最后死亡,整个过程是怎么样的
- Servlet对象由谁维护
- Servlet对象的创建、销毁、调用,javaweb程序员无权干预
- Servlet对象的生命周期由Tomcat服务器(Web Server)全权负责
- Tomcat服务器我们通常又称为WEB 容器(WEB Container)
- WEB容器来管理Servlet的死活
- 思考:我们自己new的Servlet对象接受WEB容器的管理吗?
- 我们自己new的Servlet对象不受WEB容器管理
- WEB容器创建的Servlet对象,这些Servlet对象都会被放在一个集合(HashMap),只有放在这个HashMap集合中的Servlet才能够被WEB容器管理,自己new的Servlet对象不会被WEB容器管理(自己new的Servlet对象不在容器当中)
- WEB容器的底层应该有一个HashMap集合,在这个集合中存储了Servlet对象和请求路径之间的关系

- 研究:Tomcat服务器在启动的时候,Servlet对象有没有被创建?(默认情况下)
- 在Servlet中提供一个无参数的构造方法,启动服务器的时候看看构造方法是否执行
- 经过测试,默认情况下,服务器在启动的时候Servlet对象不会被实例化
- 这个设计很合理,用户发送请求之前,如果提前创建出所有的Servlet对象,必然是耗费内存的,并且创建出来的Servlet如果一直没有用户访问,显然这个Servlet是一个废物,没有必要创建
- 怎么让服务器启动的时候创建Servlet?
- 在servlet标签中添加子标签,在子标签中填写整数,越小的整数优先级越高
```xml
aservlet
com.bjpowernode.javaWeb.servlet.AServlet
0
aservlet
/a
```
- Servlet生命周期
- 默认情况下服务器启动的时候AServlet并没有被实例化
- 用户发送一次请求的时候,控制台输出以下信息:
```txt
AAA无参构造方法执行了
AServlet's init method is execute
AServlet's service method is execute
```
- 根据以上输出内容得出结论:
- 用户发送第一次请求的时候Servlet对象被实例化(AServlet的构造方法执行了,并且执行的是无参构造方法)
- AServlet对象被创建出来之后,Tomcat服务器马上调用AServlet的init方法(init方法在执行的时候,AServlet对象已经存在,已经被创建出来)
- 用户发送第一次请求的时候,init方法执行之后,Tomcat服务器马上调用了AServlet对象的service方法
- 用户继续发送第二次请求,控制台给出以下信息:
```
AServlet's service method is execute
```
- 根据以上输出结果得知,用户发送第二次,或者第三次,或者第四次请求的时候,Servclet对象并没有新建,还是使用之前创建好的Servlet对象,直接调用该Servlet对象的service方法,这说明:
- 第一:Servlet对象是单例的(单实例的,但是要注意的:Servlet对象是单实例的,但是Servlet类并不符合单例模式。我们称之为假单例。之所以单例是因为Servlet对象的创建我们程序员管不着,这个对象只能由Tomcat服务器说的算,Tomcat只创建一个,所以导致了单例,但是是属于假单例。真单例模式,构造方法是私有化的)
- 第二:无参构造方法、init方法只是在第一次用户发送请求的时候执行,也就是无参构造方法只执行一次,init方法也只被Tomcat服务器调用一次
- 第三:只要用户发送一次请求:service方法必然会被Tomcat服务器调用一次,发送100次,service方法会被调用100次
- 关闭服务器的时候,控制台输出以下信息
```
AServlet's destroy method is execute
```
- 通过以上输出内容,可以得出以下结论:
- Servlet的destroy方法只被Tomcat服务器调用一次
- destroy方法是在什么时候被调用?
- 在服务器关闭的时候
- 因为服务器在关闭的时候要销毁AServlet对象的内存
- 服务器在销毁AServlet对象内存之前,Tomcat服务器会自动调用AServlet对象的destroy方法
- destroy方法调用的时候,对象是否被销毁了?
- destroy方法执行的时候AServlet对象还在,没有被销毁。destroy方法执行结束之后,AServlet对象的内存才会被Tomcat释放
- Servlet对象更像一个人的出书
- Servlet无参构造方法的执行,标志着一个人的出生
- Servlet对象的init方法的执行,标志着你正在接受教育
- Servlet对象的service方法的执行,标志着你已经开始工作,已经开始为人类提供服务了
- Servlet对象的destroy方法的执行,标志着临终
- 关于Servlet类中方法的调用次数
- 构造方法只执行一次
- init方法只执行一次
- service方法:用户调用一次则执行一次
- destroy方法只执行一次
- 当我们Servlet类中编写一个有参数构造方法,如果没有手动编写无参数构造方法会出现什么问题?
- 报错:500错误
- 注意:500错误是HTTP协议的错误状态吗
- 如果没有无参数构造方法,无法实例化Servlet对象,会报错
- 所以,一定要注意:在Servlet开发当中,不建议程序员定义构造方法,因为定义不当,一不小心会导致无法实例化Servlet对象
- Servlet对象无参构造方法是在对象第一次创建的时候执行,并且只执行一次。init方法也是在对象第一次创建的时候执行,并且只执行一次。那么无参数构造方法可以替代init方法吗?
- 不能
- Servlet规范中要求:作为javaweb程序员,编写servlet类的时候,不建议手动编写构造方法,因为编写构造方法,很容易让无参数构造方法消失,这个操作可能导致Servlet对象无法实例化,所以init方法是有存在的必要的
- init、service、destroy方法中使用做多是哪一个?
- 使用做多的是service方法,所以service方法一定要实现的,因为service方法是处理用户请求的核心方法
- 什么时候使用init方法
- init方法很少有
- 通常在init方法中做初始化操作,并且这个初始化只需要执行一次。例如:初始化数据库连接池、初始化线程池...
- 什么时候使用destroy方法
- destroy方法也很少使用
- 通常在destroy方法当中,进行资源关闭。
- Servlet中的方法
- ```java
void destroy();
ServletConfig getServletConfig();
String getServletInfo();
void init(ServletConfig config);
void service(ServletRequest req, ServletResponse res);
```
## GenericServlet
- 我们编写一个Servlet类直接实现Servlet接口有什么缺点?
- 我们只需要service方法,其他方法大部分情况下是不需要使用的,代码很丑陋
- 适配器设计模式Adapter
- 编写一个GenericServlet类,这个类是抽象类,其中有一个抽象方法service
- GenericServlet实现Servlet接口
- GenericServlet是一个适配器
- 以后编写的所有Servlet类继承GenericServlet,重写service方法
- GenericServlet类是否需要改造一下?更利于子类程序的编写?
- init方法中的ServletConfig对象是谁创建?谁传递过来的?
- 都是Tomcat服务器
- Tomcat服务器先创建了ServletConfig对象,然后调用init方法,将ServletConfig对象传给init方法
## ServletConfig
- ServletConfig是什么
- jakarta.servlet.ServletConfig
- 显然ServletConfig是Servlet规范中的一员
- ServletConfig是一个接口 (jakarta.servlet.Servlet是一个接口)
- 谁实现了这个接口
- public class org.apache.catalina.core.StandardWrapperFacade implements ServletConfig(){}
- 结论:Tomcat服务器实现了ServletConfig接口
- 思考:如果把Tomcat服务器换成jetty服务器,输出ServletConfig对象时,还是这个结果吗?
- 不一定,包名类名可能和Tomcat不一样,但是实现了ServletConfig这个规范
- 一个Servlet对象中有一个ServletConfig对象。Servlet对象和ServletConfig对象一一对应
- ServletConfig对象是谁创建的?什么时候创建的?
- Tomcat服务器创建的
- 在创建Servlet对象创建的时候,同时创建了ServletConfig对象
- ServletConfig有什么用?
- Config是哪个单词缩写?
- Configuration
- ServletConfig对象被翻译为:Servlet对象的配置信息对象
- 一个Servlet对象就有一个ServletConfig对象
- 两个Servlet对象就有两个ServletConfig对象
- ServletConfig对象中包装了什么信息?
- ```xml
config2
com.bjpowernode.javaWeb.servlet.ConfigTextServlet2
```
- ServletConfig对象中包装的信息是:
- web.xml文件中****标签配置信息
- Tomcat服务器解析web.xml,将web.xml文件中****标签中的配置信息自动包装到ServletConfig对象中
- ServletConfig中的方法
- Enumeration getInitParameterNames() 获取所有初始化参数的name
- String getInitParameter(String name) 通过初始化信息的name获得value
- ```xml
config
com.bjpowernode.javaWeb.servlet.ConfigTextServlet
diriver
com.mysql.cj.jdbc.Driver
url
jdbc:mysql://localhost:3306/library
user
root
password
admin
```
- 以上****标签中的****是初始化参数,这个参数信息会自动被Tomcat封装到ServletConfig对象中
- 事实上获取一个ServletConfig对象的初始化参数,可以不用获取ServletConfig对象,直接通过this也可以,因为GenericServlet也实现了ServletConfig接口
- String getServletName() 获取Servlet的name
- ServletContext ServletContext() 获得ServletContext对象
- 以上四个方法,在自己编写的Servlet类中也可以通过this去调用(这个Servlet继承了GenericServlet)
- 总结
- ServletConfig对象是Servlet配置信息对象
- ServletConfig对象中封装了标签的配置信息
- 一个Servlet对应一个ServletConfig对象
- ServletConfig是Tomcat服务器创建的,并且Servlet对象也是Tomcat服务器创建的,并且默认情况下,他们都在用户发送第一次请求的时候创建
- Tomcat服务器调用Servlet对象中的init方法时需要传一个ServletConfig对象给init方法
- ServletConfig接口的实现类是Tomcat服务器实现的(Tomcat服务器就是WEB服务器)
- ServletConfig接口有哪些方法
- ```java
String getServletName();
ServletContext getServletContext();
String getInitParameter(String name);
Enumeration getInitParameterNames();
```
- 以上四种方法在Servlet中通过this也可以获得,因为GenericServlet实现了ServletConfig接口
## ServletContext
- ServletContext是什么
- ServletContext是接口,是Servlet规范中的一员
- ServletContext是谁实现的?
- Tomcat服务器实现的
- public class org.apache.catalina.core.ApplicationContextFacade implements ServletContext{}
- ServletContext对象是谁创建的?什么时候创建的?
- ServletContext对象在WEB服务器启动的时候创建
- ServletContext对象是WEB服务器创建的
- 对于一个webapp来说,ServletContext对象只有一个
- ServletContext对象在服务器关闭时销毁
- ServletContext怎么理解?
- ServletContext什么意思?
- Servlet对象的环境对象(Servlet对象的上下文对象)
- ServletContext对象其实对应的就是整个web.xml文件
- 50个学生,每个学生都是一个Servlet对象,这50个学生都在同一个教室,那么这个教室就相当于ServletContext对象。放在ServletContext对象中的数据,所有的servlet可以共享,比如教室中的空调,所有学生都可以操作
- Tomcat是一个容器,一个容器可以放多个webapp,一个webapp对应一个ServletContext对象
- ServletContext中常用的方法?
- ```java
String getInitParameter(String name);//通过初始化参数的name获得value
Enumeration getInitParameterNames();//通过初始化参数所有的name
```
```xml
pageSize
10
startIndex
0
```
- ```java
//获取应用的根(非常重要),这个方法可以动态获得应用的根路径
//在java源码中,不要将应用的根路径写死,因为你永远不知道这个应用在最终部署的时候,起一个什么名字
public String getContextPath();
```
- ```java
//获取文件的绝对路径(真实路径)
//不加/也可以 默认是从web根下开始找
public String getRealPath(String path);
```
- ```java
//通过ServletContext对象也可以记日志
public void log(String msg);
public void log(String message, Throwable throwable);
//这些日记记录到哪里?CATALINA_HOME/logs目录下
//localhost.2022-08-31.log
//Tomcat服务器下都有哪些日志文件
//catalina.2022-08-31.log 服务器端的Java程序运行的控制台信息
//localhost.2022-08-31.log ServletContext对象的log方法记录的日志信息存储到这个文件中
//localhost_access_log.2022-08-31.txt 访问日志
```
- ```java
//ServletContext还有一个名字(应用域)
//如果所有的用户共享一份数据,并且这个数据很少的被修改,并且这个数据量很少,可以将这些数据放到ServletContext这个运用域中
//为什么是所有用户共享?因为ServletContext对象只有一个,只有共享的数据放进去才有意义
//为什么数据量要少?因为数据量比较大的话,太占用推内存,并且这个对象的生命周期比较长,服务器关闭的时候,这个对象才会被销毁。大数据量会影响服务器的性能
//为什么数据几乎不修改?所有用户共享的数据,如果涉及到修改操作,必然会存在线程并发所带来的安全问题。所以放在ServletContext对象中的数据一般都是只读的
//数据量小、所有用户共享、又不修改,这样的数据放到ServletContext这个应用域中,会大大提升性能。因为作用域相当于一个缓存,放到缓存里的数据,下次再用的时候,不需要从数据库中再次获取,大大提升性能
//存(怎么向ServletContext应用域中存数据)
public void setAttribute(String name,Object value);
//取(怎么从ServletContext应用域中存数据)
public Object getAttribute(String name);
//删(怎么删除ServletContext应用域中数据)
public void removeAttribute(String name);
```
- 注意:以后我们编写Servlet类不会直接继承GenericServlet类的,因为我们是B/S结构的系统,这种系统我们是基于HTTP超文本传输协议的,在Servlet规范中,提供了一个类叫做HttpServlet,它是专门为HTTP协议准备的一个Servlet类。我们编写的Servlet类要继承HttpServlet。(HttpServlet是HTTP协议专用的)使用HttpServlet处理HTTP协议更便捷。但你需要知道它的继承结构
- ```java
jakarta.sevlet.Servlet(接口)【爷爷】
jakarta.servlet.GenricServlet(抽象类)【儿子】
jakarta.servlet.http.HttpServlet extends GenericServlet(抽象类)【孙子】
我们以后编写的Servlet要继承HttpServlet类
```
- 我们学过哪些缓存机制
- 字符串常量池
- “abc”先在字符串常量池中查找,如果有,直接拿来用,没有就新建,然后再放入字符串常量池
- 推内存中整数型常量池
- [-128-127]一共256个Integer类型的引用,放在整数型常量池,没有超出这个范围,直接从常量池中取
- 连接池(Connection Cache)
- 这里所说的连接池中的连接是Java连接数据库对象:Connection对象
- JVM是一个进程,MySQL也是一个进程,进程与进程之间打开通道是很费劲的,也很费资源的。怎么办?可以先创建N个Connection连接对象,将连接对象放到一个集合中,我们把这个放Connection对象的集合称为连接池。用户每一次连接数据库不需要新建连接对象,省去了新建的环节,直接从连接池中获取连接,大大提升了访问效率
- 连接池
- 最小连接数
- 最大连接数
- 连接池可以提高访问效率,当然也可以保证数据的安全性
- 线程池
- Tomcat服务器本身就支持多线程
- Tomcat服务器是在用户发送一次请求,就新建一个Thread线程对象吗?
- 当然不是,实际上是Tomcat服务器启动的时候,就会创建好N多个线程Thread对象,然后将线程对象放到集合中,称为线程池。用户发送请求过来之后,需要一个对应的线程对象来处理这个请求,效率比较高
- 所以的WEB服务器,或者是应用服务器,都是支持多线程的,都有线程机制
- redis
- NoSQL数据库,非关系型数据库,缓存数据库
- ServletContext应用域中储存数据
## HTTP
- 什么是协议
- 协议实际上是某些人或某些组织制定好的一套规范。大家按照这个规范来,这样可以做到沟通无障碍
- 什么是HTTP协议
- HTTP协议:是W3C制定的一种超文本传输协议(通信协议)
- W3C
- 万维网联盟组织
- 负责制定标准的:HTTP HTML4.0 HTML5 XML DOM等规范都是W3C制定的
- 万维网之父:蒂姆-伯纳斯-李
- 什么是超文本
- 超文本说的是:不是普通文本,比如流媒体:声音、视频、图片等
- HTTP协议支持:不但可以传送普通文本,同样支持声音、视频、图片等流媒体信息
- 这种协议游走在B和S之间,B向S发送数据要遵循HTTP协议,S向B发送输数据同样是需要遵循HTTP协议,这样B和S才能解耦合
- HTTP协议包括:
- 请求协议
- 浏览器向WEB服务器发送数据时需要遵循一套标准
- 响应协议
- WEB服务器向浏览器发送数据时需要遵循一套标准
- HTTP请求协议
- HTTP请求协议包括:4部分
- 请求行
- 请求头
- 空白行
- 请求体
- HTTP请求协议报文(Get):
- ```txt
GET /servlet05/getServlet?username=123&password=123 HTTP/1.1 请求行
Host: localhost:8888 请求头
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:104.0) Gecko/20100101 Firefox/104.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Referer: http://localhost:8888/servlet05/index.html
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
Pragma: no-cache
Cache-Control: no-cache
空白行
请求体
```
- HTTP请求协议报文(Post):
- ```
POST /servlet05/postServlet HTTP/1.1 请求行
Host: localhost:8888 请求头
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:104.0) Gecko/20100101 Firefox/104.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded
Content-Length: 25
Origin: http://localhost:8888
Connection: keep-alive
Referer: http://localhost:8888/servlet05/index.html
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
Pragma: no-cache
Cache-Control: no-cache
空白行
username=123&password=123 请求体
```
- 请求行
- 包括三部分
- 第一部分:请求方式
- get(常用的)
- post(常用的)
- delete
- put
- head
- options
- trace
- 第二部分:URI
- URI?:统一资源标识符。代表网络某个资源的名字,但是通过URI是无法定位资源的
- URL?:统一资源定位符。代表网络中某个资源,通过URL可以定位到该资源
- URI和URL有什么关系?
- URL包括URI。
- http://localhost:8888/servlet05/index.html URL
- /servlet05/index.html URI
- 第三部分:HTTP协议版本号
- 请求头
- 请求的主机
- 主机的端口
- 平台信息
- cookie信息
- ......
- 空白行
- 用来区分 请求头 和 请求体
- 请求体
- 向服务器发送的具体数据
- HTTP响应协议
- HTTP响应协议包括:4部分
- 状态行
- 响应头
- 空白行
- 响应体
- HTTP响应协议报文:
- ```
HTTP/1.1 200 ok 状态行
Content-Type:text/html;charset=UTF-8 响应头
Content-Length:157
Date:Thu, 01 Sep 2022 04:52:30 GMT
Keep-Alive:timeout=20
Connection:keep-alive
空白行
响应体
get Servlet
get请求
```
- 状态行
- 三部分组成
- 第一部分:协议版本号(HTTP/1.1)
- 第二部分:状态码(HTTP协议规定的响应状态号,不同的响应结果对应不同的号码)
- 200 表示响应成功,正常结束
- 400 表示访问的资源不存在,通常是因为要么是你路径写错了,要么时路径写对了但是的服务器对应的资源没有启动成功。总之404代表前端错误
- 405 表示前端发送的请求方式与后端请求的处理方式不一致
- 比如:前端发送POST请求,后端处理方式时按照GET方式进行处理
- 500 表示服务器端的程序出现了异常,一般会认为是服务端的错误导致
- 以4开头的,一般是浏览器端的错误导致的
- 以5开头的,一般是服务器端的错误导致的
- 第三部分:状态的描述信息
- ok:表示正常成功结束
- not found:表示资源找不到
- 响应头:
- 响应的内容类型
- 响应的内容长度
- 响应的时间
- ......
- 空白行:
- 用来分割 响应头 和响应体
- 响应体:
- 响应体就是响应的内容,这些内容是一个长的字符串,这个字符串被浏览器渲染,解释并执行,最终展示出效果
- 怎么查看协议内容?
- 使用浏览器:F12,然后找到network,通过这个面板可以查看协议的内容
- 怎么向服务器发送Get请求?怎么向服务器发送Post请求?
- 到目前为止,只有一种情况可以发送POST请求:使用form表单,并且form表单中的method属性为:method="post"
- 其他所以情况一律属于GET请求
- 在浏览器地址栏直接输入URL
- 在浏览器上直接点击超链接
- 使用form表单,没有写method属性,默认是get
- 或者使用form表单,form表单中method属性是:method="get"
- ......
- GET请求和POST请求有什么区别?
- get请求发送数据的时候,数据会挂在URL的后面,并且URL的后面加了一个"?","?"后面是数据。这样会导致发送的数据会回显在浏览器地址栏上(get请求在请求行上发送数据)
- servlet05/getServlet?username=123&password=123
- post请求发送数据的时候,在请求体当中发送,不会回显到浏览器地址栏上(post请求在请求体当中发送数据)
- /servlet05/getServlet
- get请求只能发送普通的字符串,并且发送的字符串长度有限制。不同的浏览器限制不同
- get请求无法发送大数据量
- post请求可以发送任何类型的数据,包括普通字符串、流媒体信息:视频、声音、图片
- post请求可以发送大数据量,理论上没有长度限制
- get请求在W3C中是这样说的:get请求比较适合从服务器端获取数据
- post请求在W3C中是这样说的:post请求比较适合向服务器端传送数据
- get请求是安全的,为什么?因为get请求只是从服务器上获取数据
- post请求是危险的,为什么?因为post请是向服务器提交数据,如果数据通过后门进入服务器中,服务器是很危险的,另外post请求是为了提交数据,所以一般情况下拦截请求的时候,大部分会拦截(监听)post请求
- get请求支持缓存,post请求不支持缓存
- 任何一个get请求最终的“响应结果”都会被浏览器缓存起来。
- 一个get请求对应一个资源
- 事实上,你只要发送了get请求,浏览器第一件事先从本地浏览器缓存中找,找不到的时候才会去服务器上找,这种缓存机制的目的是为了提高用户的体验
- post请求之后,服务器“响应结果”不会被浏览器缓存起来,因为这个缓存没有意义
- 如果我们有一个需求:不希望get请求走缓存,每一次get请求都从服务器上找资源
- 只要每一次get请求路径不同即可
- 怎么解决
- 使用时间戳 lt=系统毫秒数
- http://localhost:8888/servlet05/getServlet?username=123&password=123<=21567
- http://localhost:8888/servlet05/getServlet?username=123&password=123<=系统毫秒数
- GET请求和POST请求如何选择?
- 如果想从服务器获取资源,建议使用get请求
- 如果是为了向服务器提交资源,建议使用post请求
- 不管你是在get请求还是在post请求,发送的数据格式都是相同的,只是位置不同
- name=value&name=value&name=value
- name是什么?
- 以form表单为例:form表单中input标签的name
- value是什么?
- 以form表单为例:form表单中input标签的value
## 模板方法设计模式
- 什么是设计模式?
- 解决某个问题的固定的解决方案
- 有哪些设计模式?
- GoF设计模式
- 通常我们所说的23种设计模式(Gang of Four:4人组提出的设计模式)
- 单例模式
- 工厂模式
- 代理模式
- 门面模式
- ......
- JavaEE设计模式
- DAO
- DTO
- VO
- PO
- ......
- 什么是模板方法设计模式?
- 在模板类的模板方法定义核心算法骨架,具体的实现步骤可以延迟到子类中去完成
- 模板类通常是一个抽象类,模板类中的模板方法定义核心算法,这个方法通常是final(但也可以不是final的)
- 模板类当中的抽象方法就是不确定实现的方法,这个不确定怎么实现的事交给子类去做
## HttpServlet源码分析
- HttpServlet类是专门为HTTP协议准备的,比GenericServlet更适合HTTP协议下的开发
- HttpServlet在哪个包下?
- jakarta.servlet.http.HttpServlet
- 到目前为止我们接触了servlet规范中哪些接口?
- jakarta.servlet.Servlet 核心接口
- jakarta.servlet.ServletConfig Servlet配置信息接口
- jakarta.servlet.ServletContext Servlet上下文接口
- jakarta.servlet.ServletRequest Servlet请求接口
- jakarta.servlet.ServletResponse Servlet响应接口
- jakarta.servlet.ServletException Servlet异常类
- jakarta.servlet.GenericServlet 标准通用的Servlet类(抽象类)
- http包下都有哪些类和接口?jakarta.servlet.http.*
- jakarta.servlet.http.HttpServlet (HTTP协议专用的Servlet类,抽象类)
- jakarta.servlet.http.HttpServletRequest(HTTP协议专用的请求对象,接口)
- jakarta.servlet.http.HttpServletResponse(HTTP协议专用的响应对象,接口)
- HttpServletRequest对象中封装了哪些信息?
- HttpServletRequest,简称request对象
- HttpServletRequest封装了请求协议的全部内容
- Tomcat服务器(WEB服务器)将“请求协议”中的数据全部解析出来,然后将这些数据全部封装到request对象中
- 也就是说,我们只要面向HttpServletRequest,就可以获得请求协议中的数据
- HttpServletRequest对象是专门用来响应HTTP协议到浏览器的
- HttpServlet源码分析
```java
public class HelloWordServlet extends HttpServlet {
// 通过无参构造方法创建对象
public HelloWordServlet() {
}
// 没有提供init方法,那么必然执行HttpServlet的init方法
// HttpServlet类中没有init方法,会继续执行HttpServlet父类GenericServlet中的init方法
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
public void init() throws ServletException {
}
// 没有提供service方法,那么必然执行HttpServlet的service方法
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
HttpServletRequest request;
HttpServletResponse response;
//将ServletRequest和ServletResponse向下转型为HttpServletRequest和HttpServletResponse
try {
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
} catch (ClassCastException e) {
throw new ServletException(lStrings.getString("http.non_http"));
}
//调用重载的service方法
service(request, response);
}
//这个service方法的两个参数都是带有Http
//这个service方法是一个模板方法 HttpServlet就是一个模板类
//在该方法中定义核心算法骨架,具体的实现步骤延迟到子类中去完成
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//获取请求方式
//注意:getMethod()方法获取的请求方式,可能是其中之一:
//GET POST PUT DELETE HEAD OPTIONS TRACE
String method = req.getMethod();
//如果请求方式是GET,执行doGet方法
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
} catch (IllegalArgumentException iae) {
// Invalid date header - proceed as if none was set
ifModifiedSince = -1;
}
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
//HttppServlet中doGet和doPost方法
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{ //报405错误
String msg = lStrings.getString("http.method_get_not_supported");
sendMethodNotAllowed(req, resp, msg);
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String msg = lStrings.getString("http.method_post_not_supported");
sendMethodNotAllowed(req, resp, msg);
}
}
```
- 通过以上源码分析,假如前端发送的请求是get请求,但是程序员重写的是doPost方法;或者是前端发送post请求,后端程序员重写的doGet方法,会发生什么?
- 发生405错误,405表示前端错误,发送的请求方式不对,不是服务器需要的请求方式
- 只要HttpServlet类中的doGet方法或者doPost方法执行了,必然会出现405错误
- 怎么避免405错误?
- 后端重写了doGet方法,前端一定要发送get请求
- 后端重写了doPost方法,前端一定要发送post请求
- 你会看到,为了避免错误,有些人在Servlet中将doGet和doPost方法都进行了重写
- 这样确实可以避免405错误,但不建议。405错误还是有用的,该报错的时候就该让它报错
- 如果同时重写了doGet和doPost方法,还不如直接重写service方法
- 我们编写的HelloServlet类继承HttpServlet类,直接重写HttpServlet类中的service()方法行吗?
- 可以,不过你享受不到405错误,享受不到HTTP协议专属的东西
- 到今天我们终于得到了一个最终的Servlet类的开发过程
- 第一步:编写一个Sevlet,直接继承HttpServlet
- 第二步:重写doPost方法或者doGet方法,到底重写谁,由javaweb程序员说的算
- 第三步:将Servlet类配置到web.xml文件中
- 第四步:准备前端的页面
## 关于一个web站点的欢迎页面
- 什么是一个web站点的欢迎页面
- 对于一个webapp来说,我们可以设置它的欢迎页面的
- 设置了web站点欢迎页面后,当你访问这个webapp的时候,或者访问这个web站点的时候,没有指定任何资源路径,这个时候会默认访问你的欢迎页面
- 怎么设置欢迎页面
- 第一步:在IDEA工具的web目录下新建了一个login.html
- 第二步:在web.xml文件中进行以下配置
- ```xml
login.html
```
- 注意:设置欢迎界面的时候,这个路径不需要以“/”开始,并且这个路径默认是从webapp的根下开始找
- 第三步 :启动服务器在浏览器地址栏输入地址
- http://localhost:8888/servlet07
- 如果在webapp的根下新建一个目录,目录中再给个文件,那么这个欢迎页面如何设置?
- web.xml文件中这样配置
- ```xml
目录名/文件名
```
- 一个webapp是可以设置多个欢迎页面的
- ```xml
page1/page2/page.html
login.html
```
- 注意:越靠上的优先级越高。找不到往下找
- 当我的文件名设置为index.html的时候,不需要再web.xml文件中进行配置,为什么?
- 这是因为Tomcat服务器已经提前配置好了
- 实际上设置欢迎页面有两个地方可以配置
- 一个是webapp内部的web.xml文件(这个地方属于局部配置)
- 一个是再CATALINA_HOME/conf/web.xml文件中进行配置(这个地方属于全局配置)
- ```xml
index.html
index.htm
index.jsp
```
- 注意原则:局部优先级(就近原则)
- 欢迎页可以是一个Servlet吗?
- 当然可以,欢迎页就是一个资源,既然是一个资源,那么可以是静态资源,也可以是动态资源
- 静态资源:index.html等
- 动态资源:Servlet
## 关于WEB-INF目录
- 放在WEB-INF目录下的资源是受保护的,在浏览器上不能通过路径直接访问。所以像HTML、CSS、image等静态资源一定要放到WEB-INF目录之外
## HttpServletRequest接口详解
- HttpServletRequest是一个接口
- 全限定名称:jakarta.servlet.http.HttpServletRequest
- HttpServletRequest接口是Servlet规范中的一员
- HttpServletRequest的父接口是:ServletRequest
- ```java
public interface HttpServletRequest extends ServletRequest {}
```
- HttpServletRequest接口实现类是谁写的?HttpServletRequest对象是给谁创建的?
- 通过测试:org.apache.catalina.connector.RequestFacade实现了HttpServletRequest接口
- Tomcat服务器是实现了HttpServletRequest接口
- HttpServletRequest对象中有什么信息?都包装了什么信息?
- HttpServletRequest对象是Tomcat服务器负责创建的,这个对象封装了HTTP的请求协议
- 实际上是用户发送请求的时候,遵循了HTTP协议,发送的是HTTP的请求协议,Tomcat服务器将HTTP协议中的信息以及数据全部解析出来,然后Tomcat服务器把这些信息封装到HttpServletRequest对象中,传给了我们javaweb程序员
- javaweb程序员面向HttpServletRequest接口编程,调用方法就可以获得请求的信息了
- request和response对象的生命周期
- 一个是请求对象,一个是响应对象。这两个对象只在当前请求有效
- 一次请求对应一个request
- ......
- HttpServletRequest接口中有哪些方法?
- 怎么获取前端用户提交的数据?
- ```java
public String getParameter(String name); 获取value这个一维数组的第一个元素(最常用)
public Map getParameterMap(); 这个获取Map
public Enumeration getParameterNames(); 这个获取Map集合的key
public String[] getParameterValues(String name); 根据key获取value
//以上的4个方法,和获取用户提交的数据有关
```
- 思考:如果是你,前端的form表单提交了数据之后,你准备怎么存储这些数据,采用什么样的数据结构?
- 前端提交的数据格式:username=123&userpwd=122&aihao=s&aihao=d&aihao=tt
- 我会采用map集合
- ```java
Map
key存储String
value存储String
如果采用这种想法,key重复的时候value被覆盖
------------------------------------------
改进后:
Map
key存储String
value存储String[]
```
- 注意:前端表单提交数据的时候,前端永远提交字符串,后端获取的也永远是字符串
- request对象实际上又称为“请求域对象”
- 应用域对象是什么?
- ServletContext(Servlet上下文对象)
- 请求域对象只在一次请求内有效,一次请求结束,这个请求域就销毁了。请求域对象比应用域对象范围小很多,生命周期也短很多
- 请求域和应用域都有如下三个方法
- ```java
public void setAttribute(String name,Object value);
public Object getAttribute(String name);
public void removeAttribute(String name);
```
- 请求域与应用域的选用原则?
- 尽量使用小的域对象,以为小的域对象占用的资源少
- 使用Servlet的转发机制,完成资源跳转
- 怎么跳转?
- 第一步:获取请求转发器对象
- ```java
RequestDispatcher requestDispatcher =request.getRequestDispatcher("/b");
//相当于把“/b”这个路径包装到请求转发器当中,实际上是把下一个跳转的资源路径告知Tomcat服务器 以"/"开始,不需要加项目名
```
- 第二步:调用请求转发器RequestDispatcher的forward方法,进行转发
- ```java
requestDispatcher.forward(request,response);
//转发的时候,这两个参数很重要,request和response都是要传递给下一个资源的
```
- 第一步和第二步可以联合在一起
- ```java
request.getRequestDispatcher("/b").forward(request,response);
```
- 两个Servlet怎么共享数据?
- 将数据放到ServletContext应用域当中,当然是可以的,但是应用域范围太大,占用资源太多,不建议使用
- 可以将数据放到Request域当中,然后AServlet跳转到BServlet,保证AServlet和BServlet在同一次请求中,这样就可以做到两个Servlet,或者多个Servlet共享一份数据
- 转发的下一个资源必须是一个Servlet吗?
- 不一定,只要是Tomcat服务器当中的合法资源,都是可以转发的,例如:html......
- 注意:转发的时候,路径的写法要注意,转发的路径以“/”开头,不加项目名
- 关于request对象中的两个非常容易混淆的方法
- ```java
String username=request.getParameter("username");
Object obj=request.getAttribute("username");
//第一个方法,获取的是用户在浏览器上提交的数据
//第二个方法,获取的是请求域当中绑定的数据
```
- HttpServletRequest接口的其他常用方法
- ```java
//获取客户端IP地址
String addr = request.getRemoteAddr();
//设置请求体的字符集 显然这个方法是处理POST请求的乱码问题。这种方式不能解决get请求乱码问题
//Tomcat10之后,request请求体当中的字符集默认是UTF-8,不需要设置字符集,不会出现乱码
//Tomcat9之前(包括9在内),如果当前请求体提交的是中文,获取之后出现乱码,怎么解决这个乱码?执行以下代码
request.setCharacterEncoding("UTF-8);
//在Tomcat9之前(包括9)响应中文也会有乱码,怎么解决这个乱码?
response.setContentType("text/html;charset=UTF-8");
//在Tomcat10之后,包括10在内,响应中文的时候不会再出现乱码,以上代码不需要设置UTF-8
//get请求出现乱码怎么解决?
//get请求提交数据的时候是在请求行上进行的
//修改CATALINA_HOME/conf/server.xml配置文件
//注意:从Tomcat8之后,URIEncoding默认值是UTF-8,7之前的都是ISO8859-1,所有GET请求也没有乱码问题
//动态获取应用的根路径
public String getContextPath();
//获取请求方式
public String getMethod();
//获取请求的URI
public String getRequestURI(); // servlet08/request
//获取ServletPath(url-pattern)
public String getServletPath(); // request
```
## 使用纯Servlet做一个单表的CRUD操作
- 使用纯粹的Servlet完成单表【部门】的增删改查操作(B/S)
- 实现步骤
- 第一步:准备数据库表
- ```sql
#部门表
drop table if exists dept;
create table dept(
deptno int primary key,
dname varchar(255),
loc varchar(255)
);
insert into dept(deptno,dname,loc) values(10,'销售部','北京');
insert into dept(deptno,dname,loc) values(20,'研发部','上海');
insert into dept(deptno,dname,loc) values(30,'技术部','广州');
insert into dept(deptno,dname,loc) values(40,'媒体部','深圳');
commit;
select * from dept;
```
- 准备一套HTML页面(项目原型)
- 把HTML页面准备好
- 然后把HTML页面中的链接都能跑通
- 应该设计哪些页面?
- 新增页面:add.html
- 修改页面:edit.html
- 详情页面:detail.html
- 欢迎页面:index.html
- 列表页面:list.html(以list列表为核心,展开其他操作)
- 第三步:分析我们系统有哪些功能
- 查看部分列表
- 新增部门
- 删除部门
- 查看部门详细信息
- 跳转到修改页面
- 修改部门
- 第四步:在IDEA中搭建开发环境
- 创建一个webapp
- 向webapp添加连接数据库的jar包
- 必须在WEB-INF目录下新建一个lib目录
- JDBC的工具类
- 第五步:实现功能
- 注意:转发是一次请求,在请求转发的时候,如果当前请求是post请求,那么之后依旧是post请求
## 在一个web应用中应该如何完成资源的跳转
- 在一个web应用中,可以通过两种方式完成资源的跳转
- 第一种方式:转发
- 第二种方式:重定向
- 转发和重定向有什么区别?
- 代码上有什么区别
- 转发
- ```java
//获取请求转发器
RequestDispatcher dispatcher=request.getRequestDispatcher("/dept/list");
//调用请求转发器的forward方法完成转发
dispatcher.forward(request,response);
//合并成一行代码
request.getRequestDispatcher("/dept/list").forward(request,response);
```
- 重定向
- ```java
//注意:路径上要加一个项目名,为什么?
//浏览器发送请求,请求路径上需要添加项目名
//以下这一行代码会将请求路径"/oa/dept/list"发送给浏览器
//浏览器会自发的向服务器发送一次全选的请求:"/oa/dept/list"
response.sendRedirect("/oa/dept/list");
```
- 形式上什么区别
- 转发(一次请求)
- 在浏览器地址栏上发送的请求是:http://localhost:8888/servlet10/a, 最终请求结束之后,浏览器地址栏上的地址还是这个,没变
- 重定向(两次请求)
- 在浏览器地址栏上发送的请求是:http://localhost:8888/servlet10/a,最终请求结束之后,浏览器地址栏上的显示的地址是:http://localhost:8888/servlet10/b
- 转发和重定向的本质区别?
- 转发:是WEB服务器来控制的,A资源跳转到B资源,这个跳转动作是Tomcat服务器内部完成的
- 重定向:是浏览器完成的,具体跳转到哪个资源,浏览器说了算
- 转发和重定向如何选择?
- 如果在上一个Servlet当中向request域中绑定了数据,希望从下一个Servlet中把request域里面的数据取出来,使用转发机制
- 剩下的所有请求均使用重定向(重定向使用较多)
- 跳转的下一个资源有没有要求?必须是一个Servlet吗?
- 不一定,跳转的资源只要是服务器内部合法的资源即可。包括:Servlet、JSP、HTML......
- 转发会存在浏览器刷新问题
## Servlet注解,简化配置
- 分析项目中的web.xml文件
- 现在只是一个单标的CRUD,没有复杂的业务逻辑,很简单的一丢丢功能。web.xml文件中就有如此多的配置信息。如果采用这种方式,对于一个大的项目来说,这样的话web.xml文件会非常大,有可能会达到几十兆
- 在web.xml文件中进行servlet信息的配置,显然开发效率比较低,每一个都需要配置一下
- 而且web.xml文件中的配置很少被修改的,所以这种配置信息能不能直接写到java类中呢?当然可以
- Servlet3.0版本之后,推出了各种Servlet基于注解式开发,优点是什么?
- 开发效率高,不需要编写大量的配置信息,直接在java类中使用注解进行标注
- web.xml文件体积变小了
- 并不是说有了注解之后,web.xml文件就不需要了
- 有一些需要变化的信息,还是需要配置到web.xml文件中。一般都是 注解+配置文件 的开发模式
- 一些不会经常变化修改的配置建议使用注解,一些可能会被修改的建议写到配置文件中
- 我们的第一个注解
- ```java
jakarta.servlet.annotation.WebServlet
```
- 在类上使用:@WebServlet
- WebServlet注解中有哪些属性
- name属性:用来指定Servlet的名字,等同于:
- urlPatterns属性:用来指定Servlet的映射路径,可以指定多个字符串,等同于:
- loadOnStartUp属性:用来指定在服务器启动阶段是否加载该Servlet类,等同于:
- value属性:和urlPatterns效果一样,不同在于,当注解的属性名是value的时候,使用注解的时候属性名可以省略
- 注意:不是必须将所有属性都写上,只需要提供需要的
- 注意:属性是一个数组,如果数组中只有一个元素,使用注解的时候,属性值的大括号可以省略
- 注解对象的使用格式:
- @注解名(属性名=属性值,属性名=属性值,属性名=属性值......)
## 使用模板设计模式优化oa项目
- 上面的注解解决了配置文件的问题,但是现在的oa项目仍然存在一个比较臃肿的问题
- 一个单标的CRUD,就写了6个Servlet,如果一个复杂的业务系统,这种开发模式,显然会导致类爆炸(类的数量太大)
- 怎么解决这个类爆炸问题?可以使用模板方法设计模式
- 以前的设计是一个请求一个Servlet,1000个请求1000个Servlet
- 可以这样做:一个请求对应一个方法,一个业务对应一个Servlet类
- 处理部门相关业务的对应一个DeptServlet。处理用户相关业务的对应一个UserServlt类。处理银行相关业务的对应一个CardServlet
## 分析使用纯粹的Servlet开发web应用的缺陷
- 在Servlet当中编写HTML/CSS/JavaScript等前端代码,存在什么问题?
- java程序员编写前端代码,编写难度大,麻烦,耦合度非常高,代码不美观,维护成本太高
- 修改小小的一个前端代码,,只要有改动,就需要重新编译java代码,生成新的class文件,打一个新的web包,重新发布
- 思考一下,怎么解决这个问题?
- 上面的Servlet(java程序)能不能不写了,让机器自动生成。我们程序员只需要写这个Servlet程序中的前端的“代码”,然后让机器将我们写的“前端”代码自动翻译生成“Servlet这种java程序”,然后机器自动将“java程序”翻译生成“class文件”。然后让JVM调用这个class中的方法
## B/S结构系统的会话机制(session机制)
- 什么是会话
- 会话对应的英语单词:session
- 用户打开浏览器,进行一系列操作,然后最终关闭浏览器,这个整个过程叫做:一次会话。会话在服务器端也有一个对象,这个java对象叫做:session
- 回顾:什么是一次请求?用户在浏览器点击一下,然后到页面停下来,可以粗略的认为是一次请求。请求对应的服务器端的对象是:request
- 在Java的Servlet规范中,session对应的类名是:HttpSession(jakarta.servlet.http.HttpSession)
- session机制属于B/S结构的一部分。session机制实际上是规范,不同的语言对这种会话机制都有实现
- session对象的主要作用是什么?
- 保存会话状态(用户登陆成功了,这是一种登陆成功的状态,那么你怎么把登陆成功的状态一直保存下来?使用session对象可以保留会话状态)
- 为什么需要session对象来保存会话状态呢?
- 因为HTTP协议是一种无状态协议
- 什么是无状态?
- 请求的时候,B和S是连接的,但是请求结束之后,连接就断了。为什么要这样做?HTTP协议为什么要设计成这样?
- 因为这样的无状态协议可以降低服务器压力。请求的瞬间连接,请求结束之后,连接断开,这样服务器压力小
- 只要B和S断开了,那么关闭浏览器这个动作,服务器知道吗?
- 不知道,服务器是不知道浏览器关闭的
- 张三打开一个浏览器A,李四打开一个浏览器B,访问浏览器后,在服务器端就会生成:
- 张三专属的session对象
- 李四专属的session对象
- 为什么不使用request对象保存会话状态?为什么不使用ServletContext对象保存会话状态?
- request是一次请求一个对象
- ServletContext对象是服务器启动的时候创建,服务器关闭的时候销毁,这个ServletContext对象只有一个
- ServletContext对象的域太大了
- request请求域(HttppServletRequest)、session域(HttpSession)、application域(ServletContext)
- request < session < application
- session对象怎么获取?
- ```java
HttpSession session = request.getSession();
//从服务器端获取当前的session对象,如果没有获取到任何的session对象,则新建
```
- ```java
HttpSession session = request.getSession(false);
//从服务器端获取当前的session对象,如果获取不到,则不会新建,返回null
```
- session对象的实现原理
- session对象是存储在服务器端的
- 一个session对象对应一个会话
- 一次会话包含多次请求
- session的实现原理
- 在web服务器中有一个session列表,类似于map集合,这个map集合的key存储的是sessionid,这个map集合的value存储的是对应的session对象
- 用户发送第一次请求的时候:服务器会创建一个新的session对象。同时给session对象生成一个id,然后web服务器会将session对象的id发送给浏览器,浏览器将session的id保存在浏览器的缓存中
- 用户发送第二次请求的时候:浏览器会将浏览器缓存中的sessionid自动发给服务器,服务器获取到sessionid,然后从session列表中查找到对应的session对象
- JSESSIONID=xxxx 这个是以Cookie的形式保存在浏览器的内存中的,浏览器只要关闭,这个cookie就没有了
- 
- 为什么关闭浏览器,会话结束?
- 关闭浏览器之后,浏览器中保存的sessionid消失,下次重新打开浏览器之后,浏览器缓存中没有这个sessionid,自然找不到服务器中对应的session对象,session对象找不到等同于会话结束
- session对象什么时候被销毁?
- 一种销毁:超时销毁
- 一种销毁:手动销毁
- Cookies禁用了,session还能找到吗?
- cookie禁用了是什么意思?服务器正常发送cookie给浏览器,但是浏览器不要了,拒收了,不是服务器不发了
- 因此找不到了,浏览器每一次请求都会获得新的session对象
- cookie禁用了,session机制还能实现吗?
- 可以,使用URL重写机制
- http://localhost:8888/servlet13/text/session;jsessionid=B9F40A6A6C0526231EC14DCBBB9EEF1E
- URL重写机制会提高开发者的成本。开发人员在编写任何请求路径的时候,后面都要添加一个sessionid,给开发带来了很大的难度,很大的成本。所以大部分的网站都是这样设计的:你要是禁用cookie,你就别用了
- 总结一下,到目前为止我们所了解的域对象
- request(HttpServletRequest)
- 请求域
- session(HttpSession)
- 会话域
- application(ServletContext)
- 应用域
- 这三个域的大小关系
- request < session < application
- 他们三个域对象都有下面三个公用的方法
- setAttribute
- getAttribute
- removeAttribute
- 使用原则:尽量使用小的域
- 学习了session之后,我们怎么解决oa项目中的登陆问题
- 登陆成功之后,可以将用户的登录信息存储到session当中
- 销毁session对象
- ```java
session.invalidate();
```
- 怎么让jsp页面不自己创建session?
- ```jsp
<%--访问jsp时不生成session对象,但要注意session为false时 内置对象session就不能用了--%>
<%@page session="false" %>
```
- 怎么设置session的销毁时间
- ```xml
30
```
- 如果没有配置,session默认销毁时间时30分钟
- 在CATALINA_HOME/conf/web.xml中可以查看session默认销毁时间
## Cookie
- session实现原理中,每一个session对象都会关联一个sessionid
- JSESSIONID=8BCDCEC3DBEFE92FDF825BCEA5045CA3
- 以上这个键值对就是Cookie对象
- 对于session关联的cookie来说,这个cookie是被保存在浏览器运行的内存中
- 只要浏览器不关闭,用户再次发送请求的时候,浏览器会自动将运行的cookie发送给服务器
- 例如,这个Cookie:JSESSIONID=8BCDCEC3DBEFE92FDF825BCEA5045CA3就会再次发送给服务器
- 服务器会根据8BCDCEC3DBEFE92FDF825BCEA5045CA3这个值来找对应的session对象
- cookie怎么生成?cookie保存在什么地方?cookie有什么用?浏览器什么时候会发送cookie?发送哪些cookie给服务器?
- cookie最终是保存在浏览器客户端上的
- 可以保存在运行内存中(浏览器关闭cookie就消失了)
- 也可以保存在硬盘文件中(永久保存)
- cookie有什么用?
- cookie和session机制其实都是为了保存会话的状态
- cookie是将会话的状态保存在浏览器客户端(cookie数据存储在浏览器客户端)
- session是将会话的状态保存在服务器端上(session对象是存储在服务器上)
- 为什么要有cookie和session机制呢?因为HTTP协议是无状态 无连接协议
- Cookie经典案例
- 京东商城,在未登录的情况下,向购物车中放几件商品。然后关闭商城,再次打开浏览器,访问京东商城的时候,购物车中的商品还在,这是怎么做到的?我没有登录,为什么购物车中还有商品?
- 将购物车的商品编号放到cookie中,cookie保存在硬盘文件中,这样即使关闭浏览器,硬盘上的cookie还在,下一次再次打开京东购物车的时候,会自动读取本地硬盘中存储的cookie,拿到商品编号,动态展示购物车中的商品
- 京东存储购物车中商品的cookie可能是这样:prodects=xxx.yyy.zzz
- 注意:cookie如果清除掉,购物车中的商品就消失了
- 126邮箱中有一个功能,十天免登录
- 这个功能也是需要cookie来实现的
- 怎么实现的呢?
- 用户输入正确的用户名和密码,并且同时选择了十天内免登录。登陆成功后。浏览器客户端会保存一个cookie,这个cookie保存了用户名和密码等信息,这个cookie是保存在硬盘文件中的,十天有效。在十天内用户再次访问126的时候,浏览器自动提交126的关联的cookie给服务器,服务器接收到cookie之后,获取用户名和密码,验证通过之后,自动登陆成功
- 怎么让cookie失效?
- 十天后自动失效
- 改密码
- 在浏览器客户端清除cookie (ctrl+shift+del)
- cookie机制和session机制其实都不属于java中的机制,实际上cookie机制和session机制都是HTTP协议的一部分,php开发中也有cookie机制和session机制,只要你是做web开发,不管是什么编程语言,cookie和session机制都是需要的
- HTTP协议中规定:任何一个cookie都是由name和value组成的,name和value都是字符串类型的
- 在java的Servlet中,对cookie提供了哪些支持呢?
- 提供了一个Cookie类专门表示cookie数据:jakarta.servlet.http.Cookie
- java程序员怎么把cookie数据发送给浏览器?response.addCookie(cookie);
- HTTP协议中是这样规定的:当浏览器发送请求的时候,会自动携带该path下的cookie数据给服务器(URL)
- 关于cookie的有效时间
- 怎么用java设置cookie的有效时间
- cookie.setMaxAge(60*60);设置cookie一小时之后消失
- 没有设置cookie有效时间,默认保存在浏览器的运行内存当中,浏览器关闭则失效
- 只要设置cookie的有效时间 > 0,cookie保存在硬盘文件中
- 设置cookie的有效时间为0,表示该cookie被删除,主要应用在:删除浏览器上同名的cookie
- 设置cookie的有效时间为负数,表示该cookie不会被存储在硬盘文件中,会放在浏览器运行内存中,和不调用setMaxAge同一个效果
- 关于cookie的path,cookie关联的路径是什么?
- 假设当前发出的请求路径是:http://localhost:8888/servlet14/cookie/generate 生成cookie,如果cookie没有设置path,默认的path是什么?
- 默认的path是:http://localhost:8888/servlet14/cookie 以及它的子路径
- 也就是说,以后只要浏览器的请求路径是:http://localhost:8888/servlet14/cookie 这个路径以及这个路径下的子路径,cookie都会被发送到服务器
- 手动设置cookie的path
- cookie.setPath("/servlet14");表示只要是这个servlet14项目的请求路径,都会提交这个cookie给服务器
- 浏览器发送cookie给服务器,服务器中的Java程序怎么接收?
- ```java
Cookie[] cookies = request.getCookies(); //可能返回null
if(cookies!=null){
for (Cookie cookie : cookies) {
//获取cookie的name
String name=cookie.getName();
//获取cookie的value
String value = cookie.getValue();
}
}
```
- 使用cookie实现十天内免登录
- 先实现登陆功能
- 登陆成功
- 跳转到部门列表页面
- 登陆失败
- 跳转到登录失败页面
- 修改前端页面
- 在登录页面给一个复选框,复选框后面给一句话:十天内免登录
- 修改servlet中login方法
- 如果用户登陆成功并且用户选择是十天内免登录,这个时候要在login中创建cookie,用来存储用户名和密码,并且设置路径,设置有效时间,将cookie响应给浏览器(浏览器将其自动保存在硬盘文件中十天)
- 用户再次访问网站欢迎页的时候,有两个走向
- 要么跳转到部门列表页面
- 要么跳转到登录页面
# JSP
- 我的的第一个JSP程序
- 在WEB-INF目录之外新建一个index.jsp文件,然后这个文件中没有任何内容
- 将上面项目部署之后,启动服务器,打开浏览器,访问以下地址
- http://localhost:8888/jsp/index.jsp 展现在大家面前的是一个空白
- 实际上访问以上的这个:index.jsp,底层执行的是:index.class这个程序
- 这个index.jsp会被tocmat翻译生成index_jsp.java文件,然后tomcat服务器又会将index_jsp.java文件编译生成index_jsp.class文件
- 访问index.jsp,实际上执行的是index_jsp.class中的方法
- JSP实际上就是一个Servlet
- index_jsp访问的时候,会自动翻译生成index_jsp.java,然后自动编译生成index_jsp.class,那么这个index.jsp就是一个类
- index.jsp继承了HttpJspBase,而HttpJspBase继承的是HttpServlet。所以index_jsp类就是一个Servlet类
- jsp的生命周期和Servlet生命周期完全相同。就是同一个东西,没有任何区别
- jsp和Servlet一样都是单例的(假单例)
- jsp文件第一次访问的时候是比较慢的,为什么?
- 第一次比较麻烦,要把jsp文件翻译生成java源文件
- java源文件要编译生成class字节码文件
- 然后通过class去创建servlet对象
- 然后调用servlet对象中的init方法
- 最后调用servlet对象中的service方法
- 第二次就比较快了,为什么?
- 因为第二次直接调用单例Servlet对象中的service方法
- JSP是什么?
- JSP是java程序(Jsp本质还是一个Servlet)
- JSP是:JavaServlet Page的缩写(基于java语言实现的服务端的页面)
- Servlet是JavaEE的13个子规范之一,那么JSP也是JavaEE的13个子规范之一
- JSP是一套规范。所有的WEB容器/WEB服务器都是遵循这套规范的,都是按照这套规范进行翻译
- 每一个WEB容器/WEB服务器都会内置一个JSP翻译引擎
- 对JSP进行错误调试的时候,还是需要直接打开JSP文件对应的java文件,检查java代码
- 既然JSP本质上是一个Servlet,那么JSP和Servlet有什么区别?
- 职责不同:
- Servlet的职责是什么:收集数据(Servlet的强项是做逻辑处理、业务处理、连接数据库、获取收集数据)
- JSP的职责是什么:展示数据(JSP的强项是做数据展示)
- JSP的基础语法
- 在jsp文件中直接编写的文字,都会自动被翻译到哪?
- 翻译到Servlet类的service方法的out.write("翻译到这里"),直接翻译到双引号里,被java程序当成普通字符串打印输出到浏览器
- 在JSP中编写HTML/CSS/JavaScript代码,这些代码对于JSP来说只是一个普通字符串。但是JSP把这个普通字符串一旦输出到浏览器,浏览器就会对HTML/CSS/JavaScript进行解释执行,展现一个效果
- JSP的page指令,解决响应的乱码问题
- 通过page指令设置响应的内容类型,在内容类型的后面添加:charset=utf-8
- ```jsp
<%@page contentType="text/html;charset=utf-8" %>
//表示响应的内容类型是text/html,采用的字符集是utf-8
```
- 怎么在JSP中编写java程序?
- <% java语句; %>
- 在这个符号当中编写的被视为java代码,被翻译到Servlet类的service方法内部
- 这里要细心点,时刻记住你正在方法体里写代码,什么可以写,什么不可以写
- 在service方法中编写的代码是有顺序的,方法中的代码遵循自上而下的顺序依次逐行执行
- 在同一个JSP当中<%%>可以出现多次
- <%! java语句; %>
- 在这个符号当中编写的java程序会自动翻译到service方法之外
- 这个语法很少用,为什么?
- 因为在service方法外面写静态变量和实例变量,都会存在线程安全问题。JSP就是Servlet,servlet是单例的,在多线程并发的环境下,这个静态变量和实例变量一旦有修改操作,必然会存在线程安全问题
- jsp的输出语句
- 怎么向浏览器上输出一个java变量
- ```jsp
<%
String name="jack";
out.write("name="+name);
%>
```
- 注意:以上代码中的out是JSP九大内置对象之一,可以直接拿来用。当然,必须只能在service方法中使用
- 如果向浏览器上输出的内容没有java代码,例如输出一个字符串,可以直接在jsp文件中编写,不需要写到<%%>这里
- 如果输出的内容含有java代码,这个时候可以使用以下语法格式
- <%= %> 注意:在=后面编写要输出的内容
- <%= %> 会被翻译到哪里?会被翻译成什么?
- 翻译成了这个java代码:out.print();
- 翻译到service方法当中
- 什么时候使用<%=%>输出呢?输出的内容是一个含有java的变量,输出的内容是一个动态的内容,不是一个死的字符串。如果输出是一个死的字符串,直接在JSP文件中编写即可
- 在JSP中如何编写JSP专业注释
- <%--JSP的专业注释,不会被翻译到java源代码当中--%>
-
- 使用Servlet+JSP完成oa项目的改造
- 使用Servlet处理业务,收集数据
- 使用JSP展示数据
- JSP文件的扩展名必须是xxx.jsp吗?
- jsp文件的扩展名是可以配置的,不是固定的
- 在CATALINA_HOME/conf/web.xml,这个文件当中配置jsp文件的扩展名
- ```xml
default
/
jsp
*.jsp
*.jspx
```
- javaBean其实就是java中的实体类,负责数据的封装
- JavaBean可以理解为符合某种规范
- 有无参构造方法
- 属性私有化
- 对外提供公开的set和get方法
- 实现java.io.Serializable接口
- 重写toString
- 重写hashCode和equals
- ......
- 由于javaBean符合javaBean规范,具有更强的通用性
- 当前oa系统存在一个问题,任何用户都可以访问该系统进行增删改这些危险操作,增加一个登录功能
- 实现登录功能
- 第一步:准备用户表
- t_user表当中存储的是用户的登录信息,包括用户名和密码
- 密码一般在数据库当中存储的是密文,一般不以明文的形式存储
- 第二步:准备一个登陆界面
- 登陆页面上应该有一个登录的表单,有用户名和密码输入的框
- form表单是post方式提交
- 第三步:后台有一个对应的Servlet来处理登录的请求
- 登陆成功:跳转到部门列表页面
- 登陆失败:跳转到登录失败页面
- 第四步:准备一个登陆失败的页面
- 登录功能实现了,目前存在大的最大的问题
- 这个登录目前只是一个摆设,没有任何作用,只要用户知道后端的请求路径,照样可以在不登录的情况下访问
- 这个登录没有真正起到拦截的作用
- Jsp的page指令
- 指令的作用:指导JSP的翻译引擎如何工作(指导当前的JSP翻译引擎如何翻译JSP文件)
- 指令包括哪些
- include指令:包含指令,在jsp中完成静态包含,很少用了
- taglib指令:引入标签库的指令,JSTL标签指令
- page指令:目前重点学习的一个page指令
- 指令的使用语法
- <%@指令名 属性名=属性值 属性名=属性值 属性名=属性值......%>
- 关于page指令中的有哪些属性
- ```jsp
<%@page session="false"%>
true表示启用jsp的内置对象session,没有session对象会创建
如果没有设置,默认值session="true"
session="false"表示不启用内置对象session,当前JSP页面中无法使用内置对象session
```
- ```jsp
<%@page contentType="text/json%>
contentType属性用来设置响应的内容类型
```
- ```jsp
<%@page pageEncoding="utf-8" %>
pageEncoding属性用来设置响应的字符集编码
也可以不使用这个属性,直接在contentType中一起设置
<%@page contentType="text/json;charset=utf-8"%>
```
- ```jsp
<%@page import="java.util.List,java.util.Date" %>
import语句 导包
```
- ```jsp
<%@page errorPage="/error.jsp" %>
当前页面出现异常之后,跳转到error.jsp
errorPage属性用来指定页面出现错误时的跳转位置
```
- ```jsp
<%@ page isErrorPage="true" %>
启用jsp九大内置对象之一:exception
exception内置对象就是刚刚发生的异常对象
默认值false
```
- Jsp的九大内置对象
- jakarta.servlet.jsp.PageContext -pageContext:页面作用域
- jakarta.servlet.http.HttpServletRequest -request:请求作用域
- jakarta.servlet.http.HttpSession -session:会话作用域
- jakarta.servlet.ServletContext -application:应用作用域
- pageContext、<%java代码%>等,看着不美观
- EL表达式可以算是jsp语法的一部分,EL表达式归属于jsp
- EL表达式出现在jsp中主要是
- 从某个域中取数据,然后将其转换为字符串,然后将其输出到浏览器,这就是EL表达式的功效
- 第一功效:从某个域中取数据
- 四个域
- pageContext
- request
- session
- application
- 第二功效:将取出的数据转成字符串
- 如果是一个java对象,会调用toString方法将其转为字符串
- 第三功效:将字符串输出到浏览器
- EL表达式的语法
- ${表达式}
- EL表达式的使用
- ```jsp
<%
//创建User对象
User user=new User();
user.setUsername("jack");
user.setPassword("123");
user.setAge(50);
request.setAttribute("userObj",user);
%>
<%-- 使用el表达式,取出User对象--%>
${这个位置写什么?一定是存储在某个域的name}
${userObj}
等同于java代码<%=request.getAttribute("userObj")%>
${userObj} 底层是怎么实现的?从域中取数据,然后调用user对象的toString方法,转为字符串,输出到浏览器
<%--输出User的属性--%>
${userObj.username}
使用这个方法的前提:userObj有getUsername()方法
${userObj.password}
使用这个方法的前提:userObj有getPassword()方法
${userObj.age}
使用这个方法的前提:userObj有getAge()方法
${userObj.email}
使用这个方法的前提:userObj有getEmail()方法
表达式中的.这个语法,实际上调用的是:getXXX()方法
注意:如果没有对应的get方法,会报500异常
在el表达式中不能加双引号"" 加了双引号el表达式把它当作普通字符串直接输出到浏览器
```
- 在没有指定范围的前提下,EL表达式优先从小的域取数据
- pageContext==null?<%=request.getAttribute("usernam")%>:"";
- EL表达式中取数据包括两种方式
- 第一种:.(大部分都采用这种方式)
- 第二种:[ ](如果存储的name中含有特殊字符,可以采用这种方式)
- 使用[ ]的时候,需要加双引号。[ ]没有加双引号,会别当成变量;如果加了双引号,会去找user对象的username属性
- ${user["username"]}
- 掌握使用EL表达式怎么从Map集合中取数据
- ${map.key}
- 掌握使用EL表达式怎么从数组和List集合中取数据
- ${数组[下标]}
- ${集合[下标]}
- page当中有个指令,可以忽略页面整个EL表达式,被当成普通字符串
- isELIgnored="true"
- 默认值是false
- 可以使用反斜杠 \ 忽略指定的EL表达式,被当成普通字符串
- \\${username}
- 使用EL表达式获取应用的根
- pageContext可以获得九大内置对象,但在EL表达式中没有request对象
- requestScope只代表请求范围,不代表对象
- EL表达式中有一个隐式对象:pageContext
- EL表达式中的pageContext和JSP中的pageContext是同一个对象
- 使用EL表达式获取应用的根
- ```jsp
${pageContext.request.contextPath}
等同于
<%=((HttpServletRequest)pageContext.getRequest()).getContextPath()%>
```
- 除了pageContext,EL表达式其他的隐式对象
- param:和request.getParameter(name)用法相同
- ```jsp
爱好:${param.aihao}
爱好:<%=request.getParameter("aihao")%>
```
- paramValues:和request.getParameterValues(name)用法相同
- ```jsp
一位数组:${paramValues.aihao}
一位数组:<%=request.getParameterValues("aihao")%>
获取一维数组中的值:${paramValues.aihao[0]}、${paramValues.aihao[1]}、${paramValues.aihao[2]}
```
- initParam:获取ServletContext初始化参数信息
- ServletContext是Servlet上下文对象,对应JSP九大内置对象之一:application
- ```jsp
String a=application.getInitParameter("pageSize");
String b=application.getInitParameter("pageNum");
pageSize:${initParam.pageSize}
pageNum:${initParam.pageNum}
```
- ......(不是重点)
- EL表达式的运算符
- 算数运算符:+、-、*、/、%
- 在EL表达式中+号不会做字符串拼接,转不成数字会报错
- ```jsp
${3+20}
-> 23
${3+"20"}
-> 23
```
- 关系运算符:==、!=、>、 >=、 < 、<= 、eq
- EL表达式中的 == 、eq、!= 调用了equals方法
- 逻辑运算符:!、 &&、 ||、 not、 and、 or
- !和not相同用法
- 条件运算符:?:
- ${empty param.username?"对不起,用户名不能为空":"admin"}
- 取值运算符:[ ]、.
- . 如果是作用在对象上,这个运算符后面的的属性必须是开头字母小写,并且有getXXX()这个方法才行
- empty运算符
- 判断是否为空,为空返回true,不为空返回false
- ${empty param.username}
- ${!empty param.username}
# JSTL标签库
- 什么是JSTL标签库
- Java Standard Tag Lib(java标准的标签库)
- JSTL标签库通常结合EL表达式一起使用,目的是让jsp中的java代码消失
- 标签是写在jsp中,但实际上最终还是要执行java程序(java程序在jar包中)
- 使用JSTL标签库的步骤
- 第一步:引入JSTL标签库的jar包
- tomcat10之后引入的jar包是:
- jakarta.servlet.jsp.jstl-2.0.0.jar
- jakarta.servlet.jsp.jstl-api-2.0.0.jar
- 在IDEA中怎么引入
- 在WEB-INF下新建lib,然后将jar包拷贝到lib当中。然后将其add as lib...
- 第二步:在jsp中引入要使用的标签库(使用taglib指令引入标签库)
- JSTL提供了很多种标签,你要引入哪个标签?
- 重点掌握核心标签库
- ```jsp
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
prefix="" 这里随便起个名字,核心标签库,大家默认叫做c
```
- 第三步:在需要的位置使用标签即可,表面使用的是标签,底层还是使用java程序
- JSTL标签的原理
- <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
- 以上uri后面的路径实际上指向了一个xxx.tld文件
- tld文件是一个xml配置文件
- 在tld文件中描述了”标签“和”java类“之间的关系
- 以上核心标签库对应的tld文件是:c.tld文件,它在哪?
- 在jakarta.servlet.jsp.jstl-2.0.0.jar里面META-INF目录下,有一个c.tld文件
- 源码分析
- ```xml
对标签的描述
catch 标签的名字
org.apache.taglibs.standard.tag.common.core.CatchTag 标签对应的java类
JSP 标签体当中可以出现的内容
对属性的描述
var 属性的名字
false false表示该属性不是必须的,true表示该属性是必须的
false 这个描述说明了该属性是否支持EL表达式,false表示支持
```
- jstl中核心标签库core中的有哪些常用的标签?
- ```jsp
<%--items="要迭代的集合" var="迭代的每一项"--%>
id:${stu.id},name:${stu.name}
<%--var用来指定循环中的变量--%>
<%--begin开始--%>
<%--end结束--%>
<%--step步长--%>
<%--底层实际上,会将var存储在pageContext域中--%>
${i}
<%--varStatus表示var的状态对象,这里是个java对象,这个java对象代表了var的状态--%>
<%--varStatus这个状态对象有个count属性,count属性从1递增,主要用于编号/序号,可以直接使用--%>
编号:${varStatus.count},id:${stu.id},name:${stu.name}
```
- ```jsp
<%--text属性是必须,text属性支持EL表达式,text属性值是boolean类型--%>
<%--if标签还有var属性和scope属性,不是必须的,
scope用来指定var的存储域 page(pageContext域) request(request域) session(session域) application(application域)--%>
<%--将var中的v存储到request域--%>
欢迎你${param.username}
<%--通过EL表达式将request域中的v取出 v变量值存储的是text中的值--%>
${v}
```
- ```jsp
<%--和if else if else用法一样--%>
青少年
青年
中年
老年
```
## 改造oa
- 使用什么技术改造?
- 使用Servlet+JSP+EL表达式+JSTL标签库,进行改造
- 在前端HTML代码中,有一个标签叫做base标签,在这个标签中可以设置整个网页的基础路径
- 这不是java语法,也不是jsp语法,是HTML中的一个语法,base标签通常出现在head标签中
- ```
```
- 在当前页面中,凡是路径没有以”/“开始,都会自动将base中的路径添加到这些路径之前
- ```html
等同于
```
- 需要注意:在js代码中的路径,保险起见,最好不要依赖base路径。js代码最好写上全路径
- ```javascript
```
# Filter过滤器
- 当前oa项目存在什么问题?
- DeptServlet、UserServlet、EmpServlet、OtherServlet都是处理相关的业务,在执行Servlet之前都要判断用户是否登录。如果登录了,可以继续操作,如果没有登录,需要用户登录。这段判断用户是都登录的代码是固定的,并且每一个Servlet都要用到,代码没有得到复用。并且,设置请求和响应编码的代码也没有得到复用。
- 可以使用Servlet规范中的过滤器Filter来解决这个问题
- Filter是什么?有什么用?执行原理是什么?
- Filter是过滤器
- Filter可以在Servlet这个目标程序执行前添加代码,也可以在目标Servlet执行之后添加代码,之前之后都可以添加过滤器规则
- 一般情况下,都是在过滤器中编写公共代码
- 一个过滤器怎么写呢?
- 第一步:编写一个java类实现一个接口:jakarta.servlet.Filter。并且实现这个接口中所有方法
- init方法:在Filter对象第一次被创建之后调用,并且只调用一次
- doFilter方法:只要用户发送一次请求,则执行一次。发送N次,执行N次。这个方法中编写过滤规则
- destory方法:在Filter对象被销毁之前调用,并且只调用一次
- 第二步:在web.xml文件中对Filter进行配置,这个配置和Servlet很像
- ```xml
filter1
com.bjpoweinode.javaweb.servlet.Filter1
filter1
*.do
```
- 或者使用注解:@WebFilter({"*.do"})
- 注意:
- Servlet对象默认情况下,在服务器启动的时候不会创建对象
- Filter对象默认情况下,在服务器启动的时候会新建对象
- Servlet是单例的,Filter也是单例的(单实例)
- 目标程序Servlet是否执行,取决于两个条件
- 第一:在过滤器中是否编写了:chain.doFilter(request,response);代码
- 第二:用户发送的请求路径是否和Servlet的请求路径一致
- chain.doFilter(request,response); 这行代码的作用
- 执行下一个过滤器,如果下面没有过滤器了,执行最终的Servlet
- 注意:Filter的优先级,天生就比Servlet高
- /a.do 对应一个Filter,也对应一个Servlet,那么一定是先执行Filter再执行Servlet
- 关于Filter的配置路径
- /a.do、/b.do、/dept/add。这些配置方式都是精确匹配
- /* 匹配所有路径
- *.do 后缀匹配。不要以/开始
- /dept/* 前缀匹配
- 在web.xml文件中,Filter的执行顺序是什么?
- 依靠filter-mapping标签的配置位置,越靠上优先级越高
- 过滤器的调用顺序,遵循栈数据结构
- 使用@WebFilter的时候,Filter的执行顺序是怎么样的?
- 执行顺序:比较Filter这个类名的字典顺序
- 比如:FilterA和FilterB,则先执行FilterA
- 比如:Filter1和Filter2,则先执行Filter1
- Filter的生命周期?
- Filter的生命周期和Servlet什么周期一致
- 唯一区别是:Filter默认情况下,在服务器启动阶段就实例化,Servlet不会
- Filter这里有一个设计模式
- 责任链设计模式
- 过滤器最大的优点:
- 在程序编译阶段不会确认调用顺序。因为Filter的调用顺序是配置到web.xml文件中,只要修改web.xml配置文件中的filter-mapping顺序就可以调整Filter的执行顺序。显然Filter的执行顺序是在程序运行阶段动态组合的。那么这种设计模式被称为责任链设计模式
- 责任链设计模式最大的核心思想:
- 在程序运行阶段,动态组合程序的调用顺序
- 使用过滤器改造oa
# Listener监听器
- 什么是监听器?
- 监听器是Servlet规范中的一员,就像Filter一样,Filter也是Servlet规范中的一员
- 在Servlet中,所以的监听器接口都是以“Listener”结尾
- 监听器有什么用?
- 监听器实际上是Servlet规范留给我们javaweb程序员的特殊时机
- 特殊的时机如果想执行这段代码,你需要想到使用对应的监听器
- Servlet规范中提供了哪些监听器?
- jakarta.servlet包下
- ServletContextListener
- ServletContextAttributeListener
- ServletRequestListener
- ServletRequestAttributeListener
- jakarta.servlet.http包下
- HttpSessionListener
- HttpSessionAttributeListener
- 该监听器需要使用@WebListener注解进行标记
- 该监听器监听什么?是session域中所有数据的变化,只要数据变化,则执行相应的方法,只要检测点在session域对象上
- HttpSessionBindingListener
- 该监听器不需要使用@WebListener进行标记
- 假如你使用User类实现了监听器,那么User类在被放入session的时候触发Bind事件,User对象从session域中删除的时候,触发unbind事件
- 假设Customer类没有实现该监听器,那么Customer对象放入session域或从session域中删除的时候,不会触发bind事件
- HttpSessionIdListener
- session中的id发生改变时,监听器中唯一一个方法就会被调用
- HttpSessionActivationListener
- 监听session的钝化和活化
- 钝化:session对象从内存存储到硬盘
- 活化:session对象从硬盘恢复到内存中
- 实现一个监听器的步骤:以ServletConextListener为例
- 第一步:编写一个类实现ServletContextListener接口,并且实现里面的方法
- ```java
//ServletContext对象被创建时调用
public void contextInitialized(ServletContextEvent sce) {}
//ServletContext对象被销毁时调用
public void contextDestroyed(ServletContextEvent sce) {}
```
- 第二步:在web.xml文件中对ServletContextListener进行配置
- ```xml
com.bjpowernode.javaweb.listener.MyServletContextListerner
```
- 也可以不适应配置文件,使用注解:@WebListener
- 注意:所以监听器中的方法都是不需要javaweb程序员调用,由服务器负责调用
- 当某个特殊的事件发生之后,被服务器调用
- 关于Attribute的监听,以HttpSessionAttributeListener为例
- ```java
//向session域中添加属性时监听
public void attributeAdded(HttpSessionBindingEvent se) {}
//向session域中替换属性值时监听
public void attributeReplaced(HttpSessionBindingEvent se) {}
//向session域中删除属性时监听
public void attributeRemoved(HttpSessionBindingEvent se) {}
```
- HttpSessionBindingListener
- ```java
//向session域中添加数据时触发事件
public void valueBound(HttpSessionBindingEvent event) {}
//从session域中删除数据时触发事件
public void valueUnbound(HttpSessionBindingEvent event) {}
```
- 思考一个业务场景:
- 请编写一个功能,记录网站实时在线的用户的个数
- 我们可以通过服务器端有没有分配session对象统计人数,因为一个session代表一个用户,有一个session就代表一个用户。如果你采用这种逻辑去实现的话,session有多少个,在线用户就有多少个。这种方式的话:HttpSessionListener够用了,session对象只要新建,就count++,然后将count存储到ServletContext域当中,在线显示在线人数
- 业务发生改变,只统计登录的用户在线数量,这个怎么办?
- session.setAttribute("user",userObj);
- 用户登录的标志是什么?session中曾经存储过User类型的对象,那么这个时候就可以让user类型的对象实现HttppSessionBindingListener监听器,以后只要User对象存储到session域中,就count++,然后将count存储到ServletContext对象中,页面显示在线人数即可
- 实现oa项目中当前登录的在线人数
- 什么代表用户登录l?
- session.setAttribute("user",userObj); User类型的对象只要在session中存储过,表示有新用户登录
- 什么代表用户退出了?
- session.removeAttribute("user"); User类型的对象从session域中移除了
- 或者有可能是session销毁了(session超时)
# MVC架构模式
- M(Model:数据/业务)V(View:视图/展示)C(Controller:控制器)
- C:是核心,是控制器,是司令官
- 控制器调用Model处理业务
- 控制器调用View进行展示
- M:处理业务/处理数据的一个秘书
- 连接服务器处理数据/业务
- 并将处理数据返回给控制器
- V:负责也页面展示的一个秘书
- 通过jsp、html......展示
- 将展示结果返回给控制器
- DAO
- 什么是DAO?
* Data Access Object(数据访问对象)
* DAO实际上是一种设计模式,属于javaEE的设计模式之一(不是23种设计模式)
- DAO只负责数据库表的CRUD,没有人任何的业务逻辑在里面
- 没有任何业务逻辑,只负责数据库表的增删改查的对象,有一个特殊的称谓:DAO对象
- 为什么叫AccountDao
* 这是因为这个DAO负责处理t_act这种表
- 一般情况下,一张表对应一个DAO对象
- 封装数据的实体类
- 有的人也会把这种专门封装数据的对象叫:pojo对象
有的人也会把这种专门封装数据的对象叫:bean对象(javabean:咖啡豆)
有的人也会把这种专门封装数据的对象叫:领域模型对象(domain对象)
- AccountService:专门处理Account业务的一个类
- 业务类一般起名:XxxService、XxxBiz
- 一个业务对应一个方法
- MVC和三层架构
- 
- 解决事务问题
- 事务一定是在业务逻辑层控制的
- 一般是一个业务方法对应一个事务
- 使用ThreadLocal解决事务问题
- ThreadLocal源码分析
- ThreadLocal中数据以键值对存储(Thread,Connection)
- ```java
private Map map=new HashMap<>();
public void set(T obj){ //向当前线程内添加数据 Thread.currentThread()拿到当前线程对象
map.put(Thread.currentThread(),obj);
}
public T get(){ //从当前线程内获得数据
return map.get(Thread.currentThread());
}
public void remove(){ //从当前线程中移除数据
map.remove(Thread.currentThread());
}
```
- 关闭ThreadLocal中资源的时候为什么一定要使用remove方法?
- Tomcat服务器有一个线程池,这些线程都是提前创建好的,也就是说一个人用过了t1线程后,下一次可能还会用到t1,可能存在重复使用。
- 目前项目存在的缺陷?
- 在service层控制了事务,service方法中的事务控制代码看着优点别扭,以后能不能不用?
- 可以使用动态管理机制来解决这个问题。
- 目前虽然面向接口编程了,但是并没有完全解决对象和对象之间的依赖关系,怎么办?
- 可以使用spring的IOC容器来解决这个问题
# 关于web中"/"路径问题
- 直接写结论
- 加"/"是绝对路径,是**根路径**
- 不加"/"是相对路径
- 前端和后端根路径
- 前端的根路径是 http://127.0.0.1/
- 后端的根路径是 http://127.0.0.1/web_content/
- 前端:跳转发生在前端,/ 是web服务器的根路径 ,不加 / 是相对当前页面的路径 (如重定向)
后端:跳转发生在后端,/ 是web应用的根路径 ,不加 / 是相对当前页面的路径 (如请求转发)
注意:重定向的跳转发生在前端,即浏览器再次发出请求,而请求转发的跳转发生在服务器端