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

零. 引言

为什么使用缓存? 当网站流量逐渐增大, 数据库 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 代码:

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-16 02:03:20

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

Java缓存学习之二:浏览器缓存机制

浏览器端的九种缓存机制介绍 浏览器缓存是浏览器端保存数据用于快速读取或避免重复资源请求的优化机制,有效的缓存使用可以避免重复的网络请求和浏览器快速地读取本地数据,整体上加速网页展示给用户.浏览器端缓存的机制种类较多,总体归纳为九种,这里详细分析下这九种缓存机制的原理和使用场景.打开浏览器的调试模式->resources左侧就有浏览器的8种缓存机制. 一.http缓存 http缓存是基于HTTP协议的浏览器文件级缓存机制.即针对文件的重复请求情况下,浏览器可以根据协议头判断从服务器端请求文件还是从

定义一个方法get_page(url),url参数是需要获取网页内容的网址,返回网页的内容。提示(可以了解python的urllib模块)

1 定义一个方法get_page(url),url参数是需要获取网页内容的网址,返回网页的内容.提示(可以了解python的urllib模块) 2 import urllib.request 3 4 def get_page(url): 5 response = urllib.request.urlopen(url) 6 html = response.read() 7 return html 8 9 print(get_page(url='https://www.baidu,com'))

工作随笔——Java调用Groovy类的方法、传递参数和获取返回值

接触Groovy也快一年了,一直在尝试怎么将Groovy引用到日常工作中来.最近在做一个功能的时候,花了点时间重新看了下Java怎么调用Groovy的方法.传递参数和获取返回值. 示例Groovy代码如下: # TestGroovy.groovy 定义testC方法,传入3个参数,返回处理后的数据 def testC(int numA, int numB, int numC) { "传入参数:" + numA + numB + numC + "计算之和为:" + (

java多线程之从任务中获取返回值

package wzh.test; import java.util.ArrayList; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; class TaskWithResult implements Callable<Strin

java URL实现调用其他系统发送报文并获取返回数据

模拟本系统通过Url方式发送报文到目标服务器,并获取返回数据:(实现类) 1 import java.io.BufferedOutputStream; 2 import java.io.BufferedReader; 3 import java.io.InputStream; 4 import java.io.InputStreamReader; 5 import java.io.OutputStream; 6 import java.io.OutputStreamWriter; 7 impor

java 如何获取网页的动态内容,并解析网页内容

(笔记) 获取网页的动态内容参考 https://pastebin.com/raw/FePkm2kp Maven: <!--获取网页源码,包括动态内容--><dependency> <groupId>htmlunit</groupId> <artifactId>htmlunit</artifactId> <version>1.14</version></dependency> 实现: WebClie

Java缓存学习之三:CDN缓存机制

CDN是什么? 关于CDN是什么,此前网友详细介绍过. CDN是Content Delivery Network的简称,即"内容分发网络"的意思.一般我们所说的CDN加速,一般是指网站加速或者用户下载资源加速. 举个通俗的例子: 谈到CDN的作用,可以用8年买火车票的经历来形象比喻:8年前,还没有火车票代售点一说,12306.cn更是无从说起.那时候火车票还只能在火车站的售票大厅购买,而我所住的小县城并不通火车,火车票都要去市里的火车站购买,而从县城到市里,来回就是4个小时车程,简直就

【黑马Android】(07)多线程下载的原理/开源项目xutils/显示意图/隐式意图/人品计算器/开启activity获取返回值

多线程下载的原理 司马光砸缸,多开几个小水管,抢救小朋友. import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.io.InputStreamReader; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import

(转)java缓存技术,记录

http://blog.csdn.net/madun/article/details/8569860 最近再ITEYE上看到关于讨论JAVA缓存技术的帖子比较多,自己不懂,所以上网大概搜了下,找到一篇,暂作保存,后面如果有用到可以参考.此为转贴,帖子来处:http://cogipard.info/articles/cache-static-files-with-jnotify-and-ehcache 介绍 JNotify:http://jnotify.sourceforge.net/,通过JNI