[Day 10] Listener 侦听器

前言

今天来整理一下Listener监听器
为本书的范围ch5


定义

因为ServletContext只能提供字串类的初始参数
无法将物件类的型态传入DD里面
因此Listener就是要负责侦听Context初始化前
先去执行一个独立的类别
去解析所要的参数,取出其内容
照本书的例子是说明使用在建立资料库连接的部分

主要需要下列部分:

  • XXXXServletContextListener
    =>实作ServletContextListener interface
    会从DD里取出初始参数,并呼叫目标类别,设定在新的Object

  • 目标属性的类别
    =>设定SrevletContext的属性

  • XXXXServlet
    =>继承HttpServlet,使用目标属性去呼叫其方法取得属性

  • DD设定档(web.xml)
    =>设定一个初始参数给listener,供XXXXServletContextListener使用

分类

Context只是其中一种的Listener
Listener总共有分成八种interface:

  • ServletContextAttributesListener
    内容:可确认Web应用程序Context中的属性新增或删除取代
    事件类型:ServletContextAttributesEvent

  • ServletContextListener
    内容:可确认Context是否被建立或是destory
    事件类型:ServletContextEvent

  • ServletRequestListener
    内容:可记录每次的request
    事件类型:ServletRequestEvent

  • HttpSessionListener
    内容:纪录正在活动的Session
    事件类型:HttpSessionEvent

  • HttpSessionAttributeListener
    内容:提供Session属性被新增移除或取代的method
    事件类型:HttpSessionBindingEvent

  • HttpSessionActivationListener
    内容:提供类别Session被移到另一个JVM时(或从别的JVM移入),提供相关的method
    事件类型:HttpSessionEvent

  • HttpSessionBindingListener
    内容:当此类别物件被Bind到某个Session或是从Session移除时,提供相关的method
    事件类型:HttpSessionBindingEvent

  • ServletRequestAttributeListener
    内容:提供Request属性被新增移除或取代的method
    事件类型:ServletRequestAttributeEvent

属性VS.参数

  • 属性
    一种Object,被设定在三种ServletAPI上(Context,Request,Session)
    设定的方法皆须透过物件的method
    所以在取出内容时需做转型的动作
    较重要的部分为每个属性存在的scope

    属性就像被钉在布告栏上的物件
    主要有两个重要问题
    谁能到看到或张贴这个布告栏?还有它能存在多久?

  • 参数
    属于一种字串String
    设定在DD的值
    可直接透过getParameter(String s)或getInitParameter(String s)呼叫

Context属性

Scope: WEB application的每个部分都可以使用存取
因为Context属于全域变数的概念
意即每个Servlet(就是多个thread)都可以使用它
所以他不是thread-safe的

因此会在呼叫getServletContext()时,加上synchronized

synchronized(getServletContext()){
  getServletContext().setAttribute("XXX","XXX");
}

Request属性

Scope: 同样的ServletRequest才可以使用存取
只有Request是thread-safe的
会利用RequestDispatcher去做分派(使用forward方法)

ArrayList.setAttribute("xxxx",objResult);

RequestDispatcher view = request.getRequestDisoatcher("show.jsp")  
view.forward(request,response);

关于RequestDispatcher的路径参数
在使用上也有点差异:

  • 透过ServletRequest取得
    若路径有加上/,代表从Container的跟目录算起
    若没有,则会从request的同一层去找相关的JSP

    request.getRequestDispatcher("/Index.jsp").forward(request, response);
    //不管多加几个斜线,container就是会从根目录开始去找
    
    request.getRequestDispatcher("Index.jsp").forward(request, response);
    //会从request的同一层目录去找对应的JSP
    
  • 透过ServletContext取得
    不能指定要不要加上斜线
    必须用/开头表示路径

    RequestDispatcher result = getServletContex.getRequestDispatcher("/Index.jsp");
    //必须用/开头表示路径
    result.forward(request, response);
    
  • forward(request, response)
    确认回应会将所有结果传回client
    若在这之前已经先将结果传送出去了(eg. OutputStream 的flush())
    则不能再使用此方式用forward去处理
    会得到IllegalStateException

Session属性

Scope:同样的HttpSession才可以使用存取
虽然Session只对一个client
但一个client有可能有多个Session(eg.开多个浏览器)

和Context相同,加上synchronized

synchronized(session)){
  session.setAttribute("XXX","XXX");
}

小结

今天先将此部分先简单的做整理
后续在去做补充及实际的练习