(尚硅谷)JavaWeb新版教程03-Tomcat-Servlet

1、CS 和 BS 的异同点

在这里插入图片描述

2、Tomcat

2.1 Tomcat 安装及运行问题解决

(1)解压:不要有中文不要有空格,尽量不要在 C 盘。
(2)目录结构说明:
在这里插入图片描述

注意:

  1. 这里用老师给提供的 8.0.42 版本的压缩包,明明自己 JAVA_HOME 环境变量用的好好的,没有配置错误,但是点 startup 就是会闪退,后来自己去官网下载了 9.0.62 版本的压缩包(高版本 Tomcat 适配高版本 JDK),重新解压缩之后不闪退了。
  2. 但是又出问题了,乱码了(因为 cmd 命令行窗口解析的时候默认的字符集设置的是 GBK,而 Tomcat 设置的默认字符集是 UTF-8),奇了怪了,然后找教程。
  3. 老师也没教我们配置 Tomcat (这里网上找了教程,配置完之后还是不能从 默认 cmd 直接打开 startup 批处理文件),所以每次打开肯定是从 bin 目录下直接点击 startup.bat 文件进行打开,或者从 bin 目录下输入 cmd 打开的命令提示符窗口输入 startup 打开之后输入“chcp 65001”配置 cmd 的字符集为 UTF-8,好了,启动成功且不乱码。
  4. 浏览器输入 “http://localhost:8080” 打开 Tomcat 页面,没毛病。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    小提示:将来我们在 IDEA 中启动 Tomcat ,如果 IDEA 卡死强关,Tomcat 不会正常退出。下次再启动 Tomcat 会因为残留进程仍然占用 8080 端口,导致新的进程无法启动。此时可以使用 shutdown.bat 结束残留进程。

参考文献:

  1. tomcat startup启动出现乱码解决方法。,这个直接修改Tomcat 字符集的方式不知道以后会不会出问题。
  2. 解决各种tomcat中文乱码问题,这里告诉我们不建议修改 Tomcat 的默认编码集。
  3. 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 向数据库添加水果操作

  1. 客户端你向服务器请求 add.html 资源
  2. 服务端从 Tomcat 中找到这个资源响应给客户端,显示一个添加数据的表单(html 中实现的,里面有两个属性 action=“add” method=“post”,表示了我们的行为和请求方法)
  3. 我们点击添加按钮(submit 属性)之后,把消息发给了一个 add 组件(其实就是一个 Java 类,这个 Java 类的名字就是 AddServlet,其实就是服务端的一个小应用程序),它可以获取用户发过来的数据,同时调用 DAO 中的方法完成添加功能,同时在控制台打印添加成功
  4. 功能一:AddServlet 组件会将用户添加的这些数据封装成一个 Http Request 对象,我可以通过 request 对象获取其中的一些信息。
  5. 功能二:上面 Servlet 调用 DAO 需要 JDBC 和数据库进行连接,然后进行增删改查操作。
    在这里插入图片描述
    具体实现步骤:
    这里篇幅太长了,参考我写的另外一篇文章,实现浏览器 - Servlet - 数据库交互操作

2.2.4 问题解决(主要是乱码问题)

(1)上面具体实现步骤之后,这里应该只会有一些乱码问题,不会再有其他问题了,如果有的话,可以查看一下尚硅谷-排错视频,很详细。

(2)点击 debug 之后发现输出的 Tomcat 部署和项目配置信息是乱码的话
在这里插入图片描述
点击 File - Settings - Editor,进行编码集的设置,如下图所示:
在这里插入图片描述
在这里插入图片描述
设置完成之后,输出信息没有乱码了。
在这里插入图片描述
(3)有时候还会碰到输出台打印信息乱码问题,servlet中System.out.println中文控制台显示乱码问题,不知道具体到底是哪里出问题了,反正先这么改吧,肯定是字符集的问题。

(4)添加到数据库的中文字符是乱码

Tomcat 8 之后设置 post 方式的编码集就可以了,get 方法不需要设置,并且设置编码这一句代码必须在所有的获取参数动作之前

public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    //post方式下,设置编码,防止中文乱码
    //需要注意的是,设置编码这一句代码必须在所有的获取参数动作之前
    request.setCharacterEncoding("UTF-8");

    String fname = request.getParameter("fname");

    String priceStr = request.getParameter("price");
    Integer price = Integer.parseInt(priceStr);

    String fcountStr = request.getParameter("fcount");
    Integer fcount = Integer.parseInt(fcountStr);

    String remark = request.getParameter("remark");

    FruitDAO fruitDAO = new FruitDAOImpl();
    boolean flag = fruitDAO.addFruit(new Fruit(0, fname, price, fcount, remark));

    System.out.println(flag ? "添加成功!" : "添加失败!");
}
  • 基于tomcat8:get方式目前不需要设置编码
  • tomcat8之前:如果是get请求发送的中文数据,转码稍微有点麻烦
    • 将字符串打散成字节数组
String fname = request.getParameter("fname");
byte[] bytes = fname.getBytes("ISO-8859-1");
  • 将字节数组按照设定的编码重新组装成字符串
fname = new String(bytes,"UTF-8");

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 相关方法(三个重要方法)

  1. javax.servlet.Servlet 接口中三个重要的方法:
  • void init(config) - 初始化方法
  • void service(request,response) - 服务方法
  • void destory() - 销毁方法
    在这里插入图片描述
  1. javax.servlet.GenericServlet 抽象类中实现了初始化和销毁方法,没有实现服务方法:
  • void service(request,response) - 仍然是抽象的
    在这里插入图片描述
  1. javax.servlet.http.HttpServlet 抽象子类中实现了这个 service 服务方法:
  • void service(request,response) - 不是抽象的,并且其中方法用的是 this.service() 表明实际执行的是子类中再重写的 service 服务方法

在这里插入图片描述

  • 如果子类中没有重写 service() 方法,那么调用的就是 HttpServlet 抽象子类中的这个方法,这时的请求从 Servlet 请求响应类经过强转变成了 HttpServlet 请求响应类
    在这里插入图片描述
    对这儿 service() 方法进行分析:
  1. String method = req.getMethod(); 获取请求的方式
  2. 各种 if - else 判断,根据请求方式不同,决定去调用不同的 doXxx 方法
  3. 在 HttpServlet 这个抽象类中,doXxx 方法实现的都差不多,以 doGet() 方法为例:
    在这里插入图片描述
    首先呢,去这个资源文件中找到 http.method_get_not_supported 这句话对应的字符串,作为一个 msg 消息,如下图所示
    在这里插入图片描述
    在这里插入图片描述
    然后执行下面这个 sendMethodNotAllowed(request, response, msg) 方法,先获取协议的类型,然后根据协议抛出一个对应的错误
    在这里插入图片描述
    注意:
    也就是说,如果我们没有自己重写 doXxx() 方法,一般情况下页面会报一个405 错误。

3.1.3 总结

  1. 继承关系: HttpServlet -> GenericServlet -> Servlet
  2. Servlet中的核心方法: init() , service() , destroy()
  3. 服务方法: 当有请求过来时,service方法会自动响应(其实是 tomcat 容器调用的)
    • 在 HttpServlet 中我们会去分析请求的方式:到底是 get、post、head 还是delete 等等
    • 然后再决定调用的是哪个 do 开头的方法
    • 那么在 HttpServlet 中这些 do 方法默认都是 405 的实现风格,要我们子类去实现对应的方法,否则默认会报405错误
  4. 因此,我们在新建 Servlet 时,我们会去考虑请求方法,从而决定重写哪个 do 方法

3.1.4 405 错误演示

代码,这里直接调用父类的 do 方法:

package com.atguigu.servlets;

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

public class Demo01Servlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}

网页展示:
在这里插入图片描述

3.2 Servlet 的生命周期

3.2.1 生命周期与三个重要方法的对应

  1. 生命周期:从出生到死亡的过程就是生命周期。对应 Servlet 中的三个方法:init(), service(), destroy()
  2. 默认情况下:
    • 第一次接收请求时出生,这个 Servlet 会进行实例化(调用构造方法)、初始化(调用init())、然后服务(调用service())
    • 从第二次请求开始,每一次都只有服务,就没有实例化和初始化的过程了
    • 当容器关闭时,其中的所有的 servlet 实例会被销毁,调用销毁方法

3.2.2 生命周期的演示

代码:通过重写这三种方法看一下 servlet 的整个生命周期过程,之前说过,如果这些方法进行了重写,调用的是重写的子类的方法

//演示Servlet的生命周期
public class Demo02Servlet extends HttpServlet {
    public Demo02Servlet(){
        System.out.println("正在实例化....");
    }

    @Override
    public void init() throws ServletException {
        System.out.println("正在初始化.....");
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("正在服务.....");
    }

    @Override
    public void destroy() {
        System.out.println("正在销毁......");
    }
}

控制台展示:
在这里插入图片描述

在网页上每一次刷新,代表重新发送一次请求,这个时候控制台输出信息只有正在服务

在这里插入图片描述
在这里插入图片描述
点击 stop 停止按钮,Tomcat 进行销毁
在这里插入图片描述
在这里插入图片描述

通过案例我们发现:

  • Servlet 实例 tomcat 只会创建一个,所有的请求都是这个实例去响应。
  • 默认情况下,第一次请求时,tomcat 才会去实例化,初始化,然后再服务

这样的好处是什么: 提高系统的启动速度,只有创建对象,就是说有请求,才创建 servlet 对象
这样的缺点是什么: 第一次请求时,耗时较长

结论:

  • 如果需要提高系统的启动速度,当前默认情况就是这样。
  • 如果需要提高响应速度,我们应该设置 Servlet 的初始化时机。

3.3.3 Servlet 的初始化时机

  • 默认是第一次接收请求时,实例化,初始化
  • 我们可以通过修改 web.xml 文件中 Servlet 的 <load-on-startup> 标签来设置 servlet 启动的先后顺序,数字越小,启动越靠前,最小值0
    (随着 Tomcat 的启动而进行实例化和初始化操作)
    修改配置文件:
<servlet>
    <servlet-name>Demo02Servlet</servlet-name>
    <servlet-class>com.atguigu.servlets.Demo02Servlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

启动控制台展示,从下面可以看出,在部署 Tomcat 之前就进行了实例化和初始化:
在这里插入图片描述

3.3.4 Servlet 线程不安全问题

Servlet 在容器中是:单例的、多线程情况下是线程不安全的

  1. 单例:所有的请求都是同一个实例去响应
  2. 线程不安全:
    (1)一个线程需要根据这个实例中的某个成员变量值去做逻辑判断。
    (2)但是在中间某个时机,另一个线程改变了这个成员变量的值,从而导致第一个线程的执行路径发生了变化
    在这里插入图片描述

注意:

  1. 尽量的不要在 servlet 中定义成员变量。
  2. 如果不得不定义成员变量,那么不要去:①不要去修改成员变量的值;②不要去根据成员变量的值做一些逻辑判断。

3.3 Http 协议

3.3.1 Http 介绍

  1. HTTP:Hyper Text Transfer Protocol超文本传输协议
  2. HTTP最大的作用就是确定了请求和响应数据的格式。
  3. 浏览器发送给服务器的数据:请求报文;服务器返回给浏览器的数据:响应报文。
  4. 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 都是相同的:

//演示Session
public class Demo03Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取session,如果获取不到,则创建一个新的
        HttpSession session = request.getSession() ;
        System.out.println("session ID : " + session.getId());
    }
}

控制台输出展示:
在这里插入图片描述

3.4.4 session 保存作用域

  • session 保存作用域是和具体的某一个 session 对应的
  • 常用的API:
    void session.setAttribute(k,v)
    Object session.getAttribute(k)
    void removeAttribute(k)

也就是说对于同一个会话,在一次会话持续时间内,该对象对保存作用域中的信息是可以任意获取的,但是不同的会话是获取不到的
在这里插入图片描述
代码,这里用两个组件,一个用来设置保存作用域中的信息,一个用来获取信息:

//演示向HttpSession保存数据
public class Demo04Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.getSession().setAttribute("uname","lina");
    }
}
//演示从http session 保存作用域中去获取uname
public class Demo05Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Object unameObj = request.getSession().getAttribute("uname");
        System.out.println(unameObj);
    }
}

网页展示(开同一个浏览器的情况),观察控制台输出信息:
在这里插入图片描述
在这里插入图片描述
网页展示(开不同浏览器的情况),观察控制台输出信息:
在这里插入图片描述
在这里插入图片描述

3.5 服务器内部转发以及客户端重定向

3.5.1 服务器内部转发

request.getRequestDispatcher("...").forward(request,response);

  • 一次请求响应的过程,对于客户端而言,内部经过了多少次转发,客户端是不知道的
  • 地址栏没有变化
    在这里插入图片描述

3.5.2 客户端重定向

response.sendRedirect("....");

  • 两次请求响应的过程。客户端肯定知道,请求的 URL 有变化
  • 地址栏有变化

在这里插入图片描述

3.5.2 代码及网页展示

Demo06Servlet 用来当作第一次请求的页面

//演示服务器端内部转发以及客户端重定向
public class Demo06Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("demo06......");
        //服务器端内部转发
        request.getRequestDispatcher("demo07").forward(request,response);
        //客户端重定向
//        response.sendRedirect("demo07");
    }
}

Demo07Servlet 用来当作重定向或者内部转发的页面

public class Demo07Servlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("demo07......");
    }
}

(1)内部转发页面展示
在这里插入图片描述
控制台输出信息:
在这里插入图片描述
(2)重定向页面展示

确定键按下之前:
在这里插入图片描述
确定键按下之后,地址栏发生变化:
在这里插入图片描述
控制台输出信息:
在这里插入图片描述

结论:不管是内部转发还是重定向,两个组件都会被访问到,具体不同的地方是内部转发用户是不知道的,而重定向用户是知道的。

3.6 Thymeleaf - 视图模板技术

把真实的数据库数据显示到界面上的做视图渲染的一种技术。

  1. 客户端发送 index 请求给服务器端
  2. 服务端的 IndexServlet 组件响应并调用 service() 方法,可能是调用其中的 doGet() 方法
  3. IndexServlet 组件调用 FruitDAOImpl 实现类里面的方法,这个实现类是基于 BaseDAO 这个最基本的抽象类的
  4. 然后 BaseDAO 这个类向数据库请求连接,并且实现增删改查数据库的一些数据
  5. 数据库将数据再传给 BaseDAO 类,再传给 FruitDAOImpl 实现类,再传给 IndexServlet 组件
  6. 这时一共有 index.html 这个静态界面和 fruitList 这一组集合数据,我们要在这个静态页面上加载 java 内存中的 fruitList 数据,这个过程称为渲染(render),Thymeleaf 就是一种做视图渲染的技术
    在这里插入图片描述

4、目前见到的 http 响应类型

200 : 正常响应
404 : 找不到资源,有可能是路径输错了
405 : 请求方式不支持,doXxx() 方法没有重写的话就会报这个错误
500 : 服务器内部错误,有很多种原因,数据库连接不正确,地址映射不正确等等

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值