9.3示例1:日志Filter 实例简介 作为第1个例子,将做一个简单的Filter
:
在app09a
的应用中把Request
请求的URL
记录到日志文本文件中 。
日志文本文件名通过Filter
的初始化参数来配置。
此外,日志的每条记录都会有一个前缀,该前缀也由Filter
初始化参数来定义。
通过日志文件,可以获得许多有用的信息,例如在应用中哪些资源访问最频繁;Web
站点在一天中的哪个时间段访问量最多。
这个Filter
的类名叫LoggingFilter
,一般情况下,Filter
的类名都以*Filter
结尾。
LoggingFilter类 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 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 package filter;import java.io.File;import java.io.FileNotFoundException;import java.io.IOException;import java.io.PrintWriter;import java.text.DateFormat;import java.util.Date;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.annotation.WebFilter;import javax.servlet.annotation.WebInitParam;import javax.servlet.http.HttpServletRequest;@WebFilter( filterName = "LoggingFilter", urlPatterns ={"/*"}, initParams = { @WebInitParam( name = "logFileName", value = "log.txt" ), @WebInitParam( name = "prefix", value = "URI: " ) } ) public class LoggingFilter implements Filter { private PrintWriter logger; private String prefix; static DateFormat format= DateFormat.getDateTimeInstance( DateFormat.LONG,DateFormat.LONG); @Override public void init (FilterConfig filterConfig) throws ServletException { prefix = filterConfig.getInitParameter("prefix" ); String logFileName = filterConfig.getInitParameter("logFileName" ); String appPath = filterConfig.getServletContext().getRealPath("/" ); System.out.println( "LoggingFilter的init()方法被执行" + "--->logFileName:" + logFileName); try { File file=new File (appPath, logFileName); logger = new PrintWriter (new File (appPath, logFileName)); System.out.println("日志文件绝对路径:" + file.getAbsolutePath()); } catch (FileNotFoundException e) { e.printStackTrace(); throw new ServletException (e.getMessage()); } } @Override public void destroy () { System.out.println( "LoggingFilter的destroy()方法被执行" ); if (logger != null ) { logger.close(); } } @Override public void doFilter (ServletRequest request, ServletResponse response,FilterChain filterChain) throws IOException,ServletException { System.out.println( "LoggingFilter的doFilter()方法被执行" ); HttpServletRequest httpServletRequest = (HttpServletRequest) request; logger.println( format.format(new Date ()) + " " +prefix + httpServletRequest.getRequestURI()); logger.flush(); filterChain.doFilter(request, response); } }
下面来仔细分析一下该Filter
类。
成员变量 首先,该Filter
的类实现了Filter
的接口并声明两个成员变量:PrintWriter
类型的logger
和String
类型的prefix
。
1 2 private PrintWriter logger;private String prefix:
其中PrintWriter
用于记录日志到文本文件,prefix
的字符串用于每条日志的前缀。
WebFilter注解 Filter
的类使用了@WebFilter
的Annotation
,将两个参数(logFilteName
、prefix
)传入到该Filter
中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @WebFilter( filterName = "LoggingFilter", urlPatterns ={"/ *"}, initParams = { @WebInitParam( name = "logFileName", value = "log.txt" ), @WebInitParam( name = "prefix", value = "URI: " ) } )
init方法 获取初始化参数 在Filter
的init
方法中,通过形式参数FilterConfig
对象的getInitParameter
方法来获取prefix
和getFileName
的初始化参数。其中把prefix
参数中赋给了类变量prefix
,logFileName
则用于创建一个PrintWriter
:
1 2 3 prefix = filterConfig.getInitParameter("prefix" ); String logFileName = filterConfig .getInitParameter("logFileName" );
工作目录 如果Servlet/JSP
应用是通过Servlet/JSP
容器启动的,那么当前应用的工作目录是当前JDK
所在的目录。 如果是在Tomcat
中,该目录是Tomcat
的安装目录。 在应用中创建日志文件,可以通过ServletContext.getRealPath
来获取工作目录,结合应用工作目录以及初始化参数中的logFilterNmae
,就可以得到日志文件的绝对路径:
1 2 3 4 5 6 7 8 9 10 11 String appPath = filterConfig.getServletContext().getRealPath("/" ); try { logger = new PrintWriter (new File (appPath, logFileName)); } catch (FileNotFoundException e) { e.printStackTrace(); throw new ServletException (e.getMessage()); }
当Filter
的init
方法被执行时,日志文件就会创建出来。如果在应用的工作目录中该文件已经存在,那么该日志文件的内容将会被覆盖。
destroy方法 当应用关闭时,PrintWriter
需要被关闭。因此在Filter
的destroy
方法中,需要:
1 2 3 if (logger != null ) { logger.close(); }
doFilter方法 Filter
的doFilter
实现中记录着所有从ServletRequest
到HttpServletRequest
的Request
,并调用了它的getRequestURI
方法,该方法的返回值将记录通过PrintWriter
的println
方法记录下来:
1 2 3 4 HttpServletRequest httpServletRequest = (HttpServletRequest) request; logger.println(new Date () + " " + prefix + httpServletRequest.getRequestURI());
每条记录都有一个时间戳以及前缀,这样可以很方便地标识每条记录。接下来 Filter
的doFilter
实现调用PrintWriter
的flush
方法以及FilterChain.doFilter
,以唤起资源的调用:
1 2 logger.flush(); filterChain.doFilter(request, response);
如果使用Tomcat
,Filter
的初始化并不会等到第一个Request
请求时才触发进行。这点可以在控制台中打印出来的logFileName
参数值中可以看到。
test.jsp 1 2 3 4 5 6 7 8 9 10 11 12 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> <!DOCTYPE html> <html> <head> <meta charset="UTF-8" > <title>测试</title> </head> <body> <h2>测试</h2> </body> </html>
运行效果 启动项目 启动app09a
项目,可以看到控制台部分输出如下图所示: 从控制台的输出我们可以知道:
日志文件的绝对路径为:E:\apache-tomcat-8.5.35\webapps\app09a\log.txt
.
在项目启动阶段Filter
初始化就开始了。
访问JSP页面 然后通过URL
调用test.jsp
页面http://localhost:8080/app09a/test.jsp 此时,容器会调用doFilter
方法,该方法控制台输出如下:
查看日志文件 这时候可以打开日志文件:E:\apache-tomcat-8.5.35\webapps\app09a\log.txt
查看日志输出,如下图所示:
doFilter的执行次数和JSP页面的访问次数的关系 在该过滤器中,我们设置的过滤规则为:
这表示过滤所有的JSP
页面,所以每访问一次JSP页面,过滤器就会执行一次. 再刷新两次test.jsp
页面,则doFilter
方法也会再执行两次,日志文件中也会多出两条记录,如下图所示: