目录
- 1、CS 和 BS 的异同点
- 2、Tomcat
- 2.1 Tomcat 安装及运行问题解决
- 2.2 Tomact 新建项目-部署-运行-访问
- 2.2.1 Tomcat 是什么?
- 2.2.2 部署 Tomcat 的两种方法
- 2.2.3 实现浏览器通过 AddServlet 向数据库添加水果操作
- 2.2.4 问题解决(主要是乱码问题)
- 3、Servlet 入门
- 3.1 Servlet 的继承关系 - 重点查看的是服务方法(service())
- 3.1.1 继承关系
- 3.1.2 相关方法(三个重要方法)
- 3.1.3 总结
- 3.1.4 405 错误演示
- 3.2 Servlet 的生命周期
- 3.2.1 生命周期与三个重要方法的对应
- 3.2.2 生命周期的演示
- 3.3.3 Servlet 的初始化时机
- 3.3.4 Servlet 线程不安全问题
- 3.3 Http 协议
- 3.3.1 Http 介绍
- 3.3.2 请求报文
- 3.3.3 响应报文
- 3.4 Session 会话
- 3.4.1 Http 是无状态的
- 3.4.2 会话跟踪技术
- 3.4.3 常用的API
- 3.4.4 会话代码和网页展示
- 3.4.4 session 保存作用域
- 3.5 服务器内部转发以及客户端重定向
- 3.5.1 服务器内部转发
- 3.5.2 客户端重定向
- 3.5.2 代码及网页展示
- 3.6 Thymeleaf - 视图模板技术
- 4、目前见到的 http 响应类型
1、CS 和 BS 的异同点
2、Tomcat
2.1 Tomcat 安装及运行问题解决
(1)解压:不要有中文不要有空格,尽量不要在 C 盘。
(2)目录结构说明:
注意:
- 这里用老师给提供的 8.0.42 版本的压缩包,明明自己 JAVA_HOME 环境变量用的好好的,没有配置错误,但是点 startup 就是会闪退,后来自己去官网下载了 9.0.62 版本的压缩包(高版本 Tomcat 适配高版本 JDK),重新解压缩之后不闪退了。
- 但是又出问题了,乱码了(因为 cmd 命令行窗口解析的时候默认的字符集设置的是 GBK,而 Tomcat 设置的默认字符集是 UTF-8),奇了怪了,然后找教程。
- 老师也没教我们配置 Tomcat (这里网上找了教程,配置完之后还是不能从 默认 cmd 直接打开 startup 批处理文件),所以每次打开肯定是从 bin 目录下直接点击 startup.bat 文件进行打开,或者从 bin 目录下输入 cmd 打开的命令提示符窗口输入 startup 打开,之后输入“chcp 65001”配置 cmd 的字符集为 UTF-8,好了,启动成功且不乱码。
- 浏览器输入 “http://localhost:8080” 打开 Tomcat 页面,没毛病。
小提示:将来我们在 IDEA 中启动 Tomcat ,如果 IDEA 卡死强关,Tomcat 不会正常退出。下次再启动 Tomcat 会因为残留进程仍然占用 8080 端口,导致新的进程无法启动。此时可以使用 shutdown.bat 结束残留进程。
参考文献:
- tomcat startup启动出现乱码解决方法。,这个直接修改Tomcat 字符集的方式不知道以后会不会出问题。
- 解决各种tomcat中文乱码问题,这里告诉我们不建议修改 Tomcat 的默认编码集。
- cmd更换默认编码,那咱们就每次运行 startup 之前设置一下编码集呗。
2.2 Tomact 新建项目-部署-运行-访问
2.2.1 Tomcat 是什么?
Client(客户端)向 Server(服务端)请求一个资源,这个资源其实是放在Tomcat 里面的。
Tomcat 其实就是一个 Web Container(一个免费、小巧、性能稳定的Web 容器),我们可以在容器里安装很多项目,我们把一个项目放到 Tomcat 容器的过程我们叫 deploy(部署),容器里面的这个项目(baidu)其实就是一个 context root(不同项目的 context root 是不一样的)
2.2.2 部署 Tomcat 的两种方法
第一种方法(原始方法):在 tomcat 安装文件夹 apache-tomcat-9.0.62\webapps
下,新建一个文件夹例如 baidu
,然后在该文件夹下再新建 WEB-INF
(必须全大写,一个字母和符号都不能少),然后把之前做的网页粘贴过来,贴到 baidu
文件夹下,然后启动页面输入 http://localhost:8080/baidu/demo09.html
,成功出现对应的页面。
注意:这里使用的是网络访问,和直接点击 demo09.html 打开页面是不同的,那样是本地访问。第二种方法(通过 IDEA 创建一个新项目):这里我和老师的 idea 版本不一样,如果是 idea2021 版本的参考Intellij IDEA2021.1创建Java web项目(超详细),其中配置 Web 容器那一步,配置 tomcat 所在路径,选择文件夹到安装目录那一步就可以了,如下图所示:
2.2.3 实现浏览器通过 AddServlet 向数据库添加水果操作
- 客户端你向服务器请求 add.html 资源
- 服务端从 Tomcat 中找到这个资源响应给客户端,显示一个添加数据的表单(html 中实现的,里面有两个属性 action=“add” method=“post”,表示了我们的行为和请求方法)
- 我们点击添加按钮(submit 属性)之后,把消息发给了一个 add 组件(其实就是一个 Java 类,这个 Java 类的名字就是 AddServlet,其实就是服务端的一个小应用程序),它可以获取用户发过来的数据,同时调用 DAO 中的方法完成添加功能,同时在控制台打印添加成功
- 功能一:AddServlet 组件会将用户添加的这些数据封装成一个 Http Request 对象,我可以通过 request 对象获取其中的一些信息。
- 功能二:上面 Servlet 调用 DAO 需要 JDBC 和数据库进行连接,然后进行增删改查操作。
具体实现步骤:
这里篇幅太长了,参考我写的另外一篇文章,实现浏览器 - Servlet - 数据库交互操作。
2.2.4 问题解决(主要是乱码问题)
(1)上面具体实现步骤之后,这里应该只会有一些乱码问题,不会再有其他问题了,如果有的话,可以查看一下尚硅谷-排错视频,很详细。
(2)点击 debug 之后发现输出的 Tomcat 部署和项目配置信息是乱码的话
点击 File - Settings - Editor,进行编码集的设置,如下图所示:
设置完成之后,输出信息没有乱码了。
(3)有时候还会碰到输出台打印信息乱码问题,servlet中System.out.println中文控制台显示乱码问题,不知道具体到底是哪里出问题了,反正先这么改吧,肯定是字符集的问题。
(4)添加到数据库的中文字符是乱码
Tomcat 8 之后设置 post 方式的编码集就可以了,get 方法不需要设置,并且设置编码这一句代码必须在所有的获取参数动作之前
- 基于tomcat8:get方式目前不需要设置编码
- tomcat8之前:如果是get请求发送的中文数据,转码稍微有点麻烦
- 将字符串打散成字节数组
- 将字节数组按照设定的编码重新组装成字符串
3、Servlet 入门
在整个Web应用中,Servlet主要负责处理请求、协调调度功能。我们可以把Servlet称为Web应用中的『控制器』。
3.1 Servlet 的继承关系 - 重点查看的是服务方法(service())
3.1.1 继承关系
- javax.servlet.Servlet 接口
- javax.servlet.GenericServlet 抽象类
- javax.servlet.http.HttpServlet 抽象子类
下面是他们三个接口或者类的各自所属的包以及继承实现关系:
3.1.2 相关方法(三个重要方法)
- javax.servlet.Servlet 接口中三个重要的方法:
- void init(config) - 初始化方法
- void service(request,response) - 服务方法
- void destory() - 销毁方法
- javax.servlet.GenericServlet 抽象类中实现了初始化和销毁方法,没有实现服务方法:
- void service(request,response) - 仍然是抽象的
- javax.servlet.http.HttpServlet 抽象子类中实现了这个 service 服务方法:
- void service(request,response) - 不是抽象的,并且其中方法用的是 this.service() 表明实际执行的是子类中再重写的 service 服务方法
- 如果子类中没有重写 service() 方法,那么调用的就是 HttpServlet 抽象子类中的这个方法,这时的请求从 Servlet 请求响应类经过强转变成了 HttpServlet 请求响应类
对这儿 service() 方法进行分析:
- String method = req.getMethod(); 获取请求的方式
- 各种 if - else 判断,根据请求方式不同,决定去调用不同的 doXxx 方法
- 在 HttpServlet 这个抽象类中,doXxx 方法实现的都差不多,以 doGet() 方法为例:
首先呢,去这个资源文件中找到http.method_get_not_supported
这句话对应的字符串,作为一个 msg 消息,如下图所示
然后执行下面这个sendMethodNotAllowed(request, response, msg)
方法,先获取协议的类型,然后根据协议抛出一个对应的错误
注意:
也就是说,如果我们没有自己重写 doXxx() 方法,一般情况下页面会报一个405 错误。
3.1.3 总结
- 继承关系: HttpServlet -> GenericServlet -> Servlet
- Servlet中的核心方法: init() , service() , destroy()
- 服务方法: 当有请求过来时,service方法会自动响应(其实是 tomcat 容器调用的)
- 在 HttpServlet 中我们会去分析请求的方式:到底是 get、post、head 还是delete 等等
- 然后再决定调用的是哪个 do 开头的方法
- 那么在 HttpServlet 中这些 do 方法默认都是 405 的实现风格,要我们子类去实现对应的方法,否则默认会报405错误
- 因此,我们在新建 Servlet 时,我们会去考虑请求方法,从而决定重写哪个 do 方法
3.1.4 405 错误演示
代码,这里直接调用父类的 do 方法:
网页展示:
3.2 Servlet 的生命周期
3.2.1 生命周期与三个重要方法的对应
- 生命周期:从出生到死亡的过程就是生命周期。对应 Servlet 中的三个方法:init(), service(), destroy()
- 默认情况下:
- 第一次接收请求时出生,这个 Servlet 会进行实例化(调用构造方法)、初始化(调用init())、然后服务(调用service())
- 从第二次请求开始,每一次都只有服务,就没有实例化和初始化的过程了
- 当容器关闭时,其中的所有的 servlet 实例会被销毁,调用销毁方法
3.2.2 生命周期的演示
代码:通过重写这三种方法看一下 servlet 的整个生命周期过程,之前说过,如果这些方法进行了重写,调用的是重写的子类的方法
控制台展示:
在网页上每一次刷新,代表重新发送一次请求,这个时候控制台输出信息只有正在服务
点击 stop 停止按钮,Tomcat 进行销毁
通过案例我们发现:
- Servlet 实例 tomcat 只会创建一个,所有的请求都是这个实例去响应。
- 默认情况下,第一次请求时,tomcat 才会去实例化,初始化,然后再服务
这样的好处是什么: 提高系统的启动速度,只有创建对象,就是说有请求,才创建 servlet 对象
这样的缺点是什么: 第一次请求时,耗时较长
结论:
- 如果需要提高系统的启动速度,当前默认情况就是这样。
- 如果需要提高响应速度,我们应该设置 Servlet 的初始化时机。
3.3.3 Servlet 的初始化时机
- 默认是第一次接收请求时,实例化,初始化
- 我们可以通过修改 web.xml 文件中 Servlet 的 <load-on-startup> 标签来设置 servlet 启动的先后顺序,数字越小,启动越靠前,最小值0
(随着 Tomcat 的启动而进行实例化和初始化操作)
修改配置文件:
启动控制台展示,从下面可以看出,在部署 Tomcat 之前就进行了实例化和初始化:
3.3.4 Servlet 线程不安全问题
Servlet 在容器中是:单例的、多线程情况下是线程不安全的
- 单例:所有的请求都是同一个实例去响应
- 线程不安全:
(1)一个线程需要根据这个实例中的某个成员变量值去做逻辑判断。
(2)但是在中间某个时机,另一个线程改变了这个成员变量的值,从而导致第一个线程的执行路径发生了变化
注意:
- 尽量的不要在 servlet 中定义成员变量。
- 如果不得不定义成员变量,那么不要去:①不要去修改成员变量的值;②不要去根据成员变量的值做一些逻辑判断。
3.3 Http 协议
3.3.1 Http 介绍
- HTTP:Hyper Text Transfer Protocol超文本传输协议。
- HTTP最大的作用就是确定了请求和响应数据的格式。
- 浏览器发送给服务器的数据:请求报文;服务器返回给浏览器的数据:响应报文。
- Http是无状态的
3.3.2 请求报文
请求包含三个部分: 1.请求行 ; 2.请求消息头 ; 3.请求主体
- 请求行包含是三个信息: 1. 请求的方式 ; 2.请求的URL(资源地址);3.请求的协议版本(一般都是 HTTP1.1)
- 请求消息头中包含了很多客户端需要告诉服务器的信息,比如:我的浏览器型号、版本、我能接收的内容的类型、我给你发的内容的类型、内容的长度等等
- 请求体,三种情况
get 方式,没有请求体,但是有一个 queryString
post 方式,有请求体,form data
json 格式,有请求体,request payload
3.3.3 响应报文
响应也包含三个部分: 1. 响应行 ; 2.响应头 ; 3.响应体
- 响应行包含三个信息:1.协议版本;2.响应状态码(200);3.响应状态(ok)
- 响应头:包含了服务器的信息;服务器发送给浏览器的信息(内容的媒体类型、编码、内容长度等)
- 响应体:响应的实际内容(比如请求 add.html 页面时,响应的内容就是<form…)
3.4 Session 会话
3.4.1 Http 是无状态的
- HTTP 无状态 :服务器无法判断这两次请求是同一个客户端发过来的,还是不同的客户端发过来的
- 无状态带来的现实问题:第一次请求是添加商品到购物车,第二次请求是结账;如果这两次请求服务器无法区分是同一个用户的,那么就会导致混乱
- 通过会话跟踪技术来解决无状态的问题。
3.4.2 会话跟踪技术
- 客户端第一次发请求给服务器,服务器获取 session,获取不到,则创建新的,然后响应给客户端
- 下次客户端给服务器发请求时,会把 sessionID 带给服务器,那么服务器就能获取到了,那么服务器就判断这一次请求和上次某次请求是同一个客户端,从而能够区分开客户端
3.4.3 常用的API
创建会话的 API:
- request.getSession() -> 获取当前的会话,没有则创建一个新的会话
- request.getSession(true) -> 效果和不带参数相同
- request.getSession(false) -> 获取当前会话,没有则返回 null,不会创建新的
其他常用 API:
- session.getId() -> 获取 sessionID
- session.isNew() -> 判断当前 session 是否是新的
- session.getMaxInactiveInterval() -> session 的非激活间隔时长,默认1800秒
- session.invalidate() -> 强制性让会话立即失效
3.4.4 会话代码和网页展示
下面代码是获取会话 ID 的,对于同一个浏览器来说,在一次会话时间内所获取到的 session ID 都是相同的:
控制台输出展示:
3.4.4 session 保存作用域
- session 保存作用域是和具体的某一个 session 对应的
- 常用的API:
void session.setAttribute(k,v)
Object session.getAttribute(k)
void removeAttribute(k)
也就是说对于同一个会话,在一次会话持续时间内,该对象对保存作用域中的信息是可以任意获取的,但是不同的会话是获取不到的
代码,这里用两个组件,一个用来设置保存作用域中的信息,一个用来获取信息:
网页展示(开同一个浏览器的情况),观察控制台输出信息:
网页展示(开不同浏览器的情况),观察控制台输出信息:
3.5 服务器内部转发以及客户端重定向
3.5.1 服务器内部转发
request.getRequestDispatcher("...").forward(request,response);
- 一次请求响应的过程,对于客户端而言,内部经过了多少次转发,客户端是不知道的
- 地址栏没有变化
3.5.2 客户端重定向
response.sendRedirect("....");
- 两次请求响应的过程。客户端肯定知道,请求的 URL 有变化
- 地址栏有变化
3.5.2 代码及网页展示
Demo06Servlet 用来当作第一次请求的页面
Demo07Servlet 用来当作重定向或者内部转发的页面
(1)内部转发页面展示
控制台输出信息:
(2)重定向页面展示确定键按下之前:
确定键按下之后,地址栏发生变化:
控制台输出信息:
结论:不管是内部转发还是重定向,两个组件都会被访问到,具体不同的地方是内部转发用户是不知道的,而重定向用户是知道的。
3.6 Thymeleaf - 视图模板技术
把真实的数据库数据显示到界面上的做视图渲染的一种技术。
- 客户端发送 index 请求给服务器端
- 服务端的 IndexServlet 组件响应并调用 service() 方法,可能是调用其中的 doGet() 方法
- IndexServlet 组件调用 FruitDAOImpl 实现类里面的方法,这个实现类是基于 BaseDAO 这个最基本的抽象类的
- 然后 BaseDAO 这个类向数据库请求连接,并且实现增删改查数据库的一些数据
- 数据库将数据再传给 BaseDAO 类,再传给 FruitDAOImpl 实现类,再传给 IndexServlet 组件
- 这时一共有 index.html 这个静态界面和 fruitList 这一组集合数据,我们要在这个静态页面上加载 java 内存中的 fruitList 数据,这个过程称为渲染(render),Thymeleaf 就是一种做视图渲染的技术
4、目前见到的 http 响应类型
200 : 正常响应
404 : 找不到资源,有可能是路径输错了
405 : 请求方式不支持,doXxx() 方法没有重写的话就会报这个错误
500 : 服务器内部错误,有很多种原因,数据库连接不正确,地址映射不正确等等