• 【Servlet】6:一篇文章搞懂Servlet对象的相互调用、数据共享


    本文章属于后端全套笔记的第三部分

    (更新中)【后端入门到入土!】Java+Servlet+JDBC+SSM+SpringBoot+SpringCloud 基础入门_m0_57265007的博客-CSDN博客一篇文章,后端入门到入土。包含 Java基础+高级、MySQL、JDBC、Servlet、SSM、SpringBoot、SpringCloud、项目 的笔记。https://blog.csdn.net/m0_57265007/article/details/127962617?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22127962617%22%2C%22source%22%3A%22m0_57265007%22%7D

    | 请求对象和响应对象 生命周期

    1.在Http服务器接收到浏览器发送的【Http请求协议包】之后,自动为当前的【Http请求协议包】生成一个【请求对象】和一个【响应对象】

    2.在Http服务器调用doGet/doPost方法时,负责将【请求对象】和【响应对象】作为实参传递到方法,确保doGet/doPost正确执行

    3.在Http服务器准备推送Http响应协议包之前,负责将本次请求关联的【请求对象】和【响应对象】销毁

    4.【请求对象】和【响应对象】生命周期贯穿一次请求的处理过程中

    5.形象来说:【请求对象】和【响应对象】相当于用户在服务端的代言人


    | Servlet之间的相互调用

    Servlet调用 基本概述

    • 某些来自于浏览器发送请求,往往需要服务端中多个Servlet协同处理。

    • 多个Servlet之间调用,根本目的是提高用户使用感受规则:无论本次请求涉及到多少个Servlet,用户只需要【手动】通知浏览器发起一次请求即可

    • 多个Servlet之间调用规则:重定向解决方案、请求转发解决方案


    重定向Servlet调用

    原理

    • 用户第一次通过【手动方式】通知浏览器访问OneServlet,OneServlet工作完毕后,将TwoServlet地址写入到响应头location属性中,

      将302状态码写入到状态行。浏览器检查到302状态后,寻找响应头中的 location,然后立即自动向服务器发起第二次请求

    • 即:浏览器至少发送两次请求,但是只有第一次请求是用户手动发送,后续请求都是浏览器自动发送

    • 重定向解决方案中,是通过地址栏通知浏览器发起下一次请求,因此通过重定向解决方案调用的资源文件接收的请求方式一定是【GET】

    • 缺点:重定向解决方案需要在浏览器与服务器之间进行多次往返,大量时间消耗在往返次数上,增加用户等待服务时间

    语法

    response.sendRedirect("请求地址");   //将地址写入到响应包中响应头中location属性
    • 请求地址可以是:外部网站的URL / 服务器内部的Servlet实现类对象的别名(即动态资源)/ 内部的静态资源

    • 如果是内部资源,则URL的构成为: /网站名/Servlet对象对应的别名

    示例

    1. <html>
    2. <head>
    3. <title>$Title$title>
    4. head>
    5. <body>
    6. <form action="/web4/s1" method="post">
    7. <input type="submit" value="重定向">
    8. form>
    9. body>
    10. html>

     

    (Servlet1和Servlet2代码完全一致,只是数字有区别。在此只展示Servlet1的代码)

    1. public class Servlet1 extends HttpServlet {
    2. @Override
    3. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    4. //...
    5. }
    6. @Override
    7. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    8. resp.setContentType("text/html;charset=utf-8"); //设置content-Type中的字符规则为UTF-8,以保证显示到网页为中文
    9. PrintWriter out = resp.getWriter(); //获取输出流
    10. //提示S1的 POST被调用
    11. System.out.println("Servlet1 POST 被调用");
    12. //重定向到S2
    13. System.out.println("重定向到 /web4/s2");
    14. resp.sendRedirect("/web4/s2");
    15. }
    16. }

     

    1. public class Servlet2 extends HttpServlet {
    2. @Override
    3. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    4. resp.setContentType("text/html;charset=utf-8"); //设置content-Type中的字符规则为UTF-8,以保证显示到网页为中文
    5. PrintWriter out = resp.getWriter(); //获取输出流
    6. //提示S2的 GET被调用
    7. System.out.println("Servlet2 GET被调用");
    8. }
    9. @Override
    10. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    11. //...
    12. }
    13. }

     

    1. "1.0" encoding="UTF-8"?>
    2. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    5. version="4.0">
    6. <servlet>
    7. <servlet-name>servlet1servlet-name>
    8. <servlet-class>Servlet1servlet-class>
    9. servlet>
    10. <servlet-mapping>
    11. <servlet-name>servlet1servlet-name>
    12. <url-pattern>/s1url-pattern>
    13. servlet-mapping>
    14. <servlet>
    15. <servlet-name>servlet2servlet-name>
    16. <servlet-class>Servlet2servlet-class>
    17. servlet>
    18. <servlet-mapping>
    19. <servlet-name>servlet2servlet-name>
    20. <url-pattern>/s2url-pattern>
    21. servlet-mapping>
    22. web-app>

     

    结论:通过重定向自动发起的请求,都是GET类型的~

     

    请求转发Servlet调用

    原理

    • 用户第一次通过手动方式要求浏览器访问OneServlet。OneServlet工作完毕后,通过当前的请求对象 代替浏览器 向Tomcat发送请求,申请调用TwoServlet。

      Tomcat在接收到这个请求之后,自动进入TwoServlet来完成剩余任务

    • 在请求转发解决方案中,浏览器只发送一个了个Http请求协议包。参与本次请求的所有Servlet共享同一个请求协议包,因此这些Servlet接收的请求方式与浏览器发送的请求方式保持一致

    • 请求次数:在请求转发过程中,浏览器只发送一次请求(无论本次请求涉及到多少个Servlet,用户只需要手动通过浏览器发送一次请求)

    • 优点:Servlet之间调用发生在服务端计算机上,节省服务端与浏览器之间往返次数增加处理服务速度

    语法

    1. RequestDispatcher report = request.getRequestDispatcher("/资源文件名"); //通过当前请求对象生成资源文件申请报告对象
    2. report.forward(当前请求对象,当前响应对象); //将报告对象发送给Tomcat

    • 请求转发由于是内部Tomcat调度,而tomcat只对当前网站内部的资源有调度权力,所以资源文件名只能是服务器内部的静态 / 动态资源

    • 资源文件名的书写注意细节:

      • 由于是Tomcat内部调用,都是在同一个部署的网站中,所以资源文件名【不需要写网站的部署别名】!【直接写上需要访问的静态/资源名】即可。名字前面不要漏了斜杠: / 

      • 如果写了网站名,就会导致URL出现两个网站名,404错误

    示例

    • web.xml 的配置文件和本小节中 “重定向Servlet调用” 一致,在此不再展示


    1. <html>
    2. <head>
    3. <title>$Title$title>
    4. head>
    5. <body>
    6. <form action="/web4/s1" method="post">
    7. <input type="submit" value="重定向">
    8. form>
    9. <form action="/web4/s2" method="get">
    10. <input type="submit" value="请求转发">
    11. form>
    12. body>
    13. html>

     

    1. public class Servlet1 extends HttpServlet {
    2. @Override
    3. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    4. resp.setContentType("text/html;charset=utf-8"); //设置content-Type中的字符规则为UTF-8,以保证显示到网页为中文
    5. PrintWriter out = resp.getWriter(); //获取输出流
    6. //提示S1的 GET被调用
    7. System.out.println("Servlet1 GET 被调用");
    8. }
    9. @Override
    10. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    11. //...
    12. }
    13. }

     

    1. public class Servlet2 extends HttpServlet {
    2. @Override
    3. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    4. resp.setContentType("text/html;charset=utf-8"); //设置content-Type中的字符规则为UTF-8,以保证显示到网页为中文
    5. PrintWriter out = resp.getWriter(); //获取输出流
    6. //提示S2的 GET被调用
    7. System.out.println("Servlet2 GET被调用");
    8. //请求转发到S1
    9. System.out.println("请求转发到 /web4/s1");
    10. //重定向中的URL无需写部署在Tomcat的网站名(因为本来就是内部调用了),只需要写资源名即可
    11. RequestDispatcher report = req.getRequestDispatcher("/s1"); //通过当前请求对象生成资源文件申请报告对象
    12. //RequestDispatcher report = req.getRequestDispatcher("/web4/s1"); //错误写法!不需要写网站部署名web4
    13. report.forward(req,resp); //将报告对象发送给Tomcat
    14. }
    15. @Override
    16. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    17. //...
    18. }
    19. }

     

    结论:

    通过请求转发的方式调用的其它Servlet的请求方式都是GET(因为共享一个请求、响应对象)

    请求转发的URL只需要写 /资源名 即可(因为本身就是在同一个网站内,Tomcat默认在URL写上了网站部署别名)


    | Servlet之间的数据共享

    Servlet数据共享 基本概述

    • 什么是数据共享:OneServlet工作完毕后,将产生数据交给TwoServlet来使用

    • 四种数据共享方案:

      • ServletContext接口【全局作用域对象】

      • Cookie类

      • HttpSession接口【会话作用域对象】

      • HttpServletRequest接口【请求作用域对象】


    ServletContext接口 数据共享

    基本介绍

    • ServletContext是来自Servlet规范的一个接口,其实现类由Http服务器提供

    • ServletContext实现类的对象被称为【全局作用域对象】

    • 若多个Servlet隶属于同一个网站下,则可以使用该方法进行Servlet数据共享

    原理说明

    • 每一个网站都存在一个全局作用域对象,该网站下的所有Servlet共享该对象

      如:OneServlet可以将一个数据存入到全局作用域对象,当前网站中其他Servlet此时都可以从全局作用域对象得到这个数据进行使用

    全局作用域对象生命周期

    • Tomcat启动过程中,自动为当前网站在内存中创建一个全局作用域对象

    • Tomcat运行期间,一个网站只有一个全局作用域对象

    • Tomcat运行期间,全局作用域对象一直处于存活状态(全局作用域对象生命周期贯穿网站整个运行期间)

    • Tomcat准备关闭时,负责将当前网站中全局作用域对象进行销毁处理

    语法

    • K是String,V是Object

    1. //通过【请求对象】向Tomcat索要当前网站中【全局作用域对象】
    2. ServletContext application = request.getServletContext();
    3. //将数据添加到全局作用域对象作为【共享数据】
    4. application.setAttribute("key1",数据);
    5. //从全局作用域对象获取之前添加的共享数据【注意一下数据类型默认是Object,需要转换】
    6. Object 数据 = application.getAttribute("key1");

    示例

    1. <html>
    2. <head>
    3. <title>$Title$title>
    4. head>
    5. <body>
    6. <form action="/web5/S1" method="get">
    7. <input type="text" name="v1">
    8. <input type="submit" value="发送数据v1到S1">
    9. form>
    10. body>
    11. html>

     

    1. "1.0" encoding="UTF-8"?>
    2. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    5. version="4.0">
    6. <servlet>
    7. <servlet-name>S1servlet-name>
    8. <servlet-class>S1servlet-class>
    9. servlet>
    10. <servlet-mapping>
    11. <servlet-name>S1servlet-name>
    12. <url-pattern>/S1url-pattern>
    13. servlet-mapping>
    14. <servlet>
    15. <servlet-name>S2servlet-name>
    16. <servlet-class>S2servlet-class>
    17. servlet>
    18. <servlet-mapping>
    19. <servlet-name>S2servlet-name>
    20. <url-pattern>/S2url-pattern>
    21. servlet-mapping>
    22. web-app>

     

    1. public class S1 extends HttpServlet {
    2. @Override
    3. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    4. String v1 = (String)req.getParameter("v1");
    5. System.out.println("收到了来自index的参数 v1 = " + v1);
    6. /*【把数据存入ServletContext】*/
    7. ServletContext application = req.getServletContext(); //通过【请求对象】向Tomcat索要当前网站中【全局作用域对象】
    8. application.setAttribute("key1",v1); //将数据添加到全局作用域对象作为【共享数据】
    9. System.out.println("S1的数据成功存入全局作用域对象");
    10. /*【请求转发跳转到S2】*/
    11. System.out.println("正在从S1跳转到S2");
    12. System.out.println("=========================================================");
    13. RequestDispatcher report = req.getRequestDispatcher("/S2");
    14. report.forward(req,resp);
    15. }
    16. }

    1. public class S2 extends HttpServlet {
    2. @Override
    3. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    4. System.out.println("跳转到了S2");
    5. //从全局作用域对象拿到S1存入的值
    6. ServletContext application = req.getServletContext();
    7. String v2Fromv1 = (String)application.getAttribute("key1");
    8. System.out.println("S2拿到了从S1存入的数据: v2Fromv1 = " + v2Fromv1);
    9. }
    10. }

     

    Cookie类 数据共享

    基本介绍

    • 来自于Servlet规范中一个工具类,存在于Tomcat提供servlet-api.jar中

    • 若两个Servlet来自于同一个网站,并且为同一个浏览器/用户提供服务,此时推荐借助于Cookie对象进行数据共享

    原理说明

    • 用户通过浏览器第一次向MyWeb网站发送请求申请OneServlet,OneServlet在运行期间创建一个Cookie存储与当前用户相关数据

      OneServlet工作完毕后,【Cookie写入到响应头】交还给当前浏览器,浏览器收到响应响应包之后,将cookie存储在浏览器的缓存

      一段时间之后,用户通过【同一个浏览器】再次向【myWeb网站】发送请求申请TwoServlet时,

      浏览器会【无条件地将myWeb网站之前推送过来的Cookie写入到请求头】发送过去

      此时TwoServlet在运行时,就可以通过读取请求头中cookie中信息,得到OneServlet提供的共享数据

    生命周期

    • 在默认情况下,Cookie对象存放在浏览器的缓存中。因此只要浏览器关闭,Cookie对象就被销毁掉

    • 在手动设置情况下,可以要求浏览器将接收的Cookie存放在客户端计算机上硬盘上,同时需要指定Cookie在硬盘上存活时间。

      在存活时间范围内,关闭浏览器关闭客户端计算机,关闭服务器,都不会导致Cookie被销毁。

      在存活时间到达时,Cookie自动从硬盘上被删除

    cookie.setMaxAge(60);  //cookie在客户端硬盘存活时间(单位:秒)  在此期间用户即使关闭浏览器,也不会导致Cookie销毁

    语法

    • 存数据:创建一个cookie对象,通过Cookie的构造方法保存共享数据(当前用户数据)

      • cookie相当于一个map。一个cookie中只能存放一个键值对

      • K和V都只能是String,且Key不能是中文,但是V可以是中文

    1. Cookie card = new Cookie("key1","V");
    2. resp.addCookie(card); //将cookie写入到响应头,交给浏览器

    在其它Servlet中取Cookie的数据:调用请求对象从请求头得到浏览器返回的Cookie
     

    1. //1.调用请求对象从请求头得到浏览器返回的Cookie
    2. Cookie cookieArray[] = request.getCookies();
    3. //2.循环遍历数据得到每一个cookie的key 与 value
    4. for(Cookie card:cookieArray){
    5. String key = card.getName(); //读取key "key1"
    6. String value = card.getValue(); //读取value "abc"
    7. }

    示例

    index 和 web.xml 的配置和上面一样,这里不再展示。

    1. public class S1 extends HttpServlet {
    2. @Override
    3. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    4. Cookie card = new Cookie("key1","S1的值1");
    5. Cookie card2 = new Cookie("key2","S1的值2");
    6. Cookie card3 = new Cookie("key3","S1的值3");
    7. resp.addCookie(card); //将cookie写入到响应头,交给浏览器
    8. resp.addCookie(card2);
    9. resp.addCookie(card3);
    10. resp.sendRedirect("/web5/S2"); //将地址写入到响应包中响应头中location属性
    11. }
    12. }

     

    1. public class S2 extends HttpServlet {
    2. @Override
    3. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    4. System.out.println("跳转到了S2");
    5. //1.调用请求对象从请求头得到浏览器返回的Cookie
    6. Cookie cookieArray[] = req.getCookies();
    7. //2.循环遍历数据得到每一个cookie的key 与 value
    8. int i = 1;
    9. for(Cookie card:cookieArray){
    10. String key = card.getName(); //读取key "key1"
    11. String value = card.getValue(); //读取value "abc"
    12. System.out.println("数据" + i++ + ":" + key + " = " + value);
    13. }
    14. }
    15. }

     

     

    HttpSession接口 数据共享

    基本介绍

    • 来自于Servlet规范下一个接口。存在于Tomcat中servlet-api.jar,其实现类由Http服务器提供

    • 开发人员习惯于将HttpSession接口修饰对象称为【会话作用域对象】

    • 如果两个Servlet来自于同一个网站,并且为同一个浏览器 / 用户提供服务,此时推荐借助于HttpSession对象进行数据共享

    原理说明

    • HttpSession 和 Cookie 的区别

      • 【存储位置不同】Cookie:存放在客户端计算机(浏览器内存/硬盘);HttpSession:存放在服务端计算机内存

      • 【可存储数据类型不同】Cookie:K只能是英文String,V只能是String;HttpSession:K的String可以含中文,V可以存储任意类型的共享数据Object

      • 【数据数量】一个Cookie对象只能存储一个共享数据;HttpSession使用map集合存储共享数据,所以可以存储任意数量共享数据

    • HttpSession 是存放在服务端计算机内存中的,它是如何知道不同的HttpSession对象对应的是哪个用户呢?

      • HttpSession通过Cookie,将用户与HttpSession关联起来

    生命周期

    • 用户与HttpSession关联时使用的Cookie只能存放在浏览器缓存中

    • 浏览器关闭时,意味着用户与他的HttpSession关系被切断

    • 由于Tomcat无法检测浏览器何时关闭,因此在浏览器关闭时并不会导致Tomcat将浏览器关联的HttpSession进行销毁

      为了解决这个问题,Tomcat为每一个HttpSession对象设置【空闲时间】,这个空闲时间默认30分钟

      如果当前HttpSession对象空闲时间达到30分钟,此时Tomcat认为用户已经放弃了自己的HttpSession,此时Tomcat就会销毁掉这个HttpSession

    • 手动修改HttpSession空闲时间:

      在web.xml中设置 5 (单位:分钟)

    语法

    添加数据

    1. //1.调用请求对象向Tomcat索要当前用户在服务端的私人储物柜
    2. HttpSession session = request.getSession(); //如果当前用户还没有session对象,则服务端创建一个新的
    3. HttpSession session = request.getSession(false); //如果当前用户还没有session对象,则服务端不会创建新的,会返回一个null
    4. //2.将数据添加到用户私人储物柜
    5. session.setAttribute("key1",共享数据)

     获取数据

    1. //1.调用请求对象向Tomcat索要当前用户在服务端的私人储物柜
    2. HttpSession session = request.getSession();
    3. HttpSession session = request.getSession(false); //如果当前用户还没有session对象,则服务端不会创建新的,会返回一个null
    4. //2.从会话作用域对象得到OneServlet提供的共享数据
    5. Object 共享数据 = session.getAttribute("key1");

    示例

    1. public class S1 extends HttpServlet {
    2. @Override
    3. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    4. //1.调用请求对象向Tomcat索要当前用户在服务端的私人储物柜
    5. HttpSession session = req.getSession(); //如果当前用户还没有session对象,则服务端创建一个新的
    6. //2.将数据添加到用户私人储物柜
    7. session.setAttribute("键1","值V1");
    8. //转到Servlet1中
    9. resp.sendRedirect("/web5/S2"); //将地址写入到响应包中响应头中location属性
    10. }
    11. }

     

    1. public class S2 extends HttpServlet {
    2. @Override
    3. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    4. //1.调用请求对象向Tomcat索要当前用户在服务端的私人储物柜
    5. HttpSession session = req.getSession();
    6. //2.从会话作用域对象得到OneServlet提供的共享数据
    7. Object o1 = session.getAttribute("键1");
    8. System.out.println(o1);
    9. }
    10. }

     

     

    HttpServletRequest接口 数据共享

    基本介绍

    • 在同一个网站中,如果两个Servlet之间通过【请求转发】方式进行调用,彼此之间共享同一个请求协议包。

      而一个请求协议包只对应一个请求对象,因此servlet之间共享同一个请求对象,

      此时可以利用这个请求对象在两个Servlet之间实现数据共享

    • 在请求对象实现Servlet之间数据共享功能时,开发人员将请求对象称为【请求作用域对象】

    语法

    1. //将数据添加到【请求作用域对象】中attribute属性
    2. req.setAttribute("key1",数据); //数据类型可以任意类型Object
    3. //从当前请求对象得到OneServlet写入到共享数据【当前Servlet必须和之前的Servlet共用一个请求对象。即请求转发】
    4. Object 数据 = req.getAttribute("key1");

     示例

    1. public class S1 extends HttpServlet {
    2. @Override
    3. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    4. req.setAttribute("键1","值V"); //数据类型可以任意类型Object
    5. //请求转发
    6. RequestDispatcher report = req.getRequestDispatcher("/S2"); //通过当前请求对象生成资源文件申请报告对象
    7. report.forward(req,resp); //将报告对象发送给Tomcat
    8. }
    9. }

     

    1. public class S2 extends HttpServlet {
    2. @Override
    3. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    4. System.out.println("跳转到了S2");
    5. Object o1 = req.getAttribute("键1");
    6. System.out.println(o1);
    7. }
    8. }

     

     

  • 相关阅读:
    Python下unittest框架的核心组件使用
    多肽标签VSV-G Tag、YTDIEMNRLGK
    element plus 切换页面报错‘emitsOptions‘和‘insertBefore‘
    关于内存条的知识要点⑴
    1.ClickHouse系列之Docker本地部署
    2023届C/C++软件开发工程师校招面试常问知识点复盘Part 3
    云边端协同场景下的“AI+”视频融合能力,如何赋能多行业应用?
    c++11 动态内存管理 -未初始化存储
    一个集成的BurpSuite漏洞探测插件1.1
    记一次攻防实战渗透
  • 原文地址:https://blog.csdn.net/m0_57265007/article/details/128005937