Head First Jsp&Servlet笔记2 servlet

来源:互联网 发布:c语言实现面向对象 编辑:程序博客网 时间:2024/06/05 06:33

一,Servlet入门

1.1 servlet响应过程:

  1. 用户点击一个链接,指向一个servlet。
  2. 容器“看见”链接指向servlet,所以容器创建两个对象HttpServletRequest和HttpServletResponse。
  3. 容器根据部署描述文件(DD)映射找到对应的servlet,为这个请求创建或分配一个线程,并调用这个servlet的service()方法,将创建的请求和响应对象作为参数传递给它。
  4. Service()方法根据http方法确定调用doGet或doPost处理客户请求。
  5. Servlet将响应对象通过响应写回给客户。
  6. Service()结束,线程撤销或返回到线程池,请求和响应对象垃圾回收。

1.2 servlet生命周期

  1. 容器启动时,会搜索servlet类并将之加载至内存。
  2. Servlet类通过构造函数实例化生成servlet对象(此时还不是servlet)
  3. 在调用service()服务之前,Init()初始化生成servlet
  4. 容器创建一个新的线程负责调用servlet的service()方法为客户服务。
  5. 全部服务结束,容器调用destroy()方法清理servlet

Init和destroy方法在servlet一生只调用一次。

这里写图片描述

1.3 servlet体系

这里写图片描述

1.4 servlet调用

  1. 在servlet实例创建之后,为用户提供服务之前,容器调用Init()方法初始化servlet。
  2. 用户到来之后,容器会为每一个请求创建一个线程,并创建新的请求和响应对象,并调用service()。
  3. Service()方法根据http方法确定调用doGet或doPost。
    也就是说,容器通过运行多个线程负责处理对一个servlet的多次请求服务,每一个线程只是负责运行service()方法(doGet或doPost方法)。
    对一个servlet的多次请求,并不是创建多个servlet实例,而是创建多个线程。

1.5 Servlet特权

  1. 在调用Init之前,servlet实例只是一个普通的对象;调用之后,servlet才会得到所有特权,如从ServletConfig何ServletContext得到容器信息。

    • ServletConfig:每一个Servlet都有各自的ServletConfig,用于向Servlet传递部署时信息,也可以用于获取ServletContext,其参数在DD描述
    • ServletContext:每一个Web应用都有自己的ServletContext,用于存储web应用参数信息,也在DD部署,可获取服务器信息等
  2. Servlet的真正任务是处理请求。

二 请求和响应

2.1 http方法:

这里写图片描述

2.2 Get和Post的区别

  1. Get和Post都能发送参数(默认选择get方法)
    • get发送的参数数据有限制,且只能加在url后面(url?…&),可直接在URL看见
    • Post发送的参数无限制,且放在发送的消息体中

2.3 幂等请求、非幂等

  1. post提交的数据可能用于不可逆转的事务,若客户端重复某种请求,可能产生不好的影响,因此是非幂等的。
  2. Get是幂等的,重复某件事情不会产生预想不到的副作用。

2.4 请求对象Request

这里写图片描述

2.5 响应对象Response

  1. 响应是为了向客户发送数据,主要用setContentType(String)设置响应内容类型和getWriter()获取响应输出流。
  2. 文件类型

    这里写图片描述

  3. Servlet采用响应重定向让用户重新定位URL,用法:response.sendRedirect(String); String存在两种方式:

    • 绝对URL地址
    • 相对URL地址:前面加”/”,则此时重定向的URL在当前地址下重新定位
  4. 请求分派:request.getRequestDispatcher(“url”).forward(req,resp); 将当前请求重新分配到另一个Servlet或jsp,直接在服务器中完成。

  5. 重定向:response.sendRedirect(String); 将重定向的地址返回给用户,让用户重新请求。

三 web应用整体

3.1 servlet初始化参数

  1. 当想要配置servlet参数信息时,而不是硬部署到servlet代码中去,可以在DD(web.xml)部署servlet初始化参数,然后,在servlet中通过servletConfig获取初始化参数获取。
    格式: getServletConfig().getInitParameter(“mainEmail”);

        <servlet>       .....        <init-param>            <param-name>mainEmail</param-name>            <param-value>zzxy@126.com</param-value>        </init-param>    </servlet>
  2. servlet初始化之前(init)不能获取初始化参数。
    容器只在初始化servlet之前读一次初始化参数,读完后就不能改变了;改变参数只能重新部署容器。

  3. 过程
    容器在初始化之前读取DD,并为这个servlet创建一个ServletConfig实例。
    (2)容器为每一个初始化参数提供String键值对,并向ServletConfig提供指向键值对参数的引用。
    (3)容器在init()初始化Servlet时,传入servletConfig的引用(参照前面init方法声明)

  4. servlet初始化参数只有该servlet才能调用,想要其他jsp等调用,可以将参数传入request属性,再通过获取属性获取。
    req.setAttribute(n, v);
    String s = req.getAttribute(n);

3.2,ServletContext初始化参数

  1. 想要整个web应用都能获取某项初始化参数,可以部署ServletContext初始化参数,再通过ServletContext对象获取参数。

    <web-app>    ....    <context-param>        <param-name>mainEmail</param-name>        <param-value>xzssd@zju.com</param-value>    </context-param></web-app>
  2. Servlet初始化参数:只有部署了该的Servlet才能获取
    上下文初始化参数:整个web应用的Servlet和jsp都能应用

  3. 整个web应用只有一个ServletContext,应用下的各个Servlet都有各自的ServletConfig

  4. web应用初始化:
    (1)容器读取DD,获取中的参数并为其创建string键值对。
    (2)容器创建ServletContext对象,并为其提供到各键值对的引用。
    (3)Web 应用上的Servlet和jsp即可通过ServletContext访问初始化参数。

  5. 初始化参数是部署时参数,直接在DD中部署值,可在运行中获取,而不能设置。

  6. 获取ServletContext对象:

    • [this.]getServletContext();
    • getServletConfig().getServletContext(); 通过ServletConfig获取(当Servlet未继承HttpServlet 或GenericServlet,由于getServletContext()是从GenericServlet继承的)
  7. 当想要保存的是一个对象而不是string类时,可以用ServletContext属性进行保存获取
    setAttribute(string,object):设置属性
    object getAttribute(string):获取属性
    removeAttribute(string):删除属性

tips:
(1)当重复设置同一个属性时,后设置的属性值将替换前面的属性值,并返回前面的属性值。
(2)删除属性时,返回被删除的属性值。

3.3 监听器

  1. 当想要初始化之前执行某些操作,由于Servlet没有main方法,可以采用监听器监听某些动作,当监听动作发生时,会执行相应的触发动作。如上下文监听器ServletContextListener接口。

  2. ServletContextListener有两个触发动作
    contextDestroyed(ServletContextEvent sce) 上下文销毁时触发
    contextInitialized(ServletContextEvent sce) 上下文(web)初始化时触发

  3. 监听流程:

    • 首先在DD注册监听器,格式如下:
    <listener>    <listener-class>com.web.MyServletContextListener</listener-class><listener>
    • 容器部署时会读取DD文件,根据描述找到实现了ServletContextListener接口的com.web.MyServletContextListener类,并为其实例化。
    • 当上下文初始化时,调用监听者MyServletContextListener的contextInitialized方法,其中ServletContextEvent有一个ServletContext的引用,可以通过getServletContext()获取上下文对象获取参数进行操作。
  4. HttpSessionBindingListener:实现了该监听器接口的类,当该监听器被添加到会话属性中去(setAttribute)或者从会话中删除时,会触发相应的动作。

    ValueBound(HttpSessionBindingEvent e);      //处于会话中触发    ValueUnbound(HttpSessionBindingEvent e);    //从会话中移除触发
  1. 每实现一个监听器类,就需要在DD进行部署,注意会话绑定(HttpSessionBindingListener)不需要在DD配置。

3.4 属性

  1. 属性是绑定在3个API对象ServletContext、HttpSession、HttpServletRequest中的键值对(名:String,值:Object对象)。

  2. 属性与参数

    属性 参数 类型 上下文、会话、请求对象 上下文初始化参数
    Servlet初始化参数
    请求参数 设置方法 setAttribute(String,Object) 部署时参数,都在DD中部署,不能设置 获取方法 Object getAttribute(String) getInitParameter(String) 返回类型 Object String


    注意
    (1)请求参数是用户向服务端发送请求时传递的参数,可以调整查询串,用getParameter(S)获取。
    (2) 获取属性后注意要进行类型转换

  3. 属性作用域

    可访问性 生命周期 适用于 上下文 整个web应用的所有对象 ServletContext的生命周期,即整个web应用的生命周期 整个应用共享的数据,如数据库连接 会话 能访问该特定会话的所有Servlet或jsp 会话的生命周期 与客户会话有关的数据与资源,如购物车 请求对象 能访问该请求对象的所有部分,一般即为接收转发请求的所有Servlet和jsp,加上监听器 请求的生命周期,即service()方法结束。 将model状态从controller转移到view中或传递客户请求的数据。


  4. 操作方法 : 3个属性作用域操作属性的方法是一致的。

    Object getAttribute(String):      //获取属性对象值,注意转换类型void setAttribute(String, Object)://设置属性viod removeAttribute(String):     //删除属性Enumeration getAttributeNames():  //获取属性名称的Enumeration


3.5 线程安全

  1. ServletContext上下文属性

    1. Sevlet上下文属性是非线程安全的,由于整个web应用的Servlet都能方法上下文属性,可能产生多个线程访问该属性

    2. 同步方法
      1)直接同步服务方法doGet或doPost,无效,同步服务方法会导致一个Servlet只能创建一个线程,且也无法阻止其他Servlet的线程来访问上下文属性。
      2)对上下文对象加锁,对所有访问上下文属性的语句加上下文对象的锁,使之每一个线程同步访问上下文属性,格式:

            synchronized(getServletContext()){          //对上下文对象加锁,封装所有操作上下文属性的方法            ServletContext sc = getServletContext();            sc.setAttribute("ball", "22");            String mon = (String)sc.getAttribute("ball");            out.println("money="+mon);        }
  2. 会话属性

    1. HttpSession同样不是线程安全的,一个会话中的多个Servlet也可以访问会话属性
    2. 同步方法:对会话对象加锁,封装所有操作会话属性HttpSession session = request.getSession();

      ttpSession session = request.getSession();synchronized(session){    //对会话对象加锁    session.setAttribute("bot", "442");    out.println("money="+(String)session.getAttribute("bot"));}


  3. 只有请求属性和局部变量(servlet)是线程安全的。

    1. 由于每一线程负责运行客户的每一个请求,因此请求属性、局部变量都对应一个线程,因此是安全的。
    2. Servlet中实例变量不是线程安全的,若存在一个Servlet的多个线程,则实例变量可被每个线程访问。因此,若想要线程安全的状态,Servlet中一般不设置实例变量,若要设置,则应声明为final属性。


3.6 请求分派与请求属性

  1. 请求分派

    1. 从request对象中获取:其中,若url不加”/”,则表示为相对路径,相对于当前路径下
      若加“/”,表示从web应用的根目录下开始.

      RequestDispatcher view = req.getRequestDispatcher("url");
    2. 从ServletContext获取:其中只能加”/”,从根目录下开始。

      RequestDispatcher view=getServletContext().getRequestDispatcher("/result.jsp");
  2. 转发属性
    view.forward(request, response);

  3. 若Servlet已经response提交响应(out.println()),再请求转发或重定向会发生illegalStateException。

四 会话管理

4.1 会话

  1. 对应同一个客户,HttpSession对象可以跨多个请求(Servlet线程)保持会话模式。
    即,与一个用户的会话期间,HttpSession对象可以持久化存储,对于会话期间,客户所做的所有请求信息都可以在Session中保存获取。

  2. 对于容器来说,每一个请求都默认为来自一个新的客户,创建一个新的会话。

  3. 想要容器区分每次请求是属于哪个客户的会话,因此需要为每一个用户设置一个唯一的会话ID,流程:
    (1)针对客户的第一次请求,容器会为客户生成一个唯一的会话ID,并通过响应返回给客户。
    (2)客户在以后的每一次请求中都发送自己的会话ID,容器会通过该ID找到客户对应的会话。

  4. 客户和容器是通过cookie来交换会话ID的 ,且容器会默认地完成所有cookie的工作。
    客户第一次请求,容器创建会话返回会话ID:

    HttpSession session = requset.getSession();//只需要向请求对象申请一个会话,其他的创建会话ID、创建cookie、将会话ID放在cookie中、将cookie作为响应的一部分返回给客户等都由容器自动完成。
    从客户请求根据返回的会话ID找到用户对应的会话:HttpSession session = requset.getSession();//与创建会话ID方法一样,会话id不可见,容器自动完成。
      HttpSession session = request.getSession();        if (session.isNew()){            //会话为新建的会话,即直接返回给用户        }else{            //为用户返回的会话ID,找到与该ID匹配的会话        }
  5. 若想要已经存在的客户会话:

    //返回一个已有会话,若无与用户匹配的会话,返回null        HttpSession session = request.getSession(false);        if(session == null){            //创建新会话            session = request.getSession();        }else{            //操作        }
    getSession(Boolean);参数为true时,当请求没有匹配的会话,则会创建一个新会话,无参数同理。若为false,当没匹配会话时,返回null
  6. 当客户不支持cookie时,可以使用URL重写在客户和容器之间传递会话ID

    1. 在写至响应的HTML中,URL重写会把会话ID添加到其中所有URL的后面,格式:
      URL+;jsessionid=XXX
    2. URL重写中,会话id会作为请求URL的最后“额外”信息返回给容器。
    3. 若客户不接受cookie,容器会自动地完成URL重写,但必须显示地对所有重写的URL进行编码,其他工作(添加会话id到URL)容器会自动地完成。
    4. 对URL进行编码,需要调用response.encodeURL(String);
      out.println(“Click here“);
    5. 若想在重定向中使用会话,可以用response.encodeRedirectURL(“/BeerSelect.do”);编码重定位URL。
    6. 容器在使用会话时,首先会尝试使用cookie进行会话ID交换,若不支持cookie,再回采用URL重写,此时需要编码所有重写的URL。
    7. 不能对静态页面完成URL重写,使用会话必须使用动态页面。
  7. 删除会话的情况

    1. 会话超时
      1)在DD中配置会话超时时间

      <!--设置会话超时--><session-config>    <session-timeout>15</session-timeout> <!--分钟为单位--></session-config>

      2)会话对象设置时间,会话时间设为-1后,会话永远不会到期。
      session.setMaxInactiveInterval(15*60); //秒数为单位

    2. 会话对象调用invalidate()

    3. 应用结束(崩溃或取消部署)
  8. Cookie

    1. Cookie不仅可用于在客户端和服务器间传递会话ID,还可用于在客户和服务器中传递信息,每一个Cookie都是一个String键值对对象,Cookie对象声明:new Cookie(String, String).
    2. 当客户想要长时间保持信息时(如用户名),客户在第一次请求时,Servlet从请求参数中获取信息,创建Cookie并发送给用户,用户保存在客户端,当用户再次发送请求时,用户会在每一个请求中将Cookie发回给服务端(自动),服务器从Cookie得到信息分清客户。
    3. 当客户会话消失时,会话Cookie就会消失,但可以设置Cookie时间使之在客户端持久存储。

      QuickExample1:服务端设置Cookie       String name = request.getParameter("name");       //创建Cookie        Cookie cookie = new Cookie("username", name);        //设置Cookie在客户端保存时间        cookie.setMaxAge(30*60);        //将Cookie发给客户        response.addCookie(cookie);QuickExample2:服务端获取Cookie       Cookie[] cookies = request.getCookies();         for(int i = 0; i < cookies.length; i++){            Cookie cookie = cookies[i];            if(cookie.getName().equals("username")){                String name = cookie.getValue();                out.println("Hello"+name);                break;            }        }

      由于Cookie没有getValue(String)的方法,只能首先获取Cookie数组,再在数组中寻找。

  9. 会话生命周期事件

    事件 监听器与监听事件 生命周期 创建会话:容器第一次创建会话,即此时用户未使用该会话访问服务器。
    删除会话:容器置会话无效。 HttpSessionListener
    HttpSessionEvent 属性 增加属性:setAttribute
    删除属性:removeAttribute
    替换属性:重新调用setAttribute HttpSessionAttributeListener
    HttpSessionBindingEvent 迁移 会话钝化:容器在将会话迁移到另一个VM前
    会话激活:容器已经将会话迁移到另一个VM HttpSessionActivationListener
    HttpSessionEvent


  10. 会话迁移
    (1)分布式web应用:应用的各部分复制到网络的多个结点。一个用户的请求可能发送到多个JVM环境中,则web应用中ServletConfig、HttpSession、ServletContext其分布如下:
    1)只有HttpSession会话对象会从一个VM 迁移到另一个VM,其它均在每一个VM中进行复制
    2)每一个VM中都有一个web应用的ServletContext,每个VM中的每一个Servlet都有自己的ServletConfig;但对于web应用一个特定的会话ID,整个分布式系统上只有一个HttpSession对象。

  11. 迁移过程:
    1)当客户的第一次请求经过负载平衡服务器发送到VM1后,第二个请求发送到VM2,VM2根据会话ID发送当前会话对象在VM1,则将会话对象从VM1迁移到VM2(VM1会话钝化,VM2会话激活)
    2)会话从一个VM迁移到另一个VM时,实现了java.io.Serializable的属性值(串行化)会转移到新的JVM中

11,会话监听器

这里写图片描述

原创粉丝点击