JavaEE - Servlet(Tomcat运行原理,Servlet主要API(HttpServlet HttpServletRequest HttpServletResponse))

本文详细介绍了Servlet的工作原理,包括Tomcat如何处理请求及Servlet的生命周期。同时深入探讨了HTTPServlet类、HttpServletRequest类和HttpServletResponse类的主要方法及其应用。

在Servlet中,我们是重写doGet方法来处理浏览器的Get请求。

但是如果重写的是doPost方法,那么服务器就只能处理Post方法,如果此时浏览器发送Get方法,会出现405(方法不允许)错误。

eg:
在这里插入图片描述

常见的浏览器发送GET请求的情况:

  1. 通过URL向服务器请求
  2. 通过a标签跳转
  3. 通过img、link、script标签跳转
  4. 通过from表单,method指定为GET
  5. 通过ajax,type指定为GET

常见的浏览器发送POST请求的情况:

  1. 通过from表单,method指定为POST方法
  2. 通过ajax,type指定为POST

在编写服务器时,需要让请求方式和重写函数进行对应。

1. Tomcat运行原理

首先,Tomcat的定位如下图:
在这里插入图片描述
Tomcat在处理请求的时候调用了servlet接口。

Tomcat是一个工作在用户态的普通Java进程,工作在JVM之上。
用户通过Servlet接口将自定义方法交给Tomcat,Toncat进而通过封装分用的方式交给浏览器。

Tomcat工作流程

  1. Tomcat先从指定的目录中(webapps文件夹)找到所有要加载的Servlet类。
  2. 根据加载的类,给这些类创建Servlet实例
  3. 调用Servlet实例的初始化方法(init方法)
  4. 创建TCP 套接字,监听端口,循环等待客户端连接。每次有客户端连接时accept方法返回,tomcat将其插入到线程池中。
  5. 线程池中的线程执行Tomcat中处理HTTP请求方法.(doHttpRequest方法)
  6. 当循环获取链接结束后,Tomcat会循坏调用每一个Servlet的destory方法,这个方法在对象的生命周期中只会调用一次。
    需要注意,这里的Tomcat正常流程会这样退出,如果Tomcat异常退出(进程被销毁),可能来不及调用destory方法。

Tomcat处理请求流程(doHttpRequest方法)

  1. 构造HTTP请求,响应对象。
    请求req是通过读取socket中的数据,按照HTTP协议的请求解析,构造出一个对象。
    响应resp实际上是空对象,需要服务器构造。
  2. 判断当前请求是否是静态文件,如果是静态方法,读取文件内容,将内容填充到resp对象中,返回给客户端。
  3. 如果是动态文件,首先根据请求的URL,来获取到使用那个Servlet类来处理这个请求。如果没有找到对应的类,返回404.
  4. 根据找到的Servlet对象,调用这个对象的service方法。
    在这个service方法内部,又会进一步的调用doGet/doPost方法。
    如果在这个过程中出现了未捕捉的异常,客户端提示500错误。这个异常被Tomcat处理。

Servlet对象service方法流程

  1. 根据当前请求的类型,使用条件判断来调用不同的方法eg:(doGet、doPost)

总结:

上述流程关于Servlet方法主要有三个,这就是Servlet的生命周期

  1. init:初始化阶段,对象创建好后,就会执行到,用户可以重写这个方法,来执行一些初始化逻辑。
  2. service:循环处理请求时,每个请求都要调用一次service。
  3. destory:退出接受循环,Tomcat准备结束前,调用这个方法来释放资源。

在这里插入图片描述

2. Servlet主要API(HttpServlet HttpServletRequest HttpServletResponse)

HttpServlet类

核心方法:

方法名称调用时机
initHttpServlet实例化之后调用
destroyHttpServlet对象销毁时调用
service收到HTTP请求时调用
doGet收到GET请求时由service调用
doPost收到POST请求时由service调用
doPut/doDelete/…收到其他请求时由service调用
eg:
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/method")
public class ServletPost extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        super.doPost(req, resp);
        resp.setContentType("POST/html; charset=utf8");
        resp.getWriter().write("POST 响应");
    }
}

HttpServletRequest类

方法描述
String getProtocol()返回请求协议的名称和版本
String getMethod()返回HTTP请求方法(GET、POST等)
String getRequestURI()从协议名称到HTTP请求第一行的查询字符串中,返回请求URL的一部分
String getContextPath()返回请求上下文的请求URI部分
String getQureryString()返回包含在路径后的请求URL中的查询字符串
Enumeration getParammeterNames()请求中包含的参数名称的枚举
String getParameter(String name)以字符串的形式返回请求参数的值,不存在为null
String[] getParameterValues(String name)返回一个字符串数组,包含了所有给定的请求参数的值
Enumeration getHeaderNames()返回请求中包含的所有头名
String getHeader(String name)返回指定请求头的值
String getCharacterEncoding()返回请求主体使用的字符编码的名称
String getContnetType()返回主题的MIME类型,不知道类型返回null
int getContentLength()以字节为单位返回请求主体的长度,并提供输出流,长度未知返回-1
InputStream getInputStream()读取请求body内容,返回一个InputStream对象

eg:打印请求信息

import java.io.IOException;
import java.util.Enumeration;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/showRequest")
public class ShowRequest extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // TODO Auto-generated method stub
        // super.doGet(req, resp);
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("<h3> 首行部分 </h3>");
        stringBuilder.append(req.getProtocol() + "<br>"); //协议的名称和版本
        stringBuilder.append(req.getMethod() + "<br>"); //请求使用的方法
        stringBuilder.append(req.getRequestURI() + "<br>"); //获取请求uri
        stringBuilder.append(req.getContextPath() + "<br>"); //返回请求上下文的请求URI部分
        stringBuilder.append(req.getQueryString() + "<br>"); //返回包含在路径后的请求URL中的查询字符串
        stringBuilder.append("<h3> header 部分 </h3>");
        Enumeration<String> headerNames = req.getHeaderNames(); //获取报头所有项名称
        while (headerNames.hasMoreElements()) {
            //迭代器遍历枚举元素
            String name = headerNames.nextElement();
            String value = req.getHeader(name); //获取HTTP报头对应名称的值
            stringBuilder.append(name + ":" + value + "<br>");
        }

        // 将数据写到响应中
        resp.setContentType("text/html; charset=utf8");
        resp.getWriter().write(stringBuilder.toString());
    }
}

在这里插入图片描述

eg:获取GET请求中的参数

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


@WebServlet("/Parameter")
public class GetParameterServlet extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // TODO Auto-generated method stub
        // super.doGet(req, resp);
        // /pParameter?userId=123&classId=450
        String userid=req.getParameter("userId");
        String classid=req.getParameter("classId");
        resp.getWriter().write("userId="+userid+" classId="+classid);
    }
}

在这里插入图片描述
需要注意的是如果是POST请求,数据在body上。body常见的格式为:

  1. x-www-form-urlencoded
  2. form-data
  3. json

如果是第一种body格式,服务器获取参数的方式和GET一样,使用getParameter
eg:

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/PostParameter")
public class PostParameter extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // TODO Auto-generated method stub
        // super.doPost(req, resp);
        // userid=10&classid=20
        String userid = req.getParameter("userId");
        String classId = req.getParameter("classId");

        resp.getWriter().write("userId:" + userid + " classId:" + classId + "Test");

    }
}

在这里插入图片描述
如果是第三种body请求,即数据以json形式保存到body上。

这里借助第三方库来处理json格式数据,这里使用Jackson库

eg:首先通过JS构造jsion请求,之后在java后端上通过Jackson来解析。

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.fasterxml.jackson.databind.ObjectMapper;

//存放Json转化后的数据
class user {
    public int userId;
    public int classId;
}

@WebServlet("/postJson")
public class PostJson extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 创建jackson对象
        ObjectMapper objectMapper = new ObjectMapper();
        // 将字符串转化为Java对象,第一个参数是待转化的Json字符串,第二个参数是转化的类型
        user msg = objectMapper.readValue(req.getInputStream(), user.class);

        resp.getWriter().write("userId: " + msg.userId + " classId: " + msg.classId);
    }
}

使用ajax构造json数据

<!doctype html>
<html lang="cn">

<head>
    <meta charset="UTF-8">
    <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>测试Post响应</title>
</head>

<body>
    <!-- ajax构造post请求 -->
    <!-- 引入cdn -->

    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
    <input type="text" id="userId"><br>
    <input type="text" id="classId"><br>
    <input type="submit" value="提交" id="submit">

    <script>
        let button = document.querySelector("#submit");
        let userIdInput = document.querySelector("#userId");
        let classIdInput = document.querySelector("#classId");
        button.onclick = function () {
            //获取输入框内容

            $.ajax({
                type: 'post',
                url: 'postJson',
                contentType: 'application/json',
                // 将js对象转化为字符串
                data: JSON.stringify({
                    userId: userIdInput.value,
                    classId: classIdInput.value
                }),
                success: function (body) {
                    console.log(body);
                }
            });
        }
    </script>
</body>

</html>

在这里插入图片描述

HttpServletResponse类

方法描述
void setStatus(int sc)为响应设置状态码
void setHeader(String name,String value)设置一个带有给定的名称和值的 header.如果name 已经存在,则覆盖旧的值.
void addHeader(String name, String value)添加一个带有给定的名称和值的 header.如果name 已经存在,不覆盖旧的值,并列添加新的键值对
void setContentType(String type)设置被发送到客户端的响应的内容类型。
void setCharacterEncoding(String charset)设置被发送到客户端的响应的字符编码(MIME字符集)例如,UTF-8。
void sendRedirect(String location)使用指定的重定向位置URL发送临时重定向响应到客户端。
Printwriter getWriter()用于往body中写入文本格式数据.
OutputStream getOutputStream()用于往body中写入二进制格式数据.

eg:实现网页自动刷新功能

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/states")
public class StatutsServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setStatus(200);
        resp.getWriter().write("Hello");
        resp.setHeader("Refresh", "1");
        resp.getWriter().write("time: " + System.currentTimeMillis());
    }
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BaiRong-NUC

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值