javaEE初阶 — Servlet API 详解

HttpServlet


在 HttpServlet 中 有三个核心的方法,是 init、destory、service

init 方法

@WebServlet("/method")
public class servletMethond extends HttpServlet {
    
    

    @Override
    public void init() throws ServletException {
    
    
        // 重写 inti 方法
        System.out.println("init");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        System.out.println("doGet");
        resp.getWriter().write("doGet");
    }
}


init 方法与 doGet 都是 HttpServlet 类里的方法,也是 tomcat 调用的, 同样都是可以重写的。

tomcat 收到了 /method 这样的路径的请求,就会调用到 servletMethond
于是就需要先对 servletMethond 进行实例化,这个实例化只进行一次。
后续再收到 /method 此时就不必在重复实例化了,直接复用之前的 servletMethond 实例即可。

这个方法在 HttpServlet 实例化之后只会被调用一次
也就是说,即使是多次请求也只会出现一个init。
\



开启服务器,可以看到此时只有一个 init,下面来多次刷新页面观察效果。



可以看到 doGet 会随着刷新次数增加,但是 init 只会在最开始的时候出现一次。



destroy 方法

在服务器终止的时候就会调用这个方法。

下面重写这个来演示一下。

 @Override
 public void destroy() {
    
    
     System.out.println("destroy");
 }



可以看到启动服务器的时候,并没有调用 destroy 这个方法,所以就没有显示出这个方法里的内容。

点击红色的矩形终止服务器。



可以看到终止服务器之后,就成功的调用了这个方法。


会不会出现 destroy 是不确定的。

如果是通过 smart tomcat 停止按钮,这个操作本质上是通过 tomcat 的 8005 端口主动停止,
是能够调用 destroy 这个方法,触发 destroy 的。
如果是直接杀进程,此时就可能来不及执行,此时 destroy 就没了。


service 方法

在收到路径匹配的 http 请求就会调用这个方法。

service 这个方法里包含了一个 doGet 方法,也就是说 doGet 方法就是在 service 方法中调用的。

接下来重写 service 方法观看底层代码。

 @Override
 protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
     super.service(req, resp);
 }




可以看到有一个 diGet 方法。

1 Servlet 的生命周期


这是一个比较频繁的面试题,生命周期就是在什么阶段,做什么事了。

比如一个人的生命周期:

1.小时候,要做的就是上学。
2.长大了之后,要参加工作。
3.再大一点,要结婚生娃。
4.年纪再大,娃要上学了。
5.年纪再大,娃娃要结婚了。
6.年纪再大一点,帮娃带娃。
7.ji了。


Servlet 的生命周期就是:

1.开始的时候,执行 init。
2.每次收到请求,执行 service。
3.销毁之前,执行 destroy。

2 代码示例


先来重写 doGet、doPost、doPut、doDelete 方法。

@WebServlet("/methods")
public class MyMethod extends HttpServlet {
    
    

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        System.out.println("doGet");
        resp.getWriter().write("doGet");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        System.out.println("doPost");
        resp.getWriter().write("doPost");
    }

    @Override
    protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        System.out.println("doPut");
        resp.getWriter().write("doPut");
    }

    @Override
    protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        System.out.println("doGet");
        resp.getWriter().write("doDelete");
    }
}




可以看到此时只显示出了一个 doGet,那其他的请求怎么办呢?
这个时候就可以使用 postmanajax 来构造请求。

3 使用 postman 构造请求


输入路径,选择 GET 请求,点击 send 就构造好了一个 GET 请求,就出现了一个 doGet。



如果要构造其他的请求,可以更改即可。

4 使用 ajax 构造请求


在 webapp 目录下创建一个 .html 的文件。

绝对路径的写法:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ajax</title>
</head>
<body>
    <script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
    <script>
        $.ajax({
      
      
            type: 'get',
            url: 'methods',
           success: function(body, status) {
      
      
               console.log(body);
           }
        });
    </script>
</body>
</html>


此处所写的 methods 就相当于是在 http://127.0.0.1:8080/Servlet 基础上再拼上一个 methods。
也就是 http://127.0.0.1:8080/Servlet/methods




在地址栏输入路径,打开控制台就可以看到构造好的 GET 请求了。


相对路径的写法:

 url: '/Servlet/methods',


接下来使用 ajax 构造 POST 请求。

<body>
    <script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
    <script>
        $.ajax({
    
    
            type: 'post',
            url: 'methods',
            success: function(body, status) {
    
    
               console.log(body);
           }
        });
    </script>
</body>
</html>




Put、Delete…和上面的是一样的构造方法。

HttpServletRequest


Request 表示的是 HTTP 请求,HttpServlet 这个对象是 tomcat 自动创造的。
tomcat 其实会实现监听端口,接受连接,读取请求,解析请求,构造请求对象等一系列的工作。

核心方法

  • String getProtocol() 返回请求协议的名称和版本。
  • String getMethod() 返回请求的 HTTP 方法的名称,例如,GET、POST 或 PUT。
  • String getRequestURI()
    从协议名称直到 HTTP 请求的第一行的查询字符串中,返回该请
    求的 URL 的一部分。
  • String getContextPath() 返回指示请求上下文的请求 URI 部分。
  • String getQueryString() 返回包含在路径后的请求 URL 中的查询字符串。
  • Enumeration getParameterNames()
    返回一个 String 对象的枚举,包含在该请求中包含的参数的名
    称。
  • String getParameter(String name)
    以字符串形式返回请求参数的值,或者如果参数不存在则返回null。
  • String[] getParameterValues(String name)
    返回一个字符串对象的数组,包含所有给定的请求参数的值,如
    果参数不存在则返回 null。
  • Enumeration getHeaderNames()
    返回一个枚举,包含在该请求中包含的所有的头名。
  • String getHeader(String name)
    以字符串形式返回指定的请求头的值。
  • String getCharacterEncoding()
    返回请求主体中使用的字符编码的名称。
  • String getContentType() 返回请求主体的 MIME 类型,如果不知道类型则返回 null。
  • int getContentLength() 以字节为单位返回请求主体的长度,并提供输入流,或者如果长
    度未知则返回 -1

1 代码示例

@WebServlet("/showRequest")
public class ShowRequestServlet extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        // 使用 StringBuilder 把这些 api 结果拼接起来,统一写响应中
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(req.getProtocol());
        stringBuilder.append(req.getMethod());
        stringBuilder.append(req.getRequestURI());
        stringBuilder.append(req.getContextPath());
        stringBuilder.append(req.getQueryString());
        
        // 写回到响应中
        resp.getWriter().write(stringBuilder.toString());
    }
}




getProtocol 得到的是 HTTP/1.1、getMethod 得到的是 GET
getRequestURI 得到的是 /Servlet/showRequest、getContextPath 得到的是 /Servlet
getQueryString 得到的是 null

前端如何给后端传参

1 通过 GET 里的 query string 传参


在前端给后端传两个数字,一个是同学的 studentId,一个是 classId
发送一个 ?studentId=10&classId=20 这样的请求。

@WebServlet("/getParameter")
public class GetParameterServlet extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        // 预期浏览器会发一个形如 /getParameter?studentId=10&classId=20 请求
        // 借出 req 里的 getParameter 方法就能拿到 query string 中的键值对内容了
        // getParameter 得到的是 string 类型的结果
        String studentId = req.getParameter("studentId");
        String classId = req.getParameter("classId");
        resp.setContentType("text/html");
        resp.getWriter().write("studentId = " + studentId + "classId = " + classId);
    }
}


在地址栏中输入路径访问。
我这里的路径是 127.0.0.1:8080/Servlet/getParameter?studentId=10&classId=20



通过 getParameter 方法,?studentId=10&classId=20 这个键值对会自动被 tomcat 处理成
形如 Map 这样的结构,后续就可以直接通过 key 获取 value 了。

如果 key 在 query string 中不存在,此时返回的就是 null。

2 通过 POST 借助 form 表单传参


如果前端是 form 表单格式的数据,后端还是使用 getParameter 来获取。

这里的 form 表单格式的数据也是键值对,和 query string 的格式是一样的,只是这部分内容在 body 中。

 <form action="postParameter" method="post">
     <input type="text" name="studentId">
     <input type="text" name="classId">
     <input type="submit" value="提交">
 </form>



在输入框中输入以下的数据。





当前显示 404 是因为 postParameter 还没有实现,但是不影响,打开 fiddler 抓包观察即可。



当前代码中的 action 属性里的 postParameter 得到的就是抓包结果中的 postParameter 这个路径。
method 属性 得到的就是抓包结果中的 POST 。

input 标签里的 name 属性里的 studentId 和 classId 实现的就是最下方的 studentId=10&classId=20。
而在输入框中输入的内容及决定了它们两个的值。


使用 getParameter 既可以获取到 query string 中的键值对,也可以获取到 form 表单构造的 body 中的键值对。

@WebServlet("/getparameter")
public class PostGetParameterServlet extends HttpServlet {
    
    
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        String studentId = req.getParameter("studentId");
        String classId = req.getParameter("classId");
        resp.setContentType("text/html");
        resp.getWriter().write("studentId = " + studentId + "classId = " + classId);
    }
}


抓包即可得到与之前相同的结果。



3 通过 json 格式传参


json 是一种非常主流的数据格式,也是键值对结构。

{
    
    
  classId: 20,
  studentId: 10
}


可以把 body 按照上面的格式来组织。

前段可以通过 ajax 的方式来构造出这个内容,也可以使用更简单的 postman 的方式。



打开 postman,勾选上述的选项,之后点击发送即可,由于还没有实现 getparameter2 的代码,
所以此时发送后,会报一个 404 错误。




接下来实现 getparameter2 的代码。

@WebServlet("/getparameter2")
public class PostParameter2 extends HttpServlet {
    
    
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        // 通过 getInputStream 把 req 对象里的 body 完整读取出来
        // 在流对象中读取多少个字节,取决于 Content-Length
        int length = req.getContentLength(); // 这是 body 实际的长度
        // 创建一个相同长度的字节数组
        byte[] buffer = new byte[length];
        // 借出 inputStream 来读取 body 的内容
        InputStream inputStream = req.getInputStream();
        inputStream.read(buffer);

        // 把这个字节数组转成 String,然后打印出来
        String body = new String(buffer, 0, length, "utf8");
        System.out.println("body =" + body);
        resp.getWriter().write(body);
    }
}


通过 Content-Length 得到 body 的长度,然后再按照这个长度从请求对象中读取数据,
于是就把 body 给读取出来了。





可以看到服务器与客户端都有了结果。

服务器这里打印的结果就是,从 请求 body 里读取的内容。


接下来使用 fiddler 抓包观察结果



理解它的流程



json 与 form 的区别:

json 格式代码的执行流程与上面通过 form表单传参的流程是类似的,只不过是传参的数据格式不同。

form 表单是形如 classId=20&studentId=10

json 格式则是形如:

{
    
    
  classId: 20,
  studentId: 10
}



当前通过 json 传递数据,服务器只是把整个 body 读出来了,但是没有按照键值对的的方式来处理,
也就是说,目前还不能根据 key 获取 value。

此时建议使用 第三方库 —— jackson

1、通过 https://releases.jquery.com/ 打开 maven 仓库


2、搜索 jackson,点击第一个


3、选择 2.14.1 版本


4、将 Maven 里的代码复制到 pom.xml 文件里的 dependency 标签里




接下来尝试更改代码的写法。

class Student {
    
    
    public int studentId;
    public int classId;
}

@WebServlet("/getparameter2")
public class PostParameter2 extends HttpServlet {
    
    
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        // 使用 jackson 涉及到的核心对象
        ObjectMapper objectMapper = new ObjectMapper();
        // readValue 可以把一个 jackson 格式的字符串转成 java 对象
        Student student = objectMapper.readValue(req.getInputStream(), Student.class);
        System.out.println(student.studentId + "," + student.classId);
    }
}


Student student = objectMapper.readValue(req.getInputStream(), Student.class);

这条语句中的 readValue 方法,会执行下面的流程。

1、从 body 中读取 json 格式的字符串

{
    
    
  classId: 20,
  studentId: 10
}



2、根据第二个参数类对象,创建 Student 实例


3、解析上述的 json 格式的字符串,处理成 map 键值对结构


4、遍历所有的键值对,看键的名字和 Student 实例的哪个属性名字匹配,
就把对应的 value 设置到该属性中



5、返回该 Student 实例


启动服务器再点击发送,会在服务器上得到下面这样的结果。

HttpServletResponse


1 代码示例

1.1 设置字符集


如果想让下面的代码显示出中文,此时就需要设置字符集 为 utf-8

@WebServlet("/getParameter")
public class GetParameterServlet extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        // 预期浏览器会发一个形如 /getParameter?studentId=10&classId=20 请求
        // 借出 req 里的 getParameter 方法就能拿到 query string 中的键值对内容了
        // getParameter 得到的是 string 类型的结果
        String studentId = req.getParameter("studentId");
        String classId = req.getParameter("classId");
        resp.setContentType("text/html");
        resp.getWriter().write("学生Id = " + studentId + "班级Id = " + classId);
    }
}




在设置之前会出现以上的结果。



接下来开始调用 setCharacterEncoding 设置字符集。

 resp.setCharacterEncoding("utf-8");


将上述的代码中添加上这条语句即可。


刷新页面可以看到正确的显示出来了。

需要注意的是 设置字符集那条语句必须是在 getWriter().write() 的上面。
如果写在了下面是不会生效的。


也可以把字符集和 ContentType 一起设置

 resp.setContentType("text/html; charset = utf-8");

1.2 重定向


以 3 开头的状态码,浏览器会自动跳转到指定的新地址。

@WebServlet("/redirectServlet")
public class RedirectServlet extends HttpServlet {
    
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        resp.sendRedirect("https://www.sogou.com");
    }
}


在地址栏中输入地址后,会跳转到指定的新地址。以上就是会跳转到搜狗的页面。


打开 fiddler 抓包并观察结果。




代码实现的第二种方式

 resp.setStatus(302);
 resp.setHeader("Location", "https://www.sogou.com/");


将代码修改为以上两句即可,这种写法是将上面的步骤给拆分成两步了。


下期来介绍服务器版本的表白墙案例!!!




猜你喜欢

转载自blog.csdn.net/m0_63033419/article/details/130118557