0


Servlet运行原理_API详解_请求响应构造进阶之路(Servlet_2)

目录

本章重点

  • 掌握创建Servlet项目的七步骤
  • 了解Servlet运行原理
  • 熟悉使用Servlet中的关键api (HttpServlet/HttpServletRequest/HttpServletResponse)的方法
  • 熟悉用户层协议报文的设计
  • 能够通过Servlet编写http请求和响应
  • 熟悉掌握在tomcat下部署web项目

Servlet创建项目流程

创建

Servlet

项目七步骤:

  • 创建Maven项目
  • 引入依赖(Servletjar包导入到pom.xml)
  • 创建目录src/main/webapp/WEB-INF/web.xml
  • 编写servlet代码
  • 打包
  • 部署(这里打包和部署可以通过引入smart tomcat插件完成)
  • 验证

Servlet常见出错响应状态码

**

405

** Method Not Allowed
方法不匹配

我们构造的

doGet

就只能用

get

请求处理!

doPost

需要通过

post

请求处理!
如果不匹配响应就会访问这个

405

状态码!

在这里插入图片描述
在这里插入图片描述可以看到我们需要用

Post

请求处理该

servlet

代码,而我们却直接输入

url

这样的方式就是直接通过

get

请求访问服务器!
在这里插入图片描述
当我们没有把调用父类下的

doGet

方法注释掉时,也会返回

405

状态码!在这里插入图片描述我们看一下源码就知道了!
在这里插入图片描述
这里父类的

doGet

方法直接返回

405

!

***我们怎样区分

get

post

请求呢?***

get请求

  • 直接在浏览器搜索框中输入url
  • 我们html下的<a>标签,img/linkscript标签等等!
  • form表单指定method属性为get
  • ajax构造get请求在type设置为get

post请求

  • 通过form表单,method指定为post
  • ajax构造post请求,type指定为post

**

500

** Internal Server Error
服务器出错

这里的500状态码对我们初学者来说是很常见的,就是我们的服务器出错,也就是我们的

Servlet

代码发生异常并没有处理掉!这回将异常抛到

tomcat

而tomcat直接将异常返回给客户端!
在这里插入图片描述这里出

bug

了,但是浏览器还是将响应信息返回到浏览器上了!
在这里插入图片描述
如果我们将上面的响应信息给去掉,就可以看到返回的错误信息在页面上,通过这个错误可以精准找到我们的bug!
在这里插入图片描述

服务器未启动或者端口号被占用

在这里插入图片描述
出现这个错误,说明是

TCP

连接出现了问题!

在这里插入图片描述这个错误说明我们该

Servlet

类的路径没有按指定规则编写!
在这里插入图片描述
我们需要加上

/

Servlet运行原理

***我们的

Servlet

代码连一个

main

方法都没有是怎么运行呢?***
这里我们需要了解一下

tomcat

帮我们做的工作和处理机制!

tomcat定位

我们知道tomcat就是一个

http

服务器,而

http

是用户层协议!
所以我们的

Servlet

代码是基于

tomcat 

运行的!

在这里插入图片描述
当用户在浏览器中发送请求后,

tomcat

作为应用层服务器就可以接这个请求,而

http

只是一个应用层协议,需要通过其他层协议协助传输!在这里插入图片描述这里的传输过程也是要经过5层协议进行封装分用,这里和之前的一样!
在这里插入图片描述
我们分析一下上述流程

  • 接收请求

我们浏览器客户端发送一个请求,然后用户的请求参数随着查询字符串或者body构造了一个

http

请求然后到达了用户层,用户层协议就是http,然后调用操作系统内核下的,

socket

api发送到网络层,网络层加上

TCP

报头,到达传输层加上

IP

协议报头,然后就传输到了数据链路层,加上帧头帧尾,最后到达物理层调用网卡设备将这些信息转换成光信号或者高低电平,通过网络设备传输到达服务器主机,服务器主机通过网卡接收到这一组信号解析成以太网数据帧,进行分用!层层解析最后解析成一个

http

请求并交给

tomcat

进程进行处理!

tomcat

拿到

http

协议报(字符串)按照协议报格式进行解析,根据

ContentPath

路径确定

webapp

,在通过

ServletPath

确定具体的类,根据请求的方法,决定调用

doGET/POST

方法,此时我们的

HttpServletResquest

对象就包含了这个请求的详细信息!

  • 根据请求处理响应

我们通过

HttpServletRequest

中的请求信息,计算相应的响应信息,通过

HttpServletResponse

这个对象,存放响应信息!比如我们可以设置一些响应的状态码,body字段等!

  • 返回响应

我们的

doGet/doPost 

执行结束后,就会自动把

HttpServletResponse

以及我们已经设置的一些属性转换成相应的

http

响应,通过

socket

发送!后面的过程就是网络传输层层分用分装的过程,最后将响应中的body信息展现在浏览器上给用户!

tomcat伪代码

通过下面

tomcat

伪代码,了解tomcat初始化/接收请求两部分核心内容!

  • tomcat初始化流程
classTomcat{// 用来存储所有的 Servlet 对象privateList<Servlet> instanceList =newArrayList<>();publicvoidstart(){// 根据约定,读取 WEB-INF/web.xml 配置文件;// 并解析被 @WebServlet 注解修饰的类// 假定这个数组里就包含了我们解析到的所有被 @WebServlet 注解修饰的类.Class<Servlet>[] allServletClasses =...;// 这里要做的的是实例化出所有的 Servlet 对象出来;for(Class<Servlet> cls : allServletClasses){// 这里是利用 java 中的反射特性做的// 实际上还得涉及一个类的加载问题,因为我们的类字节码文件,是按照约定的// 方式(全部在 WEB-INF/classes 文件夹下)存放的,所以 tomcat 内部是// 实现了一个自定义的类加载器(ClassLoader)用来负责这部分工作。Servlet ins = cls.newInstance();
    instanceList.add(ins);}// 调用每个 Servlet 对象的 init() 方法,这个方法在对象的生命中只会被调用这一次;for(Servlet ins : instanceList){
    ins.init();}// 利用我们之前学过的知识,启动一个 HTTP 服务器// 并用线程池的方式分别处理每一个 RequestServerSocket serverSocket =newServerSocket(8080);// 实际上 tomcat 不是用的固定线程池,这里只是为了说明情况ExecuteService pool =Executors.newFixedThreadPool(100);while(true){Socket socket =ServerSocket.accept();// 每个请求都是用一个线程独立支持,这里体现了我们 Servlet 是运行在多线程环境下的
    pool.execute(newRunnable(){doHttpRequest(socket);});}// 调用每个 Servlet 对象的 destroy() 方法,这个方法在对象的生命中只会被调用这一次;for(Servlet ins : instanceList){
    ins.destroy();}}publicstaticvoidmain(String[] args){newTomcat().start();}}

这里就是

tomcat

初始化

我们看到这里

tomcat

其实是有

main

方法的,tomcat启动就从

main

方法开始!
启动后就会将

@webServlet

标记的类获取到,这些类已经是

.class

文件,需要通过反射机制创建好对应的实例,这些实例创建好就会调用

init

方法进行初始化,这个方法在

HttpServlet

类中,我们也可以重写这个方法!
这些请求处理业务完成后,就会将这些实例销毁调用其

destroy

方法,这里的方法也是在

HttpServlet

类中,我们也可以进行重写!
我们可以看到

tomcat

的内部也是调用操作系统中的

socket

进行网络通信的!
还有这里

tomcat

需要处理多个

htttp

请求,这里采取了多线程的方式,

Servlet

运行在多线程状态下的!

  • 处理请求流程
classTomcat{voiddoHttpRequest(Socket socket){// 参照我们之前学习的 HTTP 服务器类似的原理,进行 HTTP 协议的请求解析,和响应构建HttpServletRequest req =HttpServletRequest.parse(socket);HttpServletRequest resp =HttpServletRequest.build(socket);// 判断 URL 对应的文件是否可以直接在我们的根路径上找到对应的文件,如果找到,就是静态
    内容
    // 直接使用我们学习过的 IO 进行内容输出if(file.exists()){// 返回静态内容return;}// 走到这里的逻辑都是动态内容了// 根据我们在配置中说的,按照 URL -> servlet-name -> Servlet 对象的链条// 最终找到要处理本次请求的 Servlet 对象Servlet ins =findInstance(req.getURL());// 调用 Servlet 对象的 service 方法// 这里就会最终调用到我们自己写的 HttpServlet 的子类里的方法了try{
    ins.service(req, resp);}catch(Exception e){// 返回 500 页面,表示服务器内部错误}}}

我们这里的

tomcat

通过调用socket

api

然后获取到

http

请求,然后将请求按照

http

协议报的格式解析成

HttpServlet

对象,然后通过

url

中的资源目录,获取到对应的

ContentPath

Servlet

路径获取到对应的文件,如果是静态资源就直接通过

socket

返回给客户端,如果是动态资源就会调用

HttpServlet

下的

service

方法,通过这个方法就可以调用对应的

doGET/doPOST

处理请求,然后再将计算对应的响应,最后返回!

  • Servletservice的实现
classServlet{publicvoidservice(HttpServletRequest req,HttpServletResponse resp){String method = req.getMethod();if(method.equals("GET")){doGet(req, resp);}elseif(method.equals("POST")){doPost(req, resp);}elseif(method.equals("PUT")){doPut(req, resp);}elseif(method.equals("DELETE")){doDelete(req, resp);}......}}

这里就是根据对应的请求调用对应的请求处理方法,这里是通过多态的机制处理!

Servlet API详解

Servlet API有很多,我们只需要掌握

HttpServlet/HttpServletRequset/HttpServletResponse

这时个关键类中的核心方法即可!

HttpServlet

我们编写

Servlet

代码第一步就是继承

HttpServlet

类,并重写该类中的某些方法,处理请求!

核心方法:
方法名称调用时机initHttpServlet实例化后就调用destoryHttpServlet实例不再使用就调用该方法service收到Http请求就调用,为了匹配相应的处理请求方法doGet/doPost/doPut…收到匹配的请求时,由

service

方法调用对应的方法!
而我们实际开发很少重写

init/destory

这些tomcat会帮我们调用!
我们主要任务还是处理对应请求,对不同方法请求,重写匹配的doXxx方法,处理不同的请求,返回对应的响应即可!

这些上述方法的调用时机,又称

Servlert

的生命周期!
在这里插入图片描述
这里的

init

方法,当

HttpServlet

实例化后就会通过该方法进行初始化,然后生命周期结束就是在

destroy

方法调用后将

HttpServlet

实例化对象销毁!期间可能要处理不同方法请求,所以可能会多次调用

service

方法!
注意:

HttpServlet

实例只是在程序启动后创建一次就好了,并不是每次收到

http

请求都创建实例!

在这里插入图片描述
上述这些方法我们都可以进行重写,从而设置某些特有的属性,当时我们很少这样做,我们最常用的就是重写处理请求的方法

doXXX

!
代码示例

处理一个

Get

请求

我们分别通过

ajax

form

表单进行构造!

  • 基于ajax
<!--引入jQuery--><scriptsrc="http://code.jquery.com/jquery-2.1.1.min.js"></script><script>//构造请求
        $.ajax({type:'get',url:'test',//不要加/表示绝对路径!success:function(body){
                console.log(body);//在浏览器控制台打印body信息!},error:function(){
                console.log("请求失败");}});</script>

注意:

这里的

url

不用加

/

servlet

不同,而且这里的

url

就和我们的

@WebServlet

注释对应!
在这里插入图片描述

在这里插入图片描述
我们通过

ajax

的方式用前端构造请求,记得将这个

html

文件放入到

webapp

目录下,通过访问这个网页就向服务器发送了一个

get

方式的请求!
在这里插入图片描述
然后这里

ajax

通过回调的方式,如果请求成功,就会在控制台打印服务器给我们放回的

body

内容!
在这里插入图片描述在这里插入图片描述

  • 基于form表单

我们通过

form

表单构造一个

post

请求!

<formaction="test"method="post"><inputtype="text"name="name"><inputtype="password"name="password"><inputtype="submit"name="post请求"value="post请求"></form>

客户端:在这里插入图片描述服务器

importjavax.servlet.ServletException;importjavax.servlet.annotation.WebServlet;importjavax.servlet.http.HttpServlet;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.io.IOException;@WebServlet("/test")publicclassTestextendsHttpServlet{@OverrideprotectedvoiddoGet(HttpServletRequest req,HttpServletResponse resp)throwsServletException,IOException{
       resp.getWriter().write("hello world!");}@OverrideprotectedvoiddoPost(HttpServletRequest req,HttpServletResponse resp)throwsServletException,IOException{String nameValue = req.getParameter("name");String passwordValue = req.getParameter("password");
       resp.getWriter().write("name:"+nameValue+" password:"+passwordValue);}}

在这里插入图片描述
将响应输入到了浏览器上!

HttpServletResquest

我们知道

HttpServletResquest

类就是我们收到的请求,通过这个类将接收到了

http

的请求信息然后转换了对应

http

协议格式的字符串!我们通过该类提供的一些方法就可以获取到请求的报头信息还有内容了!

核心方法

在这里插入图片描述
上述方法,我们根据其英文意思和之前对

http

协议报头的学习就可以大概得出什么功能!
我们们通过上述方法就是为了得到一个

http

请求的报头和内容!
所以我们多使用上述方法就知道使用场景如何了!
代码示例

importjavax.servlet.ServletException;importjavax.servlet.annotation.WebServlet;importjavax.servlet.http.HttpServlet;importjavax.servlet.http.HttpServletResponse;importjava.io.IOException;importjava.util.Enumeration;@WebServlet("/request")publicclassHttpServletRequestextendsHttpServlet{@OverrideprotectedvoiddoGet(javax.servlet.http.HttpServletRequest req,HttpServletResponse resp)throwsServletException,IOException{StringProtocolname= req.getProtocol();//返回协议名称和版本!String method = req.getMethod();//返回请求方法String url = req.getRequestURI();StringQueuryString= req.getQueryString();//获取到查询字符串Enumeration<String>  headernames = req.getHeaderNames();//请求header部分!
        resp.setContentType("text/html;charset=utf8");//响应的格式以及编码方式!
        resp.getWriter().write(Protocolname+"<br>"+method+"<br>"+url+"<br>"+QueuryString);while(headernames.hasMoreElements()){String headerKey = headernames.nextElement();//获取到header中的key值String headerVal = req.getHeader(headerKey);//通过key值找到val值!
            resp.getWriter().write(headerKey+":"+headerVal+"<br>");}}}

浏览器返回的结果!
在这里插入图片描述

fiddler

抓取的响应
在这里插入图片描述
这就是

HttpServletRequest

类中核心方法的使用!

HttpServletRespondse

这个类就是我们服务器用来返回响应的类!
我们

Servlet

处理请求的

doxx

方法,我们根据请求计算出响应,我们可以根据请求将响应信息通过该类中的方法构造好,然后该类方法的对象通过

http

协议格式,转化成一个字符串,并通过

socket

写会给浏览器!
核心方法:
在这里插入图片描述代码示例

importjavax.servlet.ServletException;importjavax.servlet.annotation.WebServlet;importjavax.servlet.http.HttpServlet;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.io.IOException;/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: hold on
 * Date: 2022-06-30
 * Time: 13:03
 */@WebServlet("/response")publicclassHttpResponseextendsHttpServlet{@OverrideprotectedvoiddoGet(HttpServletRequest req,HttpServletResponse resp)throwsServletException,IOException{
        resp.setContentType("text/html;charset=utf8");//设置响应内容类型,和编码方式!
        resp.setHeader("name","bug郭");//设置响应头部分,传入键值对信息!
        resp.setHeader("password","666666");
        resp.setStatus(404);//设置响应状态码!
        resp.getWriter().write("收到响应!");}}

浏览器获取到的结果:
在这里插入图片描述

fiddler

抓包获取到的响应
在这里插入图片描述可以看到我们自己构造的响应头部分,出现了中文乱码!!!
但是我们刚刚不是已经设置过了编码方式

utf8

嘛?
为啥还这样呢?
我们看到这里的

setContentType

只是设置响应内容,就是body部分的格式和字符编码!我们的响应头部分中的属性一般都是已有的属性,一般没有中文,所以并不能设置!

  resp.setStatus(304);//设置响应状态码重定向!(也可以省略)
  resp.sendRedirect("https://www.bilibili.com/");//重定向后跳转的网页

请求
在这里插入图片描述
重定向
在这里插入图片描述

fiddler

抓取到的响应!在这里插入图片描述

标签: servlet java tomcat

本文转载自: https://blog.csdn.net/weixin_52345071/article/details/125466555
版权归原作者 bug 郭 所有, 如有侵权,请联系我们删除。

“Servlet运行原理_API详解_请求响应构造进阶之路(Servlet_2)”的评论:

还没有评论