学习笔记,JavaWeb高级编程
代码地址
原生JavaWeb项目,无框架,Servlet 3.0
@WebListener
public class Configurator implements ServletContextListener
{
@Override
public void contextInitialized(ServletContextEvent event)
{
ServletContext context = event.getServletContext();
//注册日志过滤器
FilterRegistration.Dynamic registration =
context.addFilter("requestLogFilter", new RequestLogFilter());
//匹配所有请求
registration.addMappingForUrlPatterns(null, false, "/*");
//注册响应压缩过滤器
registration = context.addFilter("compressionFilter",
new CompressionFilter());
//异步支持
registration.setAsyncSupported(true);
//匹配所有请求
registration.addMappingForUrlPatterns(null, false, "/*");
}
@Override
public void contextDestroyed(ServletContextEvent event) { }
}
按照上面的配置,日志过滤器是在过滤器链的第一个,它将记录所有访问应用程序的请求信息,IP地址,时间戳,请求方式,协议,响应状态,长度以及处理的时间,类似于Apache HTTP日志格式。日志操作都被放在finally块中,这样就算抛出异常也不会影响日志语句的执行。
public class RequestLogFilter implements Filter
{
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException
{
Instant time = Instant.now();
StopWatch timer = new StopWatch();
try
{
timer.start();
chain.doFilter(request, response);
}
finally
{
timer.stop();
HttpServletRequest in = (HttpServletRequest)request;
HttpServletResponse out = (HttpServletResponse)response;
String length = out.getHeader("Content-Length");
if(length == null || length.length() == 0)
length = "-";
System.out.println(in.getRemoteAddr() + " - - [" + time + "]" +
" \"" + in.getMethod() + " " + in.getRequestURI() + " " +
in.getProtocol() + "\" " + out.getStatus() + " " + length +
" " + timer);
}
}
@Override
public void init(FilterConfig filterConfig) throws ServletException { }
@Override
public void destroy() { }
}
响应数据可以在Servlet完成请求处理之前返回到客户端,在异步请求处理的情况下,它还可以在Servlet完成请求处理之后返回到客户端。
因此,如果要修改响应内容,必须在传递响应对象到过滤器链之前对它进行封装。
首先先要检查客户端是否在Accept-Encoding请求头中包含了gzip编码,如果没有,那么客户端可能无法处理压缩响应。
如果有,那么接下来要将Content-Encoding头设置为gzip
然后使用私有内部类ResponseWrapper的实例封装响应对象
然后该类使用私有内部类GZIPServletOutputStream封装PrintWriter或者ServletOutputStream
public class CompressionFilter implements Filter
{
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException
{
if(((HttpServletRequest)request).getHeader("Accept-Encoding")
.contains("gzip"))
{
System.out.println("Encoding requested.");
((HttpServletResponse)response).setHeader("Content-Encoding", "gzip");
ResponseWrapper wrapper =
new ResponseWrapper((HttpServletResponse)response);
try
{
chain.doFilter(request, wrapper);
}
finally
{
try {
wrapper.finish();
} catch(Exception e) {
e.printStackTrace();
}
}
}
else
{
System.out.println("Encoding not requested.");
chain.doFilter(request, response);
}
}
@Override
public void init(FilterConfig filterConfig) throws ServletException { }
@Override
public void destroy() { }
private static class ResponseWrapper extends HttpServletResponseWrapper
{
private GZIPServletOutputStream outputStream;
private PrintWriter writer;
public ResponseWrapper(HttpServletResponse request)
{
super(request);
}
@Override
public synchronized ServletOutputStream getOutputStream()
throws IOException
{
if(this.writer != null)
throw new IllegalStateException("getWriter() already called.");
if(this.outputStream == null)
this.outputStream =
new GZIPServletOutputStream(super.getOutputStream());
return this.outputStream;
}
@Override
public synchronized PrintWriter getWriter() throws IOException
{
if(this.writer == null && this.outputStream != null)
throw new IllegalStateException(
"getOutputStream() already called.");
if(this.writer == null)
{
this.outputStream =
new GZIPServletOutputStream(super.getOutputStream());
this.writer = new PrintWriter(new OutputStreamWriter(
this.outputStream, this.getCharacterEncoding()
));
}
return this.writer;
}
@Override
public void flushBuffer() throws IOException
{
if(this.writer != null)
this.writer.flush();
else if(this.outputStream != null)
this.outputStream.flush();
super.flushBuffer();
}
@Override
public void setContentLength(int length) { }
@Override
public void setContentLengthLong(long length) { }
@Override
public void setHeader(String name, String value)
{
if(!"content-length".equalsIgnoreCase(name))
super.setHeader(name, value);
}
@Override
public void addHeader(String name, String value)
{
if(!"content-length".equalsIgnoreCase(name))
super.setHeader(name, value);
}
@Override
public void setIntHeader(String name, int value)
{
if(!"content-length".equalsIgnoreCase(name))
super.setIntHeader(name, value);
}
@Override
public void addIntHeader(String name, int value)
{
if(!"content-length".equalsIgnoreCase(name))
super.setIntHeader(name, value);
}
public void finish() throws IOException
{
if(this.writer != null)
this.writer.close();
else if(this.outputStream != null)
this.outputStream.finish();
}
}
private static class GZIPServletOutputStream extends ServletOutputStream
{
private final ServletOutputStream servletOutputStream;
private final GZIPOutputStream gzipStream;
public GZIPServletOutputStream(ServletOutputStream servletOutputStream)
throws IOException
{
this.servletOutputStream = servletOutputStream;
this.gzipStream = new GZIPOutputStream(servletOutputStream);
}
@Override
public boolean isReady()
{
return this.servletOutputStream.isReady();
}
@Override
public void setWriteListener(WriteListener writeListener)
{
this.servletOutputStream.setWriteListener(writeListener);
}
@Override
public void write(int b) throws IOException
{
this.gzipStream.write(b);
}
@Override
public void close() throws IOException
{
this.gzipStream.close();
}
@Override
public void flush() throws IOException
{
this.gzipStream.flush();
}
public void finish() throws IOException
{
this.gzipStream.finish();
}
}
}