Java中使用ResponseCache类构建本地缓存

《Java网络编程》中介绍了使用Java中的ResponseCache实现本地缓存HTTP请求的效果。例子举得复杂,这里有一个简单的例子可以来说明,而且可以对本地缓存有更深入的理解。

 

use the java.net package formaking HTTP requestsWhile the java.net package includes support for caching requests it does not include a default caching implementationand it seemed pretty straightforward to implement the ResponseCache, CacheRequest and CacheResponse classes

默认情况下,Java并不完成缓存。要实现本地缓存,需要安装URL类使用的系统级缓存,需要有:ResponseCache,CacheRequest and CacheResponse classes

 

Implementing the Cache

There are three classes to implement to create a cache that can be used by URLConnection in the java.net package. They are ResponseCache, CacheRequest, and CacheResponse. You will create your own implementation of these three classes then you will register your ResponseCache class to be used as the cache for URLConnections.

 

First: extend the request classes:

表示在 ResponseCache 中存储资源的通道。这种类的实例提供一个 OutputStream 对象,协议处理程序可以调用该对象来将资源数据存储到缓存中

CacheRequest is very simple. It has one member which is a ByteArrayOutputStream which is returned by its getBody() method. The ByteArrayOutputStream will eventually be passed to acompanion CacheResponse instance.

 

package _07ResponseCache应用实例;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.CacheRequest;

public class CCCacheRequest extends CacheRequest {

	final ByteArrayOutputStream body = new ByteArrayOutputStream();
	@Override
	public OutputStream getBody() throws IOException {
		return body;
	}

	@Override
	public void abort() {
	}

 

Second: extend the response classes:

 

表示从 ResponseCache 获取资源的通道。这种类的实例提供返回实体正文的 InputStream,同时提供一个返回关联响应头的getHeaders() 方法。

CacheResponse is also very simple. Its constructor will accept a Map of HTTP headers, and anOutputStream that we’ll get from a CacheRequest instance but cast to a ByteArrayOutputStream. The ByteArrayOutputStream is converted to a byte array and passed to the ByteArrayInputStream returned by the CacheRequest’s getBody()method.

 

package _07ResponseCache应用实例;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.CacheResponse;
import java.util.List;
import java.util.Map;

public class CCCacheResponse extends CacheResponse {

	Map<String, List<String>> headers;
	ByteArrayOutputStream body;

	public CCCacheResponse(Map<String, List<String>> headers, OutputStream body) {
		this.headers = headers;
		// should be the output stream defined in a companion CacheRequest.
		this.body = (ByteArrayOutputStream) body;
	}

	@Override
	public Map<String, List<String>> getHeaders() throws IOException {
		return headers;
	}

	@Override
	public InputStream getBody() throws IOException {
		return new ByteArrayInputStream(body.toByteArray());
	}

 

 

 

Third: extend the ResponseCache classes:

 

ResponseCache表示URLConnection 缓存的实现。

这种类的实例可以通过执行ResponseCache.setDefault(ResponseCache) 向系统注册,系统将调用此对象以便:

1. 将从外部源获得的资源数据存储到缓存中

2. 试图获取可能存储在缓存中的请求资源

 

ResponseCache defines the HashMap that actually stores the HTTP responses, and creates instances of the CacheRequest and CacheResponse classes.

import java.io.IOException;
import java.net.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created by Administrator on 2017/7/6.
 */
public class CCResponseCache extends ResponseCache {

    private final int maxEntries = 2;

    Map<URI, CacheResponse> mCache = new ConcurrentHashMap<>();

    @Override
    public CacheResponse get(URI uri, String rqstMethod,
                             Map<String, List<String>> rqstHeaders) throws IOException {
        //只缓存get请求
        if ("GET".equals(rqstMethod)) {
            CacheResponse resp = mCache.get(uri);
            return resp;
        }else{
            return null;
        }
    }

    @Override
    public CacheRequest put(URI uri, URLConnection conn) throws IOException {
        CacheRequest req = (CacheRequest) new CCCacheRequest();

        //缓存失效机制 由于使用了ConcurrentHashMap 所以这里只是随便选择了一项来删除
        //java中并没有ConcurrentLinkedHashMap so... fifo缓存失效机制没法实现啊
        if (mCache.size() >= maxEntries) {
            Set<Map.Entry<URI, CacheResponse>> entries = mCache.entrySet();
            Map.Entry<URI, CacheResponse> next = entries.iterator().next();
            mCache.remove(next.getKey());
        }
        Map<String, List<String>> headers = conn.getHeaderFields();
        //将CacheRequest的输出流传递给CacheResponse
        CacheResponse resp = (CacheResponse) new CCCacheResponse(headers, req.getBody());
        mCache.put(uri, resp);
        return req;
    }


    public void displayHashMap() {
        Set<Map.Entry<URI, CacheResponse>> entrySet = mCache.entrySet();
        for (Map.Entry<URI, CacheResponse> entry : entrySet) {
            System.out.println("in cache " + entry.getKey() + "--" + entry.getValue());
        }
    }
}

 

 

One thing to note about this implementation is that the cache is going to be stored in memory. This is fine so long as you are careful about how much data you store in the cache. If you expect to cache a large amount of data, it would be better to incorporate a database or some kind of file IO into the cache solution.

 

 

Last: use the locale cahce:

The last step is to create an instance of the ResponseCache and set it as the default cache via the staticsetDefault() method.
ResponseCache.setDefault(new CCResponseCache());

Now any URLConnection that is set to useCache(true) will use your cache when making HTTP requests. We’re all done right

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.MalformedURLException;
import java.net.ResponseCache;
import java.net.URL;
import java.net.URLConnection;
import java.util.Scanner;

/**
 * Created by Administrator on 2017/7/6.
 */
public class CCResponseCacheTest {

    public static void main(String[] args) {

        // 安装系统级缓存
        CCResponseCache responseCache = new CCResponseCache();
        ResponseCache.setDefault(responseCache);
        while (true) {
            try {
                Scanner scanner = new Scanner(System.in);
                String str = scanner.nextLine();
                URL url = new URL(str);
                URLConnection connection = url.openConnection();
                connection.setUseCaches(true);
                System.out.println();
                responseCache.displayHashMap();
                System.out.println();

                //执行顺序是 connection.getInputStream()的时候
                //会去执行CCResponseCache的get方法
                //如果mCache中有,那么直接从mCache中取出
                //如果没有,那么就执行put 之后再执行get
                //通过ResponseCache的get的CacheResponse来获取InputStreamReader
                BufferedInputStream bis = new BufferedInputStream(
                        connection.getInputStream());
                Reader reader = new InputStreamReader(bis);
                int c;
                //只显示10个字符的缓存内容
                int count = 10;
                while (count >= 0 && (c = reader.read()) != -1) {
                    System.out.print((char) c);
                    count--;
                }
                System.out.println();
            } catch (MalformedURLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

 

 

至此,对于HTTP请求会缓存到内存的HashMap中。第二次请求相同URL时,不再访问远程服务器,而是直接从HashMap中取出。

 

 

类图

执行结果

附:常见的缓存失效策略

1 FIFO ,first in first out ,最先进入缓存得数据在缓存空间不够情况下(超出最大元素限制时)会被首先清理出去

2 LFU , Less Frequently Used ,一直以来最少被使用的元素会被被清理掉。这就要求缓存的元素有一个hit 属性,在缓存空间不够得情况下,hit 值最小的将会被清出缓存。

3 LRU ,Least Recently Used ,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值