• HeadFirst Jsp 04 (请求和响应作为servlet)


    servlet 的存在就是为了客服服务, servlet的任务是得到一个客户的请求, 再发回一个响应.

    image

    由上图可知, web 容器会在启动后就加载所有的servlet类, 并为之创建实例和初始化

    注意: init方法是在第一个用户调用此servlet时被触发

    service() 方法属于servlet类, 容器创建的线程调用了 service() 方法. 并把 HttpRequest, HttpReponse 这两个对象传递给了这个新的线程. 

    容器首先根据配置文件找到对应的 servlet, 然后 自然而然的调用这个servlet 的 service 方法, 那么就不用再确认调用的doPost是否弄错了, 比如调用了别人的servlet.

    servlet 的继承关系

    image

    3大生命周期

    image

    image

    image

    image

    一般 servlet 都不会有很多实例, 从这来看, 就一个实例

    容器运行多个线程来处理对一个 servlet 的多个请求

    对应每个客户请求, 会生成一对新的请求和响应对象.

    注意:是容器根据请求中的url找到正确的servlet, 为这个请求创建分配了一个线程, 然后由这个线程调用

    servlet的service()方法, 把请求和响应作为参数传给它.

    image

    容器是如何处理用户请求的?

    1)用户点击一个链接,指向一个servlet而不是一个静态页面。

    2)web服务器接到这个请求后转发给容器。容器接着创建两个对象:HttpServletRequest和HttpServletResponse。

    3)容器根据请求中的URL找到相应的servlet,为这个请求创建一个线程,并把请求对象HtttpServletRequest和响应对象HttpServletResponse传递给这个servlet线程。

    4)线程接下来调用service()方法,根据请求的不同,service()方法调用doGet()和doPost()方法。

    5)doGet()方法生成动态页面,并把这个页面塞到响应对象里。

    6)service()方法结束,随之线程结束,容器把响应对象装换为一个HTTP相应,发送给客户,然后删除请求和响应对象。

    Servlet的生命周期

    注意他的一生都是由容器控制的。servlet一生中只有一个实例出现,但是有多个线程出现。

    加载类 Servlet .class文件

    实例化 构造函数运行

    初始化 容器调用 init() 方法(一生只调一次)

    service方法? servlet一生主要在这里度过

    销 毁? 销毁实例之前调用 destroy() 方法

    可回收? 等待垃圾回收等待垃圾回收

    servlet 在运行过程中, 变量的安全性问题

    (一)变量的安全性

    错误实例:

    public class test extends HttpServlet{

    String user = "" ;

    public void doGet(HttpServletRequest req , HttpServletResponse res) throws ServletException , IOException{

    user = req.getParameter("user");

    ......

    }

    }

    例如:a、b同时访问这个servlet,a提交的user=aaa,b提交的user=bbb。

    首先,servlet容器分配一个线程T-a来处理请求a,获取其user的值aaa,并赋给变量user。此时T-a时间片到了,servlet容器分配另外一个线程T-b来处理请求b,

    获取其user的值bbb,并覆盖变量user,当T-a线程重新获取执行权时,user已经“物是人非”了。

    因为user 是一个实例变量, 而这个实例(servlet) 一直都是“一个”实例,没有再增加, 所以他们调用的是同一个实例变量, 所以会有以上问题.

    这里可以类比:jdbc的事务管理,“丢失更新”和这个场景类似。

    解决方案:

    1)定义本地变量,将user在doGet方法中定义。

    因为user是本地变量,每一个线程都有user变量的拷贝,彼此不受影响。

    2)设置方法同步(或者同步块)

    因为设置了同步,可以防止多个线程同时调用doGet方法。但是所有请求该servlet的“请求”将串行处理,影响效率。同步实际是”排队”

    image

    幂等的定义: 一次一次可以重复执行的内容, doGet() 是幂等的, 可以返回执行, 而doPost()不是幂等的, 因为它可能修改服务器上的内容.

    如何判定使用GET还是POST

    Get: 简单的超链接. 默认使用Get,

    Post: method=”post”, submit,

    从请求对象中可以得到什么?

    客户的平台浏览器信息

    string client = request.getHeader(“User-Agent”);

    与请求相关的cookie

    Cookie[] cookies = request.getCookies();

    与客户相关会话(session)

    HttpSession session = request.getSession();

    请求的HTTP方法

    String theMethod = request.getMethod();

    请求的输入流

    InputStream input = request.getInputStream();


    响应

    响应要返回给客户, 这是浏览器得到, 解析并呈现给用户的东西, 一般你会使用响应对象得到一个输出流(通常是一个writer), 并使用这个流写出HTML(或其他类型内容), 返回给客户. ( 也可以生成一个jsp返回给客户 )

    image

    响应会经常调用两个方法:

    setContentType()   // 告诉浏览器你发回去的是什么

    getWriter()        // 用于输出字符

    例如你要从服务器请求一个jar文件, 服务器的返回代码是:

    public class CodeReturn extends HttpServlet {

        public void doGet(HttpServletRequest request, HttpServletResponse response)

        throws IOException, ServletException {

            response.setContentType(“application/jar”);   // 告诉服务器返回内容的类型

            ServletContext ctx = getServletContext();

            InputStream is = ctx.getResourceAsStream(“/bookCode.jar”);

            int read = 0;

            byte[] bytes = new byte[1024];

            OutputStream os = response.getOutputStream();

            while ((read = is.read(bytes)) != -1) {

                os.write(bytes, 0, read);   

            }

            os.flush();

            os.close();

        }

    }

    对于输出, 你只有两个选择, 字节或字符

    ServletOutputStream 输出字节

        ServletOutputStream out = response.getOutputSt();

        out.write(aByteArray);

    PrintWriter 输出字符

        PrintWriter writer = response.getWriter();

        writer.println(“some text and HTML”);

    利用重定向来响应, 而非servlet自己处理响应

    利用jsp 来处理响应

    image

    image

    重定向使得 servlet完全卸下担子, servlet只是调用 sendRedirect()方法:

    response.sendRedirect(“http://www.abc.com”);   // 地址直接重定向

    原路径: http://abc.com/myApp/cool/bar.do

    response.sendRedirect(“foo/stuff.html”);       // 相对路径 = http://abc.com/myApp/cool/foo/stuff.html

    reponse.sendRedirect(“/foo/stuff.html”);       // 绝对路径, 从根目录开始, 注意这是以”/”开头的, = http://abc.com/foo/stuff.html

    请求分派

    重定向让客户端完成工作 重定向 = 客户端

    请求分派要求服务器上某某来完成任务 请求分配 = 服务器

    image

  • 相关阅读:
    Python学习第15天_模块
    Python学习第14天_文件读取写入
    Python学习第13天_练习(图书馆的创建)
    Python学习第12天_类
    Python学习第11天_参数
    Python学习第10天_函数
    Python学习第九天_模块的应用
    Android Bluetooth HIDL服务分析
    Mac下CLion配置Google GTest小结
    MacOS通过homebrew安装老版本的软件
  • 原文地址:https://www.cnblogs.com/moveofgod/p/3389609.html
Copyright © 2020-2023  润新知