• Tomcat & Servlet


    一、What is “Tomcat”?

    Tomcat 本质上是一个基于 TCP 协议的 HTTP 服务器。我们知道HTTP是一种应用层协议,是 HTTP 客户端和 HTTP 服务器之间的交互数据的格式。Tomcat 就是基于 Java 实现的一个开源免费,也是被广泛使用的 HTTP 服务器。

    以下是 Tomcat 安装完成后的文件目录:

    运行Tomcat我们可以使用cmd命令查看Tomcat的端口号的进程ID:


    二、 What is “Servlet”?

    对于网站的后端开发,我们总是围绕着 HTTP 服务器展开相关工作,当然我们也可以重 0 开始写一个服务器,一步步的实现服务器的功能,包括根据 HTTP 协议解析请求报文、根据HTTP构造响应报文等等……对于服务器来说,很多功能都是千篇一律的,如果我们把中心放到整个服务器的开发上,这对于业务逻辑的开发来说,显然是非常不友好的。因此我们可以借助现成的 HTTP 服务器进行开发,例如 Tomcat 就为我们省去了这些不必要的环节,为我们提供了一些列的 API ,让我们能够更专注于业务逻辑。

    Servlet 就是一组 Tomcat 提供的 API,让程序猿自己写的代码能很好的和 Tomcat 配合起来,从而更简单的实现一个 webapp,而不必关注 Socket、HTTP协议格式、多线程并发等技术细节,降低了 webapp 的开发门槛,提高了开发效率。

    Servlet 中提供的 API 有很多,这里我们重点掌握 3 个类:HttpServletHttpServletRequestHttpServletResponse,下面我们逐一介绍👇:

    1、HttpServlet

    我们写 Servlet 代码的时候,首先第一步就是先创建类,继承自 HttpServlet,并重写其中的某些方法。

    HttpServlet 核心方法展示:

    方法名称调用时机
    init在 HttpServlet 实例化之后被调用一次
    destroy在 HttpServlet 实例不再使用的时候调用一次
    service收到 HTTP 请求的时候调用
    doGet收到 GET 请求的时候调用 (由 service 方法调用)
    doPost收到 POST 请求的时候调用 (由 service 方法调用)
    doPut/doDelete/doOptions/…收到其他请求的时候调用(由 service 方法调用)

    其中 init、destroy、service 这 3 个方法的调用时机,就称为 “Servlet 生命周期”:

    1. init方法,只会在初始情况下调用1次,也就是HttpServlet实例化时调用,一般使用这个方法做一些初始化的相关工作。(HttpServlet 的实例只是在程序启动时创建一次,而不是每次收到 HTTP 请求都重新创建实例。)
    2. destroy方法,用于该webapp被销毁之前执行1次,用来做一些首尾工作。但是并不建议使用这个方法,因为通常采用直接杀死进程的方式停止服务器,此时destroy执行不了。
    3. service方法,每次收到路径匹配的请求都会执行,例如上述doGet、doPost方法其实都是在service方法中被调用,一般不会重写service方法。

    在实际开发中,我们往往重写 doXXX 方法,很少会重写 init / destory / service:

    @WebServlet("/hello")
    public class HelloServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
        throws ServletException, IOException {
        	resp.getWriter().write("hello world");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    代码说明:

    1. 在这个类上方加上 @WebServlet("/hello") 注解, 表示 Tomcat 收到的请求中, 路径为 /hello的请求才会调用HelloServlet 这个类的代码

    2. HttpServletRequest 表示 HTTP 请求。Tomcat 按照 HTTP 请求的格式把 字符串 格式的请求转成了一个 HttpServletRequest 对象。后续想获取请求中的信息(方法、url、 header、body 等) 都是通过这个对象来获取。

    3. HttpServletResponse 表示 HTTP 响应。我们可以根据resp响应对象提供的方法,构造响应的状态码, header, body 等。Tomcat 会把整个响应转成字符串,通过 socket 写回给浏览器。

    2、HttpServletRequest

    当 Tomcat 通过 Socket API 读取到 HTTP 请求(字符串)后,就会按照 HTTP 协议的格式把字符串解析成 HttpServletRequest 对象。

    HttpServletRequest 提供了一些方法,可以获取到一个请求中的各个方面的信息:

    HttpServletRequest 提供的方法都只是“读”方法,而不是“写”方法。其实这也很容易理解,请求对象是服务器收到的内容,不应该被修改。

    方法描述
    String getProtocol()返回请求协议的名称和版本。
    String getMethod()返回请求的 HTTP 方法的名称,例如,GET、POST 或 PUT。
    StringBuffer getRequestURL()它用于获取请求的URL,不包含 query string
    String getRequestURI()它用于获取请求的统一资源标识符(URI)的部分,即资源路径。
    String getContextPath()获取请求的ContextPath,ContextPath标识一个webapp,即webapp的目录名
    String getServlettPath()获取请求的ServletPath,ServletPath标识一个类
    String getQueryString()返回包含在路径后的请求 URL 中的查询字符串。
    Enumeration getHeaderNames()返回一个枚举,包含该请求中包含的所有请求头名。
    String getHeader(String name)以字符串形式返回指定请求头的值。
    String getCharacterEncoding()返回请求主体中使用的字符编码的名称。
    String getContentType()返回请求主体的类型,如果不知道类型则返回 null。
    int getContentLength()以字节为单位返回请求主体的长度,如果长度未知则返回 -1。
    InputStream getInputStream()返回一个 InputStream 对象,用于读取请求的 body 内容。

    上面这些方法只要对HTTP协议足够了解,简单看一下就能够使用。这里主要介绍有关getParameter相关的方法:

    方法描述
    Enumeration getParameterNames()返回一个枚举,表示在该请求中包含的参数的名称。
    String getParameter(String name)返回请求参数的值,以字符串形式返回,如果参数不存在则返回 null。
    String[] getParameterValues(String name)返回一个字符串数组,表示给定请求参数的所有值,如果参数不存在则返回 null。

    这些方法主要是获取请求中的请求信息的,下面以getParameter()为例:

    1. 获取GET请求中的参数值

    @WebServlet("/getParameter")
    public class Test extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp)
                throws ServletException, IOException {
            // 前端通过 url 的 query string 传递 username 和 password 两个属性.
            String userName = req.getParameter("username");
            String passWord = req.getParameter("password");
    
            resp.setContentType("text/html; charset=utf-8");
            resp.getWriter().write("username="+userName+"
    "
    +"password="+passWord); } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2. 获取POST请求中的参数值

    (1)请求参数通过body传递,采用 form 表单格式提交,即 Content-Type 是 application/x-www-form-urlencoded
    此时同样可以使用getParameter直接获取参数值:

    注意:需要显示地告诉后端请求的编码方式,防止解析请求时出现中文乱码情况。

    @WebServlet("/postParameter")
    public class Test extends HttpServlet {
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp)
                throws ServletException, IOException {
    		// 显示地告诉后端请求的编码方式,防止中文乱码
            req.setCharacterEncoding("utf8");
    		
            // 前端通过 url 的 query string 传递 username 和 password 两个属性.
            String userName = req.getParameter("username");
            String passWord = req.getParameter("password");
    
            resp.setContentType("text/html; charset=utf-8");
            resp.getWriter().write("username="+userName+"
    "
    +"password="+passWord); } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    (2)请求参数通过body传递,Content-Type 是 application/json

    对于Servlet,其没有内置 json 的解析功能,此时想要获取这种格式的参数就需要借助第三方库 jackson,首先我们在中央仓库找到 jackson 依赖引入到 pom.xml 中,然后就可以编写解析 json 格式请求参数的代码了。

    使用 jackson,最核心的对象就是 ObjectMapper,通过这个对象里面的两个方法,就可以把 json 字符串解析成 java 对象;也可以把一个 java 对象转成一个 json 格式字符串,具体如下:

    1. writeValueAsString 方法把一个 Java 对象转成 JSON 格式字符串。
    2. readValue方法, 第一个参数为输入流对象,或Json字符串,的第二个参数为 JsonData 的 类对象,通过这个类对象,在 readValue 的内部就可以借助反射机制来构造出 Java 对象。
    class User {
        public String username;
        public String password;
    
        @Override
        public String toString() {
            return "User{" +
                    "username='" + username + '\'' +
                    ", password='" + password + '\'' +
                    '}';
        }
    }
    
    @WebServlet("/jsonParameter")
    public class Test extends HttpServlet {
        private ObjectMapper objectMapper = new ObjectMapper();
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp)
         throws ServletException, IOException {
         	// 设置响应格式
            resp.setContentType("text/html;charset=utf-8");
            // 通过 post 请求的 body 传递过来一个 json 格式的字符串.
            // 1.使用objectMapper将json字符串解析成java对象
            User user = objectMapper.readValue(req.getInputStream(), User.class);
            resp.getWriter().write("1.使用objectMapper将json字符串解析成java对象:
    "
    +user.toString()+"
    "
    ); // 2.使用objectMapper将java对象解析成json字符串 String jsonString = objectMapper.writeValueAsString(user); resp.getWriter().write("2.使用objectMapper将java对象解析成json字符串:
    "
    +jsonString); } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    3、HttpServletResponse

    Servlet 中的 doXXX 方法的目的就是根据请求计算得到响应,然后把响应的数据设置到 HttpServletResponse 对象中,Tomcat 内壁就会把这个 HttpServletResponse 对象按照 HTTP 协议的格式,转成一个字符串,并通过 Socket 写回给浏览器。

    响应对象是服务器要返回给浏览器的内容,这里的重要信息都是程序员根据请求设置到响应中的,因此下面的方法都是 “写” 方法。

    下面是在设置响应时常用到的方法:

    方法描述
    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 中写入二进制格式数据。

    其实上面这些方法和同样只要对HTTP协议足够了解,简单看一下就能够使用,下面就简单演示几个方法,体会一下设置响应的过程:

    (1)设置自动刷新

    @WebServlet("/Refresh")
    public class Refresh extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp)
         throws ServletException, IOException {
            // 设置自动刷新,刷新频率:每隔1秒自动刷新一次
            resp.setHeader("Refresh","1");
            resp.getWriter().write("time = "+System.currentTimeMillis());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    (2)设置重定向
    方式1:使用 sendRedirect 方法

    @WebServlet("/Redirect")
    public class Redirect extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp)
         throws ServletException, IOException {
         	// 设置重定向:
            resp.sendRedirect("https://www.baidu.com");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    方式1:使用 setStatus + setHeader方法

    @WebServlet("/Redirect")
    public class Redirect extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp)
         throws ServletException, IOException {
            // 设置重定向:
            resp.setStatus(302);
            resp.setHeader("Location","https://www.baidu.com");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
  • 相关阅读:
    神经网络的数学方法及应用,神经网络的数学理论
    基于Matlab实现多因子选股模型(附上源码+数据)
    nginx502常见502错误问题解决办法
    十大排序算法比较
    刷题 | 单调栈
    【大数据存储技术】第7章 MongoDB 的原理和使用
    前端 vite+vue3——写一个随机抽奖组件
    三天吃透Java并发八股文!
    element中的el-input只能输入数字,输入其他内容清空
    HarmonyOS实战经验合集之ArkUI(二)
  • 原文地址:https://blog.csdn.net/LEE180501/article/details/132188305