Servlet基本介绍

访问出错怎么办

出现 400

你要访问的资源在服务器不存在

image-20220531142826882

这是正确的路径,

  1. 少写了第一级路径(Context Path)

image-20220531143021691

  1. 少写了第二级路径(Servlet Path)

image-20220531143234390

  1. Servlet Path 写的和 URL 不匹配

image-20220531143333111

image-20220531143348352

  1. web.xml 写错了

此时将web.xml清空,再次运行

image-20220531143456758

以上就是出现404的可能了,如果在测试的时候,发现404的错误,可以根据尚上面四点对代码进行检查

出现 405

405 表示对应的 HTTP 请求方法没有实现

image-20220531143837175

image-20220531143827466

啥时候浏览器发出的是GET请求?

  1. 直接在地址栏里,输入URL
  2. 通过a标签进行跳转
  3. 通过img/link/script…
  4. 通过form表单, method指定为GET
  5. 通过ajax , type指定为GET

啥时候浏览器发出的是GET请求?

  1. 通过form表单, method指定为POST
  2. 通过ajax , type指定为POST

因为我们是通过在地址栏中直接输入URL,所以此时浏览器发出的是GET请求,但是在代码中,只有处理POST的代码,此时会出现405的异常

如果super没有注释掉也会出现405异常

image-20220531144430333

出现 500

500是一个非常高频的错误,

5开头的错误,是服务器出现了问题,

一般500就以为着服务器代码里抛出异常了,并且没有被处理,异常一直抛到tomcat

如果代码出现异常,可以被catch捕获到,如果catch没有捕获到(类型不匹配/压根没有catch),异常会沿着调用栈,向上传递

image-20220531144843390

image-20220531144851435

出现500会告诉我们代码的哪个部分出现问题,很容易找到错误并且解决它

出现 “空白页面”

如果不给响应设置任何内容,就会出现空白页面

image-20220531145332460

image-20220531145338461

出现 "无法访问此网站“

如果将tomcat关闭,此时tomcat处于关闭状态,没有启动

image-20220531145518490

小结

初学 Servlet, 遇到的这类问题会非常多. 我们不光要学习 Servlet 代码的基本写法, 也要学习排查错误的思路.

程序猿调试 BUG 如同医生诊病.

一个有经验的程序猿和一个新手程序猿相比, 最大的优势往往不是代码写的多好, 而是调试效率有多高. 同一个问题可能新手花了几天都无法解决的, 但是有经验的程序猿可能几分钟就搞定了.

你还觉得 “程序猿是吃青春饭” 嘛?

熟悉 HTTP 协议能够让我们调试问题事半功倍.

  • 4xx 的状态码表示路径不存在, 往往需要检查 URL 是否正确, 和代码中设定的 Context Path 以及Servlet Path 是否一致.
  • 5xx 的状态码表示服务器出现错误, 往往需要观察页面提示的内容和 Tomcat 自身的日志, 观察是否存在报错.
  • 出现连接失败往往意味着 Tomcat 没有正确启动, 也需要观察 Tomcat 的自身日志是否有错误提示.
  • 空白页面这种情况则需要我们使用抓包工具来分析 HTTP 请求响应的具体交互过程.

观察日志是调试程序的重要途径. Tomcat 的日志往往很多, 需要同学们耐心阅读, 经常阅读, 熟练了就能更快速的找到问题了

Servlet 运行原理

Tomcat的定位

Servlet属于上层建筑,下面的传输层,网络层,数据链路层属于经济基础

image-20220531145941406

描述了Tomcat和Servlet之间的关系,只是简单描述了当浏览器给服务器发送请求的时候, Tomcat 作为 HTTP 服务器, 就可以接收到这个请求.

image-20220531150221950

Tomcat其实是一个应用程序,是一个运行在用户态的普通进程,Tomcat其实也是一个java进程

Tomcat运行在JVM之上,Servlet是在tomcat之上的东西.Tomcat完成一些地城的工作之后,将一些数据交给Servlet,让Servlet进行进一步的处理,用户在Servlet的体系下,实现一些自定义的代码

用户写的代码(根据请求计算响应)通过Servlet和tomcat进行交互

Tomcat进一步的和浏览器之间的 网络传输,仍然走的是网络原理中的那一套(封装和分用)

image-20220531150156958

  1. 接收请求:
  • 用户在浏览器输入一个 URL, 此时浏览器就会构造一个 HTTP 请求.
  • 这个 HTTP 请求会经过网络协议栈逐层进行 封装 成二进制的 bit 流, 最终通过物理层的硬件设备转换成光信号/电信号传输出去.
  • 这些承载信息的光信号/电信号通过互联网上的一系列网络设备, 最终到达目标主机(这个过程也需要网络层和数据链路层参与).
  • 服务器主机收到这些光信号/电信号, 又会通过网络协议栈逐层进行 分用, 层层解析, 最终还原成HTTP 请求. 并交给 Tomcat 进程进行处理(根据端口号确定进程)
  • Tomcat 通过 Socket 读取到这个请求(一个字符串), 并按照 HTTP 请求的格式来解析这个请求, 根据请求中的 Context Path 确定一个 webapp, 再通过 Servlet Path 确定一个具体的 类. 再根据当前请求的方法 (GET/POST/…), 决定调用这个类的 doGet 或者 doPost 等方法. 此时我们的代码中的doGet / doPost 方法的第一个参数 HttpServletRequest 就包含了这个 HTTP 请求的详细信息.
  1. 根据请求计算响应:
  • 在我们的 doGet / doPost 方法中, 就执行到了我们自己的代码. 我们自己的代码会根据请求中的一些信息, 来给 HttpServletResponse 对象设置一些属性. 例如状态码, header, body 等.
  1. 返回响应:
  • 我们的 doGet / doPost 执行完毕后, Tomcat 就会自动把 HttpServletResponse 这个我们刚设置好的对象转换成一个符合 HTTP 协议的字符串, 通过 Socket 把这个响应发送出去.
  • 此时响应数据在服务器的主机上通过网络协议栈层层 封装, 最终又得到一个二进制的 bit 流, 通过物理层硬件设备转换成光信号/电信号传输出去.
  • 这些承载信息的光信号/电信号通过互联网上的一系列网络设备, 最终到达浏览器所在的主机(这个过程也需要网络层和数据链路层参与).
  • 浏览器主机收到这些光信号/电信号, 又会通过网络协议栈逐层进行 分用, 层层解析, 最终还原成HTTP 响应, 并交给浏览器处理.
  • 浏览器也通过 Socket 读到这个响应(一个字符串), 按照 HTTP 响应的格式来解析这个响应. 并且把body 中的数据按照一定的格式显示在浏览器的界面上.

Tomcat的伪代码

伪代码 : 并不是一些语法严谨, 功能完备的代码, 只是通过这种形式来大概表达某种逻辑.

  1. Tomcat 初始化流程
class Tomcat {
    // 用来存储所有的 Servlet 对象
	private List<Servlet> instanceList = new ArrayList<>();

    //1*执行Tomcat启动 初始化的逻辑
    public void start() {
       // 根据约定,读取 WEB-INF/web.xml 配置文件;
        // 并解析被 @WebServlet 注解修饰的类
       
        // 假定这个数组里就包含了我们解析到的所有被 @WebServlet 注解修饰的类. 
 		//*加载所有的Servlet类并放在这个数组里面
        Class<Servlet>[] allServletClasses = ...;
        
        // 这里要做的的是实例化出所有的 Servlet 对象出来;
        //2*根据刚才类加载的结果,给这些类创建Servlet实例
        for (Class<Servlet> cls : allServletClasses) {
            // 这里是利用 java 中的反射特性做的
           // 实际上还得涉及一个类的加载问题,因为我们的类字节码文件,是按照约定的
            // 方式(全部在 WEB-INF/classes 文件夹下)存放的,所以 tomcat 内部是
            // 实现了一个自定义的类加载器(ClassLoader)用来负责这部分工作。
            
            Servlet ins = cls.newInstance();
            instanceList.add(ins);
       }
        
        // 调用每个 Servlet 对象的 init() 方法,这个方法在对象的生命中只会被调用这一次;
        //3* 实例创建好之后,调用当前Servlet实例的init方法
        for (Servlet ins : instanceList) {
            //init()是Servlet自带的方法,默认情况下,这个方法什么都不做 
            ins.init();
       }
        
        // 利用我们之前学过的知识,启动一个 HTTP 服务器
        // 并用线程池的方式分别处理每一个 Request
        //4* 创建Tcp socket,监听8080端口,等待有客户端来连接
        ServerSocket serverSocket = new ServerSocket(8080);
        // 实际上 tomcat 不是用的固定线程池,这里只是为了说明情况
        ExecuteService pool = Executors.newFixedThreadPool(100);
        
        while (true) {
            Socket socket = ServerSocket.accept();
            // 每个请求都是用一个线程独立支持,这里体现了我们 Servlet 是运行在多线程环境下的
            pool.execute(new Runnable() {
               doHttpRequest(socket); 
           });
       }
        //5* 如果循环退出了,Tomcat也就结束了,就会依次循环调用Servlet的destroy方法
        // 调用每个 Servlet 对象的 destroy() 方法,这个方法在对象的生命中只会被调用这一次;
        for (Servlet ins : instanceList) {
            ins.destroy();
       }
   }
    
    public static void main(String[] args) {
        new Tomcat().start();
   }
}
  1. Tomcat 处理请求流程
class Tomcat {
    void doHttpRequest(Socket socket) {
        // 参照我们之前学习的 HTTP 服务器类似的原理,进行 HTTP 协议的请求解析,和响应构建
        HttpServletRequest req = HttpServletRequest.parse(socket);
        HttpServletRequest resp = HttpServletRequest.build(socket);
        
        // 判断 URL 对应的文件是否可以直接在我们的根路径上找到对应的文件,如果找到,就是静态内容
        
        // 直接使用我们学习过的 IO 进行内容输出
        //判断当前请求是否为静态文件
        if (file.exists()) {
            // 如果是静态文件,就读取稳固爱你内容,把文件内容构造到resp的body中,并范围resp对象
            return;
       }
        
        // 走到这里的逻辑都是动态内容了
        
        // 根据我们在配置中说的,按照 URL -> servlet-name -> Servlet 对象的链条
        // 最终找到要处理本次请求的 Servlet 对象
        //根据请求的URL,来获取到哪个类来处理
        Servlet ins = findInstance(req.getURL());
        
        // 调用 Servlet 对象的 service 方法
        // 这里就会最终调用到我们自己写的 HttpServlet 的子类里的方法了
        try {
       ins.service(req, resp); 
       } catch (Exception e) {
            // 返回 500 页面,表示服务器内部错误
       }
   }
}
  1. Servlet 的 service 方法的实现
class Servlet {
    public void service(HttpServletRequest req, HttpServletResponse resp) {
        String method = req.getMethod();
        if (method.equals("GET")) {
            doGet(req, resp);
       } else if (method.equals("POST")) {
            doPost(req, resp);
       } else if (method.equals("PUT")) {
            doPut(req, resp);
       } else if (method.equals("DELETE")) {
            doDelete(req, resp);
       } 
       ......
   }
}

在讨论到上面整套流程中,涉及到关于Servlet的关键方法,主要有三个(Servlet的生命周期)

init:初始化,对象创建好了,就会执行到,用户可以重写这个方法,爱初始化 一些初始化逻辑

service:在处理请求阶段来调用,每次,来个请求就会调用一次service

destroy : 退出主循环,Tomcat结束之后就会调用,用来释放资源

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值