图片 5

JavaWeb监听器的使用,监听域对象创造和销毁的Listener

JavaWeb监听器的使用(一)监听上下文和会话信息,javaweb监听器

1.监听上下文的类

 1 package com.examp.ch9;
 2 
 3 import java.io.FileOutputStream;
 4 import java.io.PrintWriter;
 5 import java.util.Date;
 6 
 7 import javax.servlet.ServletContext;
 8 import javax.servlet.ServletContextAttributeEvent;
 9 import javax.servlet.ServletContextAttributeListener;
10 import javax.servlet.ServletContextEvent;
11 import javax.servlet.ServletContextListener;
12 
13 public final class MyServletContextListener
14 implements ServletContextListener, ServletContextAttributeListener
15 {
16 private ServletContext context = null;
17 
18 public void contextDestroyed(ServletContextEvent sce)
19 {
20   logout("调用contextDestroyed()方法-->ServletContext被销毁");
21   this.context = null;
22 }
23 
24 public void contextInitialized(ServletContextEvent sce)
25 {
26   this.context = sce.getServletContext();
27   logout("调用contextInitialized()方法-->ServletContext初始化");
28 }
29 
30 public void attributeAdded(ServletContextAttributeEvent scae)
31 {
32   logout("调用attributeAdded('" + scae.getName() + "', '" + scae.getValue() + 
33     "')方法-->增加了一个属性");
34 }
35 
36 public void attributeRemoved(ServletContextAttributeEvent scae)
37 {
38   logout("调用attributeRemoved('" + scae.getName() + "', '" + scae.getValue() + 
39     "')方法-->删除了该属性");
40 }
41 
42 public void attributeReplaced(ServletContextAttributeEvent scae)
43 {
44   logout("调用attributeReplaced('" + scae.getName() + "', '" + scae.getValue() + 
45     "')方法-->更改了该属性");
46 }
47 
48 private void logout(String message)
49 {
50   PrintWriter out = null;
51   try
52   {
53     out = new PrintWriter(new FileOutputStream("E:\\contextLog.txt", true));
54     out.println(new Date().toLocaleString() + message);
55     out.close();
56   }
57   catch (Exception e)
58   {
59     out.close();
60     e.printStackTrace();
61   }
62 }
63 }

2.监听Http会话的类

图片 1

 1 package com.examp.ch9;
 2 
 3 import java.io.FileOutputStream;
 4 import java.io.PrintWriter;
 5 import java.util.Date;
 6 
 7 import javax.servlet.ServletContext;
 8 import javax.servlet.ServletContextEvent;
 9 import javax.servlet.http.HttpSessionAttributeListener;
10 import javax.servlet.http.HttpSessionBindingEvent;
11 import javax.servlet.http.HttpSessionEvent;
12 import javax.servlet.http.HttpSessionListener;
13 /*
14  * HttpSessionListener Http会话的创建、销毁
15  * HttpSessionAttributeListener 监听会话中属性的改变
16  */
17 public final class MySessionListener implements HttpSessionAttributeListener, HttpSessionListener
18 {
19 ServletContext context;//创建一个context对象
20 int users = 1;//初始化用户数量为1
21 /*
22  * 在session中添加对象时触发此操作 笼统的说就是调用setAttribute这个方法时候会触发的
23  * 
24  */
25 public void attributeAdded(HttpSessionBindingEvent event)
26 { 
27   logout("attributeAdded('" + event.getSession().getId() + "', '" + 
28     event.getName() + "', '" + event.getValue() + "')");
29 }
30 /*
31  * 修改、删除session中添加对象时触发此操作  笼统的说就是调用 removeAttribute这个方法时候会触发的
32  * 
33  */
34 public void attributeRemoved(HttpSessionBindingEvent event)
35 {
36   logout("attributeRemoved('" + event.getSession().getId() + "', '" + 
37     event.getName() + "', '" + event.getValue() + "')");
38 }
39 /*
40  * 在Session属性被重新设置时
41  * 
42  */
43 public void attributeReplaced(HttpSessionBindingEvent se)
44 {
45   logout("attributeReplaced('" + se.getSession().getId() + ",'" + se.getName() + "','" + se.getValue() + "')");
46 }
47 
48 
49 
50 /*
51  * 新建一个会话时候触发也可以说是客户端第一次和服务器交互时候触发
52  */
53 public void sessionCreated(HttpSessionEvent event)
54 {
55   System.out.println(users);
56   this.users += 1;//获取ID 和用户个数
57   logout("sessionCreated('" + event.getSession().getId() + "'),目前有" + this.users + "个用户");
58   this.context.setAttribute("users", new Integer(this.users));//将用户数存入context
59 }
60 /*
61  * 销毁会话的时候  一般来说只有某个按钮触发进行销毁 或者配置定时销毁 
62  * 
63  */
64 public void sessionDestroyed(HttpSessionEvent event)
65 {
66   this.users --;
67   logout("sessionDestroyed('" + event.getSession().getId() + "'),目前有" + this.users + "个用户");//获取ID 和用户个数
68   this.context.setAttribute("users", new Integer(this.users));////将用户数存入context
69 }
70 
71 public void contextDestroyed(ServletContextEvent sce)
72 {
73   logout("contextDestroyed()-->ServletContext被销毁");
74   this.context = null;
75 }
76 
77 public void contextInitialized(ServletContextEvent sce)
78 {
79   this.context = sce.getServletContext();
80   logout("contextInitialized()-->ServletContext初始化了");
81 }
82 
83 private void logout(String message)
84 {
85   PrintWriter out = null;
86   try
87   { //创建输入流 写入文件
88     out = new PrintWriter(new FileOutputStream("E:\\sessionLog.txt", true));
89     out.println(new Date().toLocaleString() + "-->" + message);
90     out.close();
91   }
92   catch (Exception e)
93   {
94     out.close();
95     e.printStackTrace();
96   }
97 }
98 }

View Code

3.前端JSP文件

  context_test.jsp

1 <%@ page contentType="text/html;charset=UTF-8"%>
2 <%
3 out.println("add attribute");
4 getServletContext().setAttribute("userName","Smith");
5 out.println("replace attribute");
6 getServletContext().setAttribute("userName","Kate");
7 out.println("remove attribute");
8 getServletContext().removeAttribute("userName");
9 %>

  session_test.jsp

 1 <%@ page contentType="text/html;charset=UTF-8"%>
 2 执行了以下的操作:
 3 session.setAttribute("userName","Smith")<br>
 4 <% session.setAttribute("userName","Smith");%><!--添加属性-->
 5 session.setAttribute("userName","Kate")<br>
 6 <% session.setAttribute("userName","Kate");%><!--更改属性-->
 7 session.removeAttribute("userName","Kate")<br>
 8 <% session.removeAttribute("userName");%><!--删除属性-->
 9 目前有<%=getServletContext().getAttribute("users")%>个用户。<br>
10 after session.invalidate()<br>
11 <% session.invalidate();%><!--销毁该session-->
12 目前有<%=getServletContext().getAttribute("users")%>个用户。

 

1.监听上下文的类 1 package com.examp.ch9; 2 3 import
java.io.FileOutputStream; 4 import j…

1.什么是Servlet监听器?

先来看看什么是监听器。监听器是专门用于对其它对象身上发生的事件或状态改变进行监听和相应处理的对象,当被监视的对象发生情况时立即采取相应的行动。Servlet监听器是Servlet规范中定义的一种特殊类,它用于监听web应用程序的ServletContext,HttpSession和ServletRequest等域对象的创建与销毁事件,以及监听这些域对象中的属性发生修改的事件。

web.xml的作用

按监听的事件类型Servlet监听器可分为如下三种类型:

web.xml,一个Tomcat工程中最重要的配置文件。web.xml没有其
实也可以—-只要你确定你的项目里面不需要任何过滤器、监听器、Servlet等等。我试了一下,没有web.xml对那些已经编译成Servlet
的jsp页面来说,是不影响正常显示的,但是那些没有编译成Servlet的jsp页面,访问的时候就会报500的错误了。下面逐一看一下web.xml
里常用标签的作用。

  • 监听域对象自身的创建和销毁的事件监听器
  • 监听域对象中的属性的增加和删除的事件监听器
  • 监听绑定到HtppSession域中的某个对象的状态的事件监听器

 

2.域对象创建和销毁的事件监听器

welcome-file-list

域对象创建和销毁的事件监听器就是用来监听ServletContext,HttpSession,HttpServletRequest这三个对象的创建和销毁事件的监听器。域对象的创建和销毁时机:

这个标签是用来配置首页用的:

  • ServletContext:当web服务器启动时为每个web应用程序创建相应的ServletContext对象,web服务器关闭时为每个web应用程序销毁相应的ServletContext对象。
  • HttpSession:浏览器开始与服务器会话时创建,调用HttpSession.invalidate();超过了session的最大有效时间间隔;服务器进程被停止。
  • ServletRequest:每次请求开始时创建,每次访问结束后销毁。
<welcome-file-list>
    <welcome-file>index1.jsp</welcome-file>
    <welcome-file>index2.jsp</welcome-file>
    <welcome-file>index3.jsp</welcome-file>
    <welcome-file>index4.jsp</welcome-file>
    <welcome-file>/target/redirectAndFoward.jsp</welcome-file>
</welcome-file-list>

3.如何编写Servlet监听器?

这么配置的意思,就是当用户访问的时候,会根据welcome-file-list配置的页面列表,从项目的根目录开始找页面:

  • Servlet规范为每种事件监听器都定义了相应的接口,编写事件监听程序只需要实现这些接口。
  • 在web.xml中进行注册,web服务器按照它们在web.xml中的注册顺序来加载和执行这些Servlet事件监听器。

1、第一个配置的index1.jsp能找到,就展示index1.jsp

4.ServletContextListener接口

2、找不到index1.jsp,则去找第二个index2.jsp,index2.jsp能找到就展示index2.jsp,

ServletContextListener接口用于监听ServletContext对象的创建和销毁事件。

3、找不到index3.jsp,则去找第三个index3.jsp,以此类推,如果所有的页面都找不到则报HTTP
Status 404即页面找不到

编写监听器,代码如下:

注意一下,像配置的最后一个welcome-file这种写法也是支持的,我试了一下最前面的那个”/”可加可不加

package com.javaweb.Listener;import javax.servlet.ServletContextEvent;import javax.servlet.ServletContextListener;public class servletContextListenerTest implements ServletContextListener {@Overridepublic void contextInitialized(ServletContextEvent sce) {System.out.println("ServletContext对象被创建");}@Overridepublic void contextDestroyed(ServletContextEvent sce) {System.out.println("ServletContext对象被销毁");}}

 

在web.xml中进行配置:

error-page

<listener>  <description>ServletContextListener</description>  <listener-class>com.javaweb.Listener.servletContextListenerTest</listener-class></listener>

error-page表示当HTTP返回指定状态码的时候,容器将此次请求转发到配置的指定页面: 

ServletContextListener是最常用的Listener,可以在当前web应用被加载时对当前web应用的相关资源进行初始化操作;创建数据库连接池,创建Spring的IOC容器,读取当前web应用的初始化参数。

 

监听域对象创建和销毁的Listener还有HttpSessionListener以及ServletRequestListener。用法类似ServletContextListener。

<error-page>
    <error-code>400</error-code>
    <location>/filter/error.jsp</location>
</error-page>

<error-page>
    <error-code>404</error-code>
    <location>/filter/error.jsp</location>
</error-page>

<error-page>
    <error-code>500</error-code>
    <location>/filter/error.jsp</location>
</error-page>

5.通过Listener理解域对象生命周期

 

5.1
request:是一个请求,当一个响应返回时,即被销毁,当发送一个请求时被创建。

这表示HTTP状态码为400、404、500的时候,此次请求都会被转发到这个页面上去。注意一下这里是error-code,所以如果是200的话,是没有效果的

index.jsp

 

<body>  <a href="Listener/test.jsp">to test page</a>  <%    request.setAttribute("resquestKey","requestValue");   %></body>

filter

test.jsp

filter就不说了,两种include方式及filter中的dispatcher解析一文已经讲得很详细了,filter的写法也在上面。

<body>  <%= request.getAttribute("resquestKey") %></body>

另外注意一点,其实大家也都知道,提一下:走filter的顺序就是filter定义的顺序

运行index.jsp,发送一个请求,返回一个响应页面,

 

图片 2

servlet

request请求被销毁。此时在index.jsp页面设置的属性request.setAttribute(“resquestKey”,”requestValue”);在test.jsp页面中获取不到对应的属性值,入下:

servlet开发者比较熟悉,先匹配规则,匹配到路径后走相应的Servlet类,就不说了。下面配一个相对不那么常用的,只是相对而已,这种servlet的写法很常见:

图片 3

 

可以通过请求转发(请求转发只有一个request)实现上述功能:

<servlet>
    <servlet-name>startUpServlet</servlet-name>
    <servlet-class>com.xrq.servlet.StartUpServlet</servlet-class>
    <init-param>
        <param-name>Name</param-name>
        <param-value>123</param-value>
    </init-param>
    <init-param>
        <param-name>Age</param-name>
        <param-value>456</param-value>
    </init-param>
    <load-on-startup>8</load-on-startup>
</servlet>
<%  request.setAttribute("resquestKey","requestValue");%><jsp:forward page="/Listener/test.jsp"></jsp:forward>

 

图片 4

这是一个启动servlet,表示容器启动的时候servlet启动,调用其init()方法,所以首先第一个标签load-on-start,分几点说:

在index.jsp页面点击超链接到TestServlet,在TestServlet中将请求转发到test.jsp页面。中间涉及到的是一个request请求。

1、load-on-startup元素标记容器是否在启动的时候就加载这个servlet(实例化并调用其init方法)

index.jsp

2、它的值必须是一个整数,表示servlet应该被载入的顺序

<body>  <a href="TestServlet">testServlet</a></body>

3、当值为0或者大于0时,表示容器在应用启动时就加载并初始化这个servlet

testServlet.java

4、当值小于0或者没有指定时,表示这个容器在该servlet被选择时才会去加载

package com.javaweb.Listener;import java.io.IOException;import java.io.PrintWriter;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class testServlet extends HttpServlet {public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {        request.setAttribute("requestKey", "requestValue2");        request.getRequestDispatcher("/Listener/test.jsp").forward(request, response);}}

5、正数值越小,该servlet的优先级就越高,应用启动时就越先加载

test.jsp

6、当值相同时,容器自己选择顺序来加载

<body>  <%= request.getAttribute("requestKey") %></body>

所以,load-on-startup中配置了一个大于等于0的正整数时,该servlet可以当作一个普通的servlet来用,无非是这个servlet启动的时候会加载其init()方法罢了。

web.xml中进行配置:

另外一个就是init-param了,表示一个键值对,只能在本servlet里面被使用,通过ServletConfig获取,StartUpServlet的写法是:

<servlet>  <servlet-name>testServlet</servlet-name>  <servlet-class>com.javaweb.Listener.testServlet</servlet-class></servlet><servlet-mapping>  <servlet-name>testServlet</servlet-name>  <url-pattern>/TestServlet</url-pattern></servlet-mapping>

 

运行后可以获得设置的request属性值。

public class StartUpServlet extends HttpServlet
{
    /**
     * 序列化
     */
    private static final long serialVersionUID = 1L;

    public void init() throws ServletException
    {
        System.out.println("StartUpServlet.init()");
        System.out.println("Name:" + getServletConfig().getInitParameter("Name"));
        System.out.println("Age:" + getServletConfig().getInitParameter("Age"));
    }

    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException
    {

    }

    public void destroy()
    {
        System.out.println("StartUpServlet.destory()");
    }
}

图片 5

 

若将testServlet.java中的请求转发request.getRequestDispatcher(“/Listener/test.jsp”).forward(request,
response);改为请求重定向response.sendRedirect(“/Listener/test.jsp”);则涉及到两个request请求。

servlet只能取到配置在自己的<servlet>标签内的<init-param>

5.2
session:当第一次访问web应用的一个JSP或Servlet时,且该JSP或servelet中还需要创建session对象,此时服务器会创建一个session对象。

 

session销毁:session过期;直接调用session的invalidate方法;当前web应用被卸载。

listener

session过期:

listener即监听器,是Servlet的监听器,它可以监听客户端的请求、服务器端的操作等,在事情发生前后做一些必要的处理。通过监听器,可以自动激发一些操作,比如监听在线用户数量,下面就写一个监听用户数量的监听器,首先web.xml配置很简单:

<%HttpSession session=request.getSession; session.setMaxInactiveInterval;%>
<listener>
    <listener-class>com.xrq.listener.UserCounterListener</listener-class>
</listener>

session的invalidate方法:

写一个监听器,监听用户数量一般都是以session创建和session失效为依据的,所以实现HttpSessionListener:

<%HttpSession session=request.getSession; session.invalidate();%>

 

关闭浏览器,并不意味着session被销毁,还可以通过sessionid找到服务器中的session对象。

public class UserCounterListener implements HttpSessionListener
{
    private AtomicInteger ai = new AtomicInteger(0);

    public void sessionCreated(HttpSessionEvent se)
    {
        ai.incrementAndGet();
    }

    public void sessionDestroyed(HttpSessionEvent se)
    {
        ai.decrementAndGet();
    }

    public int getUserCount()
    {
        return ai.get();
    }
}

5.3
application:贯穿于当前的web应用的生命周期,当前web应用被加载时创建application对象,当前web应用被卸载时销毁application对象。

 

除了监听session的监听器外,再介绍一些别的监听器接口:

1、ServletContextListener

用于监听WEB引用启动和销毁的事
件,SevletContextListener是ServletContext的监听者,如果ServletContext发生变化,如服务器启动、服
务器关闭,都会被ServletContextListener监听到。监听事件为ServletContextEvent

2、ServletContextAttributeListener

用于监听WEB应用属性改变的事件,包括添加属性、删除属性、修改属性。监听时间为ServletContextAttributeEvent

3、HttpSessionBindingListener

HttpSessionBindingListener是唯一一个不需要在
web.xml中配置的Listener,当我们的类实现了HttpSessionBindListener接口后,只要对象加入session或者从
session中移除,容器会分别自动调用以下两个方法:

(1)void
valueBound(HttpSesssionBindEvent event)

(2)void
valueUnbound(HttpSessionBindingEvent event)

注意,这个监听器的触发是针对于实现了该监听器的类的,只有把实现了该监听器的类set进session或从session中remove才会触发这个监听器

4、HttpSessionAttributeListener

用于监听HttpSession中的属性的操作,当session里面增加一个属
性时,触发attributeAdded(HttpSessionBindEvent
se)方法;当在session中删除一个属性时,触发attributeRemoved(HttpSessionBindEvent
se)方法;当session属性被重新设置时,触发attributeReplaced(HttpSessionBindingEvent
se)方法。

注意,这个监听器的触发是针对所有的session的,只要session的属性发生变化,都会触发这个监听器

5、HttpSessionListener

这个上面已经写过了,监听HttpSession。当创建一个session时,
触发sessionCreated(HttpSessionEvent
se)方法;当销毁一个session时,会触发sessionDestoryed(HttpSessionEvent
se)方法

6、HttpSessionActivationListener

这个用得不太多,主要监听同一个session转移至不同的JVM的情形

7、ServletRequestListener和ServletRequestAttributeListener

和ServletContextListener和ServletContextAttributeListener类似,前者监听Request的创建和销毁、后者监听Request中属性的增删改

 

context-param

context-param里面配置的键值对是全局共享的,整个web项目都能取到这个上下文,比方说我在web.xml里面配置了一个HTTP端口和一个HTTPS端口:

 

<context-param>
    <param-name>NotSSLPort</param-name>
    <param-value>8080</param-value>
</context-param>
<context-param>
    <param-name>SSLPort</param-name>
    <param-value>8443</param-value>
</context-param>

 

servlet可以这么取:

protected void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
{
    System.out.println("NotSSLPort:" + getServletContext().getInitParameter("NotSSLPort"));
    System.out.println("SSLPort:" + getServletContext().getInitParameter("SSLPort"));
}

filter可以这么取:

 

public void doFilter(ServletRequest request, ServletResponse response,
        FilterChain chain) throws IOException, ServletException
{
    HttpServletRequest req = (HttpServletRequest)request;
    ServletContext sc = req.getSession().getServletContext();
    System.out.println("NotSSLPort:" + sc.getInitParameter("NotSSLPort"));
    System.out.println("SSLPort:" + sc.getInitParameter("SSLPort"));
    chain.doFilter(request, response);
}

 

listener可以这么取,以ServletContextListener为例:

public void contextInitialized(ServletContextEvent sce)
{
    System.out.println("Enter SCListener.contextInitialized");
    System.out.println("NotSSLPort:" + sce.getServletContext().getInitParameter("NotSSLPort"));
    System.out.println("SSLPort:" + sce.getServletContext().getInitParameter("SSLPort"));
}

反正最终的目的就是取到一个ServletContext就对了。是不是感觉ServletContext很熟悉呢?没错,看一下jsp默认的内置对象,随便打开一个转换成Servlet的jsp页面,里面都有内置对象的定义:

 

PageContext pageContext = null;
HttpSession session = null;
ServletContext application = null;
ServletConfig config = null;
JspWriter out = null;
Object page = this;
JspWriter _jspx_out = null;
PageContext _jspx_page_context = null;

 

ServletContext也就是我们常说的Application

 

<mime-mapping>

<mime-mapping>可能不太常见,这个标签是用来指定对应的格式的浏览器处理方式的,添加mime-type的映射,就可以避免某些类型的文件直接在浏览器中打开了。

举个例子:

 

<mime-mapping>
    <extension>doc</extension>
    <mime-type>application/msword</mime-type>
</mime-mapping>
<mime-mapping>
    <extension>pdf</extension>
    <mime-type>application/pdf</mime-type>
</mime-mapping>
<mime-mapping>
    <extension>rar</extension>
    <mime-type>application/x-rar-compressed</mime-type>
</mime-mapping>
<mime-mapping>
    <extension>txt</extension>
    <mime-type>text/plain</mime-type>
</mime-mapping>
<mime-mapping>
    <extension>xls</extension>
    <mime-type>application/vnd.ms-excel</mime-type>
</mime-mapping>

 

这就指定了.doc、pdf、rar、txt、xls这五种类型的文件的打开方式了。常见的MIME类型有:

类  型 后  缀 MIME类型
超文本标记语言文本 .htm、.html text/html
普通文本 .txt text/plain
RFT文本 .rtf application/rtf
GIF图形 .gif image/gif
JPEG图形 .jpg、.jpeg image/jpeg
au声音文件 .au audio/basic
MIDI音乐文件 .mid、.midi audio/midi、audio/x-midi
RealAudio音乐文件 .ra、.ram audio/x-pn-realaudio
MPEG文件 .mpg、.mpeg video/mpeg
AVI文件 .avi video/x-msvideo
GZIP文件 .gz application/x-gzip
TAR文件 .tar application/x-tar

 

session-config

session-config是用来配置session失效时间的,因为session-config里面只有一个子标签:

<session-config>
    <session-timeout>30</session-timeout>
</session-config>

以分钟为单位。当然,代码里面也可以设置:

“request.getSession.setMaxInactiveInterval(30 * 60);”就可以了,单位为秒

 

元素加载顺序

首先可以肯定,加载顺序与它们在web.xml文件中的先后顺序无关,即不会因为filter写在listener前面就先加载filter。最终得出的结论是listener->filter->servlet。

然后是context-param,用于向ServletContext提供键值对,即应用程序上下文信息,listener、filter、servlet都可能会用到这些上下文中的信息,那么context-param是否应该写在listener、filter、servlet前面呢?未必,context-param可以写在任何位置,因此真正的加载顺序应该是:context-param->listener->filter->servlet