Servlet 详解

目录

什么是 servlet?

Servlet 是做甚的?

如何编写一个 Servlet 程序?

解析访问出错情况

Servlet 的运行原理

1. 接收请求

2. 根据请求计算响应

3. 返回响应

Servlet API 详解

HTTPServlet

HttpServletRequset

HttpServletResponse


什么是 servlet?

Servlet 是一种实现动态页面的技术, 是一组 Tomcat(一个HTTP服务器) 提供给程序员的一种 API , 帮助程序员简单高效的开发一个 web app

Servlet 也是是一些遵从Java Servlet API的Java类,这些Java类可以响应请求。尽管Servlet可以响应任意类型的请求,但是它们使用最广泛的是响应web方面的请求。 Servlet必须部署在Java servlet容器才能使用。

总的来说, servlet是完成 java Web 中处理请求和发送响应过程的一种程序,是为了解决实现动态页面的技术而衍生的东西 

Servlet 是做甚的?

  • 允许程序员注册一个类, 在 Tomcat 收到的某个特定的请求的时候, 执行这个类中的一些代码

  • 帮助程序员解析 HTTP 请求, 把 HTTP 请求从一个字符串解析成一个 HttpRequest 对象

  • 帮助程序员构造 HTTP 响应, 程序员只要给指定的 HttpResponse 对象填写一些属性字段, Servlet 就会自动的按照 HTTP 协议的方式构造出一个 HTTP 响应字符串, 并通过 Socket 写回给客户端

如何编写一个 Servlet 程序?

  1. 创建项目

    1. 先创建一个 Maven 项目

    2. 创建完毕弹出 一个对话框 选择 Enable Auto-Import

  2. 引入依赖

  • 会自动生成一个 pom.xml 文件
  • 我们需要在 pom.xml 中引入 Servlet 依赖的 jar 包 (3.1.0)

    <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
    </dependency>
    
  • Servlet 的版本要和 Tomcat 的版本匹配

Groupld : 组织名称

Artifactld : 项目名称

Version : 表示版

3. 创建目录

  • 目录介绍

    • Src : 源代码所在的目录

    • main/java :源代码的根目录, 后续创建 .Java 文件就放到这个目录中

    • main/resources : 表示项目的一些资源文件所在的目录

    • test/java : 表示测试代码的根目录

  1. 创建 webapp 目录 (在 main 目录下) : 存放以下静态文件 HTML , CSS

  2. 创建 WEB-INF 目录(webapp下) 并创建一个  web.xml 写入以下代码

<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
</web-app>  

4. 编写代码

 

// 2.
@WebServlet("/hello")

//1. 
public class HelloServlet extends HttpServlet {

    @Override
    //doGet就是 根据请求计算响应           req 请求                    resp响应
    // 3.
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // super.doGet(req, resp);  //直接返回错误
        //这是在服务器的控制台中, 打印了字符串.(服务器看到了, 客户端看不到)
        System.out.println("hello world!");
        // 这是在 resp 的 body 中写入 hello world 字符串,这是内容就会被 HTTP 响应返回给浏览器, 显示到浏览器页面上
        resp.getWriter().write("hello world!!");
    }
    }

此时代码就可以进行运行了, 但是现在不是使用 main 作为程序的入口, mian 方法现在会包含在 Tomcat 中, 这个代码 Tomcat 会在合适的时期进行调用

这个代码只是 Tomcat 中的一小部分逻辑

  • 要想被调用, 就得满足以下这三个条件

    • 创建类的时候需要继承自 HttpServlet

    • 需要使用 @WebServlet 注解, 加上一个 HTTP路径 ("/路径名")

    • 实现 doXXX 方法

5. 打包程序​​​​​​​

要打成 war 类型的包, war 类型的包才能被 Tomcat 识别到

要想打包为 war 包,就必须进行配置, 一般情况下默认是 jar 包

<!--    //描述打包的文件是war类型-->
    <packaging>war</packaging>
    //描述包名
    <build>
        <finalName>hello_servlet</finalName>
    </build>
  • war 包和 jar 包的区别

        jar 包是普通的 java 程序打包的结果. 里面会包含一些 .class 文件. war 包是 java web 的程序, 里面除了会包含 .class 文件之外, 还会包含 HTML, CSS, JavaScript, 图 片, 以及其他的 jar 包. 打成 war 包格式才能被 Tomcat 识别

  • 完整的 pom.xml 
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>Servlet</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.15.1</version>
        </dependency>

    </dependencies>
<!--    //描述打包的文件是war类型-->
    <packaging>war</packaging>
    <build>
        <finalName>hello_servlet</finalName>
    </build>
</project>

6. 部署程序

把 war 包拷贝到 Tomcat 的 webapps 目录下 ,启动

然后启动 Tomcat 就会自动解压缩 war 包

7. 验证程序

在浏览器中输入以下链接就可以进行打开运行 servlet 程序了

127.0.0.1:8080/hello_servlet(context 路径)/hello()

使用 IDEA 插件 smartTomcat 进行 部署

安装 Tomcat 插件

配置 smartTomcat

使用 Tomcat 插件省去了 打包 和 部署 两个步骤, 可以一键运行servlet程序 

 

解析访问出错情况

  • 404

表示用户访问的资源不存在, 大概就是 URL 的路径写的不正确

例如: 少些了 Context Path 或者是 Servlet Path

  • 405

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

例如: 没有实现 doGet 方法

  • 500

Servlet 代码中抛出异常导致的

  • 空页面

没有 resp.getWritter().write() 操作

响应 body 中的数据就是空数据

  • 无法访问此网站

一般情况下就是 Tomcat 启动失败了

注解 @WebServlet("/xxx") 中的 / 一定得注意写上

作为一个程序员不光要写出好的代码, 还得要很高效的调试代码

Servlet 的运行原理

Servlet 中没有 main 方法他是怎么运行的?

  • Tomcat 通过 Socket 读取到这个请求(一个字符串). 并按照 HTTP 请求的格式来解析这个请求, 根据请求中的 Context Path 确定一个 webapp, 再通过 webapp ,再通过 Servlet Path 确定一个具体的类, 再根据当亲请求的方法(GET/POST/...), 决定调用这个类的 doGet 或者 doPost 等方法, 此时我们的代码中的 doGet / doPsot 方法的第一个参数 HttpServletRequest  就包含了这个 HTTP 请求的详细信息

下面是详细的 servlet 的基本运行原理

1. 接收请求

  • 用户在浏览器中输入一个 URL , 此时浏览器就会构造出一个 HTTP 请求
  • 这个 HTTP 请求会经过网络协议栈逐层进行 封装 成二进制 bit 流, 最终通过物理层的硬件设备转换成光信号/电信号传输出去
  • 这些承载信息的光信号/电信号, 通过互联网上的一系列网络设备, 最终到达目标主机(网络层 和 数据链路层 参与)
  • 服务器主机收到这些光信号/电信号.,又会通过网络协议栈逐层进行 分用, 层层解析, 最终还原成 HTTP 请求, 交给 Tomcat 进程进行处理(根据端口号确定进程)
  • Tomcat 通过 Socket 读取到这个请求(一个字符串). 并按照 HTTP 请求的格式来解析这个请求, 根据请求中的 Context Path 确定一个 webapp, 再通过 webapp ,再通过 Servlet Path 确定一个具体的类, 再根据当亲请求的方法(GET/POST/...), 决定调用这个类的 doGet 或者 doPost 等方法, 此时我们的代码中的 doGet / doPsot 方法的第一个参数 HttpServletRequest  就包含了这个 HTTP 请求的详细信息

2. 根据请求计算响应

  • 在我们的 doGet / doPost 方法中, 就执行到了我们自己的代码, 我们自己的代码会根据请求中的一些信息, 来给 HttpServletResponse 对象设置一些属性, 例如状态码, header , body 等

3. 返回响应

  • 我们的 doGet / doPost  执行完毕后, Tomcat 就会自动把 HttpServletResponse 这个我们刚好设置好的对象转换成一个符合 HTTP 协议的字符串, 通过 Socket 把这个响应发送出去
  • 此时响应数据在服务器的主机上,通过网络协议栈进行层层封装成 bit  流, 通过物理层硬件设备转换为光信号/电信号传输出去
  • 这些承载信息的光信号/ 电信号通过互联网的一系列网络设备, 最终到达浏览器所在的主机(这个过程需要网络层 和 数据链路层 参与)
  • 浏览器主机收到这些光信号/电信号, 又会通过网络协议栈逐层进行 分用,层层解析, 最终还原成 HTTP 响应, 并交给浏览器处理
  • 浏览器也通过 Socket 读到这个响应(一个字符串), 按照 HTTP 响应的个数来解析这个响应, 并且把 body 中的数据按照一定得格式显示在浏览器的界面上

Servlet API 详解

HTTPServlet

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

核心方法

方法名称

调用时机

init

在 HTTPServlet 实例化之后被调用一次

destory

在 HttpServlet 实例不再使用的时候调用一次

service

收到 HTTP 请求的时候调用

doGet

收到 GET 请求的时候调用(由 service 方法调用)

doPost

收到 Post 请求的时候调用(由 service 方法调用)

doPut/doDelete/doOptions

收到其他请求的时候调用(由 service 方法调用)

代码示例: 处理 GET 请求

@WebServlet("/hello")

public class HelloServlet extends HttpServlet {

    @Override
    //doGet就是 根据请求计算响应           req 请求                    resp响应
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // super.doGet(req, resp);  //直接返回错误
        //这是在服务器的控制台中, 打印了字符串.(服务器看到了, 客户端看不到)
        System.out.println("hello world!");
        // 这是在 resp 的 body 中写入 hello world 字符串,这是内容就会被 HTTP 响应返回给浏览器, 显示到浏览器页面上
        resp.getWriter().write("hello world!!");
    }
}

 

代码示例: 处理 POST 请求

@WebServlet("/hello")

public class HelloServlet extends HttpServlet {

    @Override
    //doGet就是 根据请求计算响应           req 请求                    resp响应
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // super.doPost(req, resp);  //直接返回错误
        //这是在服务器的控制台中, 打印了字符串.(服务器看到了, 客户端看不到)
        System.out.println("dopost");
    }
}

使用 PostMan 发送请求对应的结果

 

HttpServletRequset

当 Tomcat 通过 Socket API 读取到 HTTP 请求(字符串), 并且按照 HTTP 协议的格式把字符解析为 HttpServletRequest 对象 核心方法

方法

描述

String getprotocol()

返回请求协议的名称和版本

String getMethod()

返回请求的 HTTP 方法的名称, 例如, GET. POST 或者 PUT

String getRequestURL()

返回该请求的一部分 URL

String getContextPath()

返回 ContentPath

String getQueryString()

返回包含在路径后的请求 URL 中的查询字符串

Enumeration getParaeterNames()

返回一个 String 对象的枚举, 包含在该请求中包含的参数的名称

String getParameter(String name)

以字符串形式返回请求参数的值, 或者如果参数不存在则返回 null

Enumeration getHeaderNames()

返回一个枚举, 包含在该请求中包含的所有头名

String getHeader(String name)

以字符串形式返回指定的请求头的值

String getChatacterEncoding()

返回请求主体中使用的字符编码的名称

String getContentTyoe()

返回请求主体的 MIME 类型, 如果不知道则返回 null

Int getContentLength()

以字节为单位返回请求主体的长度, 并提供输入流, 或者如果长度未知则返回 -1

InputStream getInputStream()

用于读取请求的 body 内容,返回一个 InputStream 对象

上面的方法可以 获取 到一个请求中的各个方面的信息

注意: 请求对象是服务器收到的内容, 不应该修改, 因此上面的方法也都只是 "读" 方法, 而不是 "写" 方法

代码示例:

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;

@WebServlet("/showRequest")
public class ShowRequest extends HelloServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        StringBuilder result = new StringBuilder();
        //获取请求的协议的名称和版本
        result.append(req.getProtocol());
        result.append("<br>");
        //返回请求的HTTP方法的名称
        result.append(req.getMethod());
        result.append("<br>");
        //获取 URL 的一部分, 唯一资源标识符
        result.append(req.getRequestURI());
        result.append("<br>");
        //获取 URL
        result.append(req.getRequestURL());
        result.append("<br>");
        //获取 contextPath
        result.append(req.getContextPath());
        result.append("<br>");
        //获取 servletPath
        result.append(req.getServletPath());
        result.append("<br>");
        //获取包含在路径后的请求 URL 中的查询字符串
        result.append(req.getQueryString());
        result.append("<br>");
        //返回一个枚举, 包含在该请求中包含的所有头名
        result.append(req.getHeaderNames());
        result.append("<br>");
        //获取一 String 对象的枚举, 包含在该请求中包含的参数的名称
        //result.append(req.getParameterValues(""));
        //以字符串的形式返回指定的请求头的值
        //result.append(req.getHeader());
        result.append("==========================<br>");

        //创建枚举进行接收, 所有的header
        Enumeration<String> headerNames = req.getHeaderNames();
        //循环读取枚举中的数据
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            String headerValues = req.getHeader(headerName);
            result.append(headerName + ": " + headerValues + "<br>");
        }

        //在响应中设置 body 的类型, 方便浏览器进行解析
        resp.setContentType("text/html;charset=utf8");
        resp.getWriter().write(result.toString());

    }
}

 

代码示例 : 获取 GET 请求中的参数

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

@WebServlet("/getParameter")
public class GetParameter extends HelloServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       //前端通过 url 的 query string 传递 username 和 password 两个属性
        String username = req.getParameter("username");
        if(username == null) {
            System.out.println("username 这个属性在 query string 中不存在");
        }
        String password = req.getParameter("password");
        if(password == null) {
            System.out.println("password 这个属性在 query string 中不存在");
        }
        System.out.println("username = " + username + "  password = " + password );
        resp.getWriter().write("ok");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //给请求设置字符编码 //告诉服务器字符编码
        req.setCharacterEncoding("utf8");
        //前端通过 body 以 form 表单的形式把 username 和 password 传给服务区
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        if(username == null) {
            System.out.println("username 这个 key 在 body 中不存在");
        }
        if(password == null) {
            System.out.println("password 这个 key 在 body 中不存在");
        }

        System.out.println("username= "+ username + "  password= " + password);
        resp.getWriter().write("ok");
    }


}

当没有使用 query String 方法传递值的时候, getParameter 的时候,获取的值为 null

​​​​​​​

使用之后, 此时说明服务器已经获取到客户端传递过来的参数

 

HttpServletResponse

        Servlet 中的 doXX 放法的目的就是根据请求计算得到响应, 然后把响应的数据设置到 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 中写入二进制格式数据.

展示常用方法的代码及运行截图

代码示例: 设置状态码

@WebServlet("/status")
public class StatusServlet extends HelloServlet{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setStatus(200);
        resp.setContentType("text/html; charset=utf8");
        resp.getWriter().write("返回 200 响应!");
    }
}

 

 

代码示例 : 自动刷新

@WebServlet("/refresh")
public class RefreshServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //每隔 1s 刷新一次
        resp.setHeader("Refresh","1");
                                           //获取毫秒级别的时间戳
        resp.getWriter().write("time = " + System.currentTimeMillis());
    }
}

         

每隔一秒进行刷新, 这里是 过了很多秒截的图

 对时间进行格式化输出

@WebServlet("/refresh")
public class RefreshServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //每隔 1s 刷新一次
        resp.setHeader("Refresh","1");
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        resp.getWriter().write("time = " + format.format(System.currentTimeMillis()));
    }
}

 总结:

        Servlet是一个用于扩展服务器功能的Java编程语言类。它是一个服务器端组件,通过HTTP或HTTPS协议接收并响应来自客户端(通常是Web浏览器)的请求。

        Servlet通常用于构建动态Web应用程序。它提供了一种处理客户端请求、执行业务逻辑和生成动态内容(如HTML页面、XML、JSON或其他类型的数据)的方式。Servlet是Java Enterprise Edition(Java EE)平台的一部分,通常部署在Web服务器上。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值