第6章 Filter过滤器
6.1 Filter接口
Filter定义了三个方法init(),doFilter(),destory()
,分别用于初始化,运行过滤器和销毁过滤器。
1、 Init()
public void init(FilterConfig filterConfig)
在Web应用程序启动时,Web服务器根据web.xml文件创建注册的Filter实例对象。创建实例对象后会立刻调用init方法,且只调用一次。
FilterConfig接口:Filter程序可能访问Servlet容器,Servlet将ServletContext和Filter配置参数信息封装到FilterConfig中。
FilterConfig常用方法:
getFilterName()/getServletContext()/getInitParameter()/getInitParameterNames()
2、 doFilter()
public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain)
当一个Filter对象拦截访问请求后,Servlet容器会调用该Filter对象的doFilter()。
FilterChain接口只有一个方法doFilter()用于通知Web容器吧请求交给FilterChain的下一个Filter或Servlet处理。
3、 destroy()
public void destroy()
6.2 Filter基础实例
public class FirstFilter implements Filter {
private FilterConfig config = null;
private String paramvalue = "";
public void init(FilterConfig config) throws ServletException {
this.config = config;
paramvalue = config.getInitParameter("encoding");
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
chain.doFilter(request, response);
}
public void destroy() {
this.config = null;
}}
<filter> <filter-name>FirstFilter</filter-name> <filter-class>filter.FirstFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>GB2312</param-value> </init-param> </filter> <filter-mapping> <filter-name>FirstFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
6.3 阻止Servlet容器继续调用Servlet
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpSession session = req.getSession();
if (session.getAttribute("user") == null) {
RequestDispatcher rd = req.getRequestDispatcher("/login.jsp");
rd.forward(req, resp);
} else {chain.doFilter(request, response);}}
6.4 修改Servlet容器传给Servlet的请求信息
下面实例是一个将Servlet的Request对象获取的请求中的HTML特殊字符进行转换。
public class ConvertSpecialCharacterServletRequestWrapper extends
HttpServletRequestWrapper {
public ConvertSpecialCharacterServletRequestWrapper(
HttpServletRequest request) {
super(request);
}
public String getParameter(String name) {
String value = super.getParameter(name);
if (null != value) {
return toHtml(value.trim());
} else {
return null;
}
}
public String toHtml(String str) {
if (str == null) {
return null;
}
StringBuffer sb = new StringBuffer();
int len = str.length();
for (int i = 0; i < len; i++) {
char c = str.charAt(i);
switch (c) {
case ' ':
sb.append(" ");
break;
case '<':
sb.append("<");
break;
case '>':
sb.append(">");
break;
case '&':
sb.append("&");
break;
case '"':
sb.append(""");
break;
case '\\':
sb.append("\");
break;
default:
sb.append(c);
}
}
return sb.toString();
}
}
public class SpecialCharacterFilter implements Filter {
public void init(FilterConfig config) throws ServletException {
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
HttpServletRequestWrapper requestWrapper =
(HttpServletRequestWrapper) request;
chain.doFilter(requestWrapper, response);
}
public void destroy() {}
}
6.5 修改Servlet回送给Servlet容器的响应结果
下例中将JSP内容通过ServletResponse打印出来
public class JSPOutputServletResponseWrapper extends HttpServletResponseWrapper {
private MyPrintWriter tmpWriter;
private ByteArrayOutputStream output;
public JSPOutputServletResponseWrapper(HttpServletResponse response) {
super(response);
output = new ByteArrayOutputStream();
tmpWriter = new MyPrintWriter(output);
}
public void finalize() throws Throwable {
super.finalize();
output.close();
tmpWriter.close();
}
public String getContent() {
try {
tmpWriter.flush(); // 刷新该流的缓冲
String s = tmpWriter.getByteArrayOutputStream().toString("UTF-8");
// 此处可根据需要进行对输出流以及Writer的重置操作
// 比如tmpWriter.getByteArrayOutputStream().reset()
return s;
} catch (UnsupportedEncodingException e) {
return "UnsupportedEncoding";
}
}
// 覆盖getWriter()方法,使用我们自己定义的Writer
public PrintWriter getWriter() throws IOException {
return tmpWriter;
}
public void close() throws IOException {
tmpWriter.close();
}
// 自定义PrintWriter,为的是把response流写到自己指定的输入流当中
// 而非默认的ServletOutputStream
private static class MyPrintWriter extends PrintWriter {
ByteArrayOutputStream myOutput; // 此即为存放response输入流的对象
public MyPrintWriter(ByteArrayOutputStream output) {
super(output);
myOutput = output;
}
public ByteArrayOutputStream getByteArrayOutputStream() {
return myOutput;
}
}
}
6.6 使用Filter压缩HTML正文
public class CompressionResponseStream extends ServletOutputStream {
protected int compressionThreshold = 0;// 是否启用压缩的临界值
protected byte[] buffer = null;// 临时容纳写入的数据的缓冲区
protected int bufferCount = 0; // 缓冲区实际写入的数据量
protected GZIPOutputStream gzipstream = null;
protected boolean closed = false;// 对当前流对象是否处于关闭状态
protected int length = -1;
protected HttpServletResponse response = null;
protected ServletOutputStream output = null;
public CompressionResponseStream(HttpServletResponse response)
throws IOException {
super();
closed = false;
this.response = response;
this.output = response.getOutputStream();
}
protected void setBuffer(int threshold) {
compressionThreshold = threshold;
buffer = new byte[compressionThreshold];
}
public void close() throws IOException {
if (closed) {
throw new IOException("This output stream has already been closed");
}
/*
* 根据gzipstream是否为null,就可以判断写入的内容是否达到
* 启用压缩的临界值,因为到达临界值时,write方法会自动创建gzipstream对象
*/
if (gzipstream != null) {
flushToGZip();
gzipstream.close();
gzipstream = null;
} else {
if (bufferCount > 0) {
output.write(buffer, 0, bufferCount);
bufferCount = 0;
}
}
output.close();
closed = true;
}
public void flush() throws IOException {
if (closed) {
throw new IOException("Can't flush a closed output stream");
}
if (gzipstream != null) {
gzipstream.flush();
}
}
/**
* 将buffer缓冲区中的数据写入到gzipstream中
*
* @throws IOException
*/
public void flushToGZip() throws IOException {
if (bufferCount > 0) {
writeToGZip(buffer, 0, bufferCount);
bufferCount = 0;
}
}
@Override
public void write(int b) throws IOException {
if (closed) {
throw new IOException("Can't write to a closed output stream");
}
/*
* 当写入的数据达到启用压缩的临界值时, 则先将buffer缓冲区的数据写入到gzipstream对象中
*/
if (bufferCount >= buffer.length) {
flushToGZip();
}
buffer[bufferCount++] = (byte) b;
}
public void write(byte b[]) throws IOException {
write(b, 0, b.length);
}
public void write(byte[] b, int off, int len) throws IOException {
if (closed) {
throw new IOException("Can't write to a closed output stream");
}
if (len == 0) {
return;
}
/*
* 如果buffer缓冲区中剩余的空间能够容纳要写入的数据, 则先将这些数据写入到buffer缓冲区中
*/
if (len <= (buffer.length - bufferCount)) {
System.arraycopy(b, off, buffer, bufferCount, len);
bufferCount += len;
return;
}
/*
* 如果buffer缓冲区剩余的空间不能容纳要写入的数据, 则先将buffer缓冲区的原有数据写入到gzipstream对象中
*/
flushToGZip();
/*
* 腾空buffer缓冲区数据后,判断整个buffer缓冲区是否能够容纳要写入的数据, 如果能将这些数据写入到buffer缓冲区中
*/
if (len <= (buffer.length - bufferCount)) {
System.arraycopy(b, off, buffer, bufferCount, len);
bufferCount += len;
return;
}
/*
* 如果整个buffer缓冲区的空间都不能容纳要写入的数据, 则直接将这些数据写入gzipstream中
*/
writeToGZip(b, off, len);
}
/**
* 将gzipstream对象中写入数据,如果第一次写入 则还需创建gzips实体对象
*
* @param b
* @param off
* @param len
* @throws IOException
*/
public void writeToGZip(byte b[], int off, int len) throws IOException {
if (gzipstream == null) {
response.addHeader("Content-Encoding", "gzip");
gzipstream = new GZIPOutputStream(output);
}
gzipstream.write(b, off, len);
}
}
public class CompressionServletResponseWrapper extends
HttpServletResponseWrapper {
protected HttpServletResponse origResponse = null;
protected static final String info = "CompressionServletResponseWrapper";
protected ServletOutputStream stream = null;
protected PrintWriter writer = null;
protected int threshold = 0;
protected String contentType = null;
public CompressionServletResponseWrapper(HttpServletResponse response) {
super(response);
origResponse = response;
}
public void setContentType(String contentType) {
this.contentType = contentType;
origResponse.setContentType(contentType);
}
public void setCompressionThreshold(int threshold) {
this.threshold = threshold;
}
/**
* 创建自定义response对象包括输出流对象
*
* @return
* @throws IOException
*/
public ServletOutputStream createOutputStream() throws IOException {
CompressionResponseStream stream = new CompressionResponseStream(
origResponse);
stream.setBuffer(threshold);
return stream;
}
/**
* 为了防止目标Servlet没有关闭输出流对象,Filter调用该方法 以便自动关闭输出流对象
*/
public void finishResponse() {
try {
if (writer != null) {
writer.close();
} else {
if (stream != null) {
stream.close();
}
}
} catch (Exception e) {
// TODO: handle exception
}
}
/**
* 覆盖flushBuffer方法
*/
public void flushBuffer() throws IOException {
((CompressionResponseStream) stream).flush();
}
/**
* 覆盖getOutputStream方法
*/
public ServletOutputStream getOutputStream() throws IOException {
if (writer != null) {
throw new IllegalStateException(
"getWriter() has already been called for this resposne");
}
if (stream == null) {
stream = createOutputStream();
}
return stream;
}
public PrintWriter getWriter() throws IOException {
if (writer != null) {
return writer;
}
if (stream != null) {
throw new IllegalStateException(
"getOutputStream() has already been called for this resposne");
}
stream = createOutputStream();
String charEnc = origResponse.getCharacterEncoding();
if (charEnc != null) {
writer = new PrintWriter(new OutputStreamWriter(stream, charEnc));
} else {
writer = new PrintWriter(stream);
}
return writer;
}
}
public class CompressionFilter implements Filter {
private FilterConfig config = null;
private int minThreshold = 128;
protected int compressionThreshold;
public void init(FilterConfig filterConfig) throws ServletException {
this.config = filterConfig;
if (filterConfig != null) {
String str = filterConfig.getInitParameter("compressionThreshold");
if (compressionThreshold != 0 & compressionThreshold < minThreshold) {
compressionThreshold = minThreshold;
} else {
// 不对响应信息的实体内容进行压缩
compressionThreshold = 0;
}
} else {
compressionThreshold = 0;
}
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
/*
* 不对响应信息的实体内容进行压缩
*/
if (compressionThreshold == 0) {
chain.doFilter(request, response);
}
/*
* 检测客户端浏览器是否支持gzip数据压缩格式
*/
boolean supportCompression = false;
if (request instanceof HttpServletRequest) {
Enumeration<String> e = ((HttpServletRequest) request)
.getHeaders("Accept-Encoding");
while (e.hasMoreElements()) {
String name = e.nextElement();
if (name.indexOf("gzip") != -1) {
supportCompression = true;
}
}
}
if (!supportCompression) {
chain.doFilter(request, response);
return;
} else {
if (response instanceof HttpServletResponse) {
/*
* 创建自定义的Response对象
*/
CompressionServletResponseWrapper wrapperResponse = new
CompressionServletResponseWrapper((HttpServletResponse) response);
wrapperResponse.setCompressionThreshold(compressionThreshold);
try {
chain.doFilter(request, wrapperResponse);
} finally {
wrapperResponse.finishResponse();
}
}
return;
}
}
public void destroy() {
this.config = null;
}
}