您的位置:首页 > 编程语言 > Java开发

获取返回浏览器的内容 —— Java 缓存的使用

2015-10-23 10:17 513 查看
零. 引言

为什么使用缓存? 当网站流量逐渐增大, 数据库 IO 将比较早出现瓶颈, 而使用缓存, 可以使数据库瓶颈晚点到来, 从而提升网站性能。 Java Web 项目如何使用缓存? 缓存首先是要获取返回给页面的内容, 然后写入缓存(MemCached、 Redis等缓存), 本文使用 MemCached 作为示例。

二. 代码示例

要截获页面返回的内容,整体的思路是先把原始返回的页面内容写入到一个字符Writer,然后再组装成字符串并进行分析,最后再返回给客户端。代码如下:

用于包装 ServletOutputStream:
public class FilterServletOutputStream extends ServletOutputStream{
// 不需要太多方法, 可以用 OutputStream 代替
private DataOutputStream stream;

public FilterServletOutputStream(OutputStream output) {
stream = new DataOutputStream(output);
}

public void write(int b) throws IOException {
stream.write(b);
}

public void write(byte[] b) throws IOException {
stream.write(b);
}

public void write(byte[] b, int off, int len) throws IOException {
stream.write(b, off, len);
}

}

获取内容包装类:
import java.io.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Created by zhuyb on 15/10/21.
*/
public class MyHttpServletResponseWrapper extends HttpServletResponseWrapper implements Serializable{

private static final long serialVersionUID = -7207188787151521963L;

private static final Logger LOG = LoggerFactory.getLogger(MyHttpServletResponseWrapper.class);

private int statusCode = SC_OK;
private int contentLength;
private String contentType;
private final Map<String, List<Serializable>> headersMap = new TreeMap<String, List<Serializable>>(String.CASE_INSENSITIVE_ORDER);
private final List cookies = new ArrayList();
private ByteArrayOutputStream outstr;
private PrintWriter writer;
private boolean disableFlushBuffer = true;

/**
* Creates a GenericResponseWrapper
*/
public MyHttpServletResponseWrapper(final HttpServletResponse response) {
super(response);
// 输出 HTML 结果的地方
this.outstr = new ByteArrayOutputStream();
}

// 获取数据
public byte[] getData() {
if (writer != null)
writer.flush();
return outstr.toByteArray();
}

/**
* Gets the outputstream.
*/
public ServletOutputStream getOutputStream() {
return new FilterServletOutputStream(outstr);
}

/**
* Sets the status code for this response.
*/
public void setStatus(final int code) {
statusCode = code;
super.setStatus(code);
}

/**
* Send the error. If the response is not ok, most of the logic is bypassed and the error is sent raw
* Also, the content is not cached.
*
* @param i the status code
* @param string the error message
* @throws IOException
*/
public void sendError(int i, String string) throws IOException {
statusCode = i;
super.sendError(i, string);
}

/**
* Send the error. If the response is not ok, most of the logic is bypassed and the error is sent raw
* Also, the content is not cached.
*
* @param i the status code
* @throws IOException
*/
public void sendError(int i) throws IOException {
statusCode = i;
super.sendError(i);
}

/**
* Send the redirect. If the response is not ok, most of the logic is bypassed and the error is sent raw.
* Also, the content is not cached.
*
* @param string the URL to redirect to
* @throws IOException
*/
public void sendRedirect(String string) throws IOException {
statusCode = HttpServletResponse.SC_MOVED_TEMPORARILY;
super.sendRedirect(string);
}

/**
* Returns the status code for this response.
*/
public int getStatus() {
return statusCode;
}

/**
* Sets the content length.
*/
public void setContentLength(final int length) {
this.contentLength = length;
super.setContentLength(length);
}

/**
* Gets the content length.
*/
public int getContentLength() {
return contentLength;
}

/**
* Sets the content type.
*/
public void setContentType(final String type) {
this.contentType = type;
super.setContentType(type);
}

/**
* Gets the content type.
*/
public String getContentType() {
return contentType;
}

/**
* Gets the print writer. 最终使用输出流写回浏览器
*/
public PrintWriter getWriter() throws IOException {
if (writer == null) {
writer = new PrintWriter(new OutputStreamWriter(getOutputStream(), getCharacterEncoding()), true);
}
return writer;
}

/**
* @see javax.servlet.http.HttpServletResponseWrapper#addHeader(java.lang.String, java.lang.String)
*/
@Override
public void addHeader(String name, String value) {
List<Serializable> values = this.headersMap.get(name);
if (values == null) {
values = new LinkedList<Serializable>();
this.headersMap.put(name, values);
}
values.add(value);

super.addHeader(name, value);
}

/**
* @see javax.servlet.http.HttpServletResponseWrapper#setHeader(java.lang.String, java.lang.String)
*/
@Override
public void setHeader(String name, String value) {
final LinkedList<Serializable> values = new LinkedList<Serializable>();
values.add(value);
this.headersMap.put(name, values);

super.setHeader(name, value);
}

/**
* @see javax.servlet.http.HttpServletResponseWrapper#addDateHeader(java.lang.String, long)
*/
@Override
public void addDateHeader(String name, long date) {
List<Serializable> values = this.headersMap.get(name);
if (values == null) {
values = new LinkedList<Serializable>();
this.headersMap.put(name, values);
}
values.add(date);

super.addDateHeader(name, date);
}

/**
* @see javax.servlet.http.HttpServletResponseWrapper#setDateHeader(java.lang.String, long)
*/
@Override
public void setDateHeader(String name, long date) {
final LinkedList<Serializable> values = new LinkedList<Serializable>();
values.add(date);
this.headersMap.put(name, values);

super.setDateHeader(name, date);
}

/**
* @see javax.servlet.http.HttpServletResponseWrapper#addIntHeader(java.lang.String, int)
*/
@Override
public void addIntHeader(String name, int value) {
List<Serializable> values = this.headersMap.get(name);
if (values == null) {
values = new LinkedList<Serializable>();
this.headersMap.put(name, values);
}
values.add(value);

super.addIntHeader(name, value);
}

/**
* @see javax.servlet.http.HttpServletResponseWrapper#setIntHeader(java.lang.String, int)
*/
@Override
public void setIntHeader(String name, int value) {
final LinkedList<Serializable> values = new LinkedList<Serializable>();
values.add(value);
this.headersMap.put(name, values);

super.setIntHeader(name, value);
}

/**
* Adds a cookie.
*/
public void addCookie(final Cookie cookie) {
cookies.add(cookie);
super.addCookie(cookie);
}

/**
* Gets all the cookies.
*/
public Collection getCookies() {
return cookies;
}

/**
* Flushes buffer and commits response to client.
*/
public void flushBuffer() throws IOException {
flush();

// doing this might leads to response already committed exception
// when the PageInfo has not yet built but the buffer already flushed
// Happens in Weblogic when a servlet forward to a JSP page and the forward
// method trigger a flush before it forwarded to the JSP
// disableFlushBuffer for that purpose is 'true' by default
// EHC-447
if (!disableFlushBuffer) {
super.flushBuffer();
}
}

/**
* Resets the response.
*/
public void reset() {
super.reset();
cookies.clear();
headersMap.clear();
statusCode = SC_OK;
contentType = null;
contentLength = 0;
}

/**
* Resets the buffers.
*/
public void resetBuffer() {
super.resetBuffer();
}

/**
* Flushes all the streams for this response.
*/
public void flush() throws IOException {
if (writer != null) {
writer.flush();
}
outstr.flush();
}

/**
* Is the wrapped reponse's buffer flushing disabled?
* @return true if the wrapped reponse's buffer flushing disabled
*/
public boolean isDisableFlushBuffer() {
return disableFlushBuffer;
}

/**
* Set if the wrapped reponse's buffer flushing should be disabled.
* @param disableFlushBuffer true if the wrapped reponse's buffer flushing should be disabled
*/
public void setDisableFlushBuffer(boolean disableFlushBuffer) {
this.disableFlushBuffer = disableFlushBuffer;
}
}


缓存过滤器:
public class CacheFilter implements Filter {
public void destroy() {
}

public void init(FilterConfig filterConfig) throws ServletException {
}

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;

String fullUrl = req.getRequestURL().toString();

// 带上 debug 参数不使用缓存, 方便后续开发调试
if (req.getParameter("debug") != null) {
cacheble = false;
MemcachedCacheManager.getCache("someproject_page_cache").put(key, "");
}

String key = fullUrl.replaceAll("(\\?|&)debug=1", "").hashCode();
// 如果页面路径包含 wenniuwuren 则缓存
boolean cacheble = “http://blog.csdn.net/wenniuwuren”.contains(“wenniuwuren");
MemCached pageCache = MemcachedCacheManager.getCache("jobmd_page_cache");
if (cacheble) {

if (pageCache != null) {
String html = (String) pageCache.get(key);
if (StringUtils.isNotBlank(html)) {
res.getWriter().write(html);
return;
}
// 使用我们自定义的响应包装器来包装原始的 ServletResponse
MyHttpServletResponseWrapper myHttpServletResponseWrapper = new MyHttpServletResponseWrapper(res);
// 注意此处传入的是 myHttpServletResponseWrapper, 而不是 response 参数, 这样才能获取页面返回内容
chain.doFilter(req, myHttpServletResponseWrapper);

if (myHttpServletResponseWrapper.getStatus() == 0 || myHttpServletResponseWrapper.getStatus() == 200) {
if (myHttpServletResponseWrapper.getData().length > 0) {
String data = new String(myHttpServletResponseWrapper.getData());

// 当然在这之前可以进行订制化处理数据, 然后返回浏览器。 页面装饰框架 sitemesh, 就是在这对 HTML 装饰处理后返回页面的

// 写到浏览器
res.getWriter().write(data);
// 放入缓存, 缓存 7200 秒
pageCache.put(key, data, 7200);
return;
} else { // 没数据则, 缓存清空
pageCache.put(key, "");
chain.doFilter(request, response);
}
}
} else { // 没缓存则继续执行
chain.doFilter(request, response);
}
} else { // 不缓存页面的处理流程
if (fullUrl.contains("debug=1")) {
pageCache.remove(key);
}
chain.doFilter(request, response);
}
}

}
web.xml 配置过滤器:
<filter>
<filter-name>cache</filter-name>
<filter-class>com.wenniuwuren.CacheFilter</filter-class>
<init-param>
<param-name>cachePath</param-name>
<param-value>/var/cache/project</param-value>
</init-param>
</filter>

<filter-mapping>
<filter-name>cache</filter-name>
<url-pattern>*.*</url-pattern>
</filter-mapping>

获取结果如下图: outstr 得到了页面返回的 HTML 代码:



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: