8.4 ServletRequestListeners 8.4.1 ServletRequestListener

8.4 ServletRequestListeners

ServletRequest范围的监听器接口有三个:ServletRequestListenerServletRequestAttributeListenerAsyncListener。前两个接口会在本节进行介绍,而AsyncListener接口则会在第11章中进行介绍。

8.4.1 ServletRequestListener

ServletRequestListener监听器会对ServletRequest的创建和销毁事件进行响应。容器会通过一个池来存放并重复利用多个ServletRequestServletRequest的创建时刻是从容器池里被分配出来的时候,而它的销毁时刻是放回容器池里的时间。
ServletRequestListener接口有两个方法,requestInitializedrequestDestroyed

1
2
void requestInitialized(ServletRequestEvent event)
void requestDestroyed(ServletRequestEvent event)

当一个ServletRequest创建(从容器池里取出)时,requestInitialized方法会被调用,当ServletRequest销毁(被容器回收)时,requestDestroyed方法会被调用。这两个方法都会接收到一个ServletRequestEvent对象,可以通过使用这个对象的getServletRequest方法来获取ServletRequest对象:

1
ServletRequest getServletRequest()

另外,ServletRequestEvent接口也提供了一个getServletContext方法来获取ServletContext,如下所示:

1
ServletContext getServletContext()

实例

app08a项目里的PerfStatListener类为例。这个监听器用来计算每个ServletRequest从创建到销毁的生存时间。

PerfStatListener实现了ServletRequestListener接口,来计算每个HTTP请求的完成时间。由于容器在请求创建时会调用ServletRequestListenerrequestInitialized方法,在销毁时会调用requestDestroyed,因此很容易就可以计算出时间。只需要在记录下两个事件的事件,并且相减,就可以计算出一次HTTP请求的完成时间了。

PerfStatListener

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package app08a.listener;
import javax.servlet.ServletRequest;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpServletRequest;
@WebListener
public class PerfStatListener
implements
ServletRequestListener
{
@Override
public void requestInitialized(ServletRequestEvent sre)
{
ServletRequest servletRequest =
sre.getServletRequest();
servletRequest.setAttribute("start",
System.nanoTime());
}
@Override
public void requestDestroyed(ServletRequestEvent sre)
{
ServletRequest servletRequest =
sre.getServletRequest();
Long start =
(Long) servletRequest.getAttribute("start");
Long end = System.nanoTime();
HttpServletRequest httpServletRequest =
(HttpServletRequest) servletRequest;
String uri = httpServletRequest.getRequestURI();
System.out.println("time taken to execute "
+ uri + ":"+ ((end - start) / 1000)
+ " microseconds");
}
}

方法说明

requestInitialized方法

requestInitialized 方法调用System.nanoTime()获取当前系统时间的数值(Long类型),并将这个数值保存到ServletRequest中:

1
2
3
4
5
6
7
8
@Override
public void requestInitialized(ServletRequestEvent sre)
{
ServletRequest servletRequest =
sre.getServletRequest();
servletRequest.setAttribute("start",
System.nanoTime());
}

nanoTime返回一个long类型的数值来表示任意时间。这个数值和系统或是时钟时间都没什么关系,但是同一个JVM上调用两次nanoTime得到的数值可以计算出两次调用之间的时间。

requestDestroyed方法

requestDestroyed方法中再次调用nanoTime方法,并且减去第一次调用获得的数值,就得到HTTP请求的完成时间了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Override
public void requestDestroyed(ServletRequestEvent sre)
{
ServletRequest servletRequest =
sre.getServletRequest();
Long start =
(Long) servletRequest.getAttribute("start");
Long end = System.nanoTime();
HttpServletRequest httpServletRequest =
(HttpServletRequest) servletRequest;
String uri = httpServletRequest.getRequestURI();
System.out.println("time taken to execute "
+ uri + ":"+ ((end - start) / 1000)
+ " microseconds");
}

运行效果

调用app08a应用中的countries.jsp页面,在控制台中可以看到PerfStatListener的运行效果,下面是几次调用时控制台的输出:

1
2
3
4
time taken to execute /app08a/countries.jsp:31243 microseconds
time taken to execute /app08a/countries.jsp:6671 microseconds
time taken to execute /app08a/countries.jsp:4701 microseconds
time taken to execute /app08a/countries.jsp:4242 microseconds