實現一個java web緩存

默認情況下,java並不完成緩存。要安裝URL類使用的系統級緩存,需要由:
ResponseCache的一個具體子類。
CacheRequest的一個具體子類。
CacheResponse的一個具體子類。
要安裝你的ResponseCache子類來處理你的CacheRequest子類和CacheResponse子類,需要把它傳遞到靜態放ResponseCache.setDefault()。這會把這個緩存對象安裝爲系統的默認緩存。Java虛擬機只支持一個共享緩存。
一旦安裝了緩存,只要系統嘗試加載一個新的URL,它手下會在緩存中查找。如果緩存返回了所要的內容,URLConnecton就不需要與遠程服務器連接。不過,如果所請求的數據不在緩存中,協議處理器將從遠程服務器下載相應數據。完成之後,它會把這個響應放在緩存中,使得下一次加載這個URL時,可以很快從緩存中得到這個內容。
ResponseCache提供了兩個抽象方法,可以存儲和獲取系統緩存中的數據:
public abtract CacheResponse get(URI uri, String requestMethod, Map<String,List<String>> requestHeaders ) throws IOException
public abtract CacheResponse put(URI uri, URLConnection connection) throws IOException
put()方法返回一個CacheResponse 對象,它包裝了一個OutputStream,URL將把讀取的可緩存數據寫入這個輸入流。

get()方法從緩存中獲取數據和首部,包裝在CacheResponse對象中返回           

package cache;

import java.util.Date;
import java.util.Locale;

//緩存參數類
public class CacheControl {

	private Date maxAge = null; //從現在知道緩存項過期前的秒數
	private Date sMaxAge = null; //從現在起,直到緩存項在共享緩存中過期之前的秒數
	private boolean mustRevalidate = false; //
	private boolean noCache = false; //這個策略的作用與名字不太一致。緩存 項任然可以緩存,不過客戶端在每次訪問時要用一個Etag或者Last-modified首部重新驗證響應的狀態
	private boolean noStore = false;//不管怎麼樣都不緩存
	private boolean proxyRevalidate = false; 
	private boolean publicCache = false; //可以緩存一個經過認證的響應
	private boolean privateCache = false;//僅單個用戶緩存可以保存響應
	
	public CacheControl(String s){
		if(s == null || !s.contains(":")){
			return;
		}
		
		String value = s.split(":")[1].trim();
		String[] components = value.split(",");
		
		Date now = new Date();
		for(String component : components){
			try{
				component = component.trim().toLowerCase(Locale.US);
				if(component.startsWith("max-age=")){
					int secondsInTheFuture = Integer.parseInt(component.substring(8));
					maxAge = new Date(now.getTime() + 1000 * secondsInTheFuture);
				}else if(component.startsWith("s-maxage=")){
					int secondsInTheFuture = Integer.parseInt(component.substring(8));
					maxAge = new Date(now.getTime() + 1000 * secondsInTheFuture);
				}else if(component.equals("must-revalidate")){
					mustRevalidate = true;
				}else if(component.equals("proxy-revalidate")){
					proxyRevalidate = true;
				}else if(component.equals("no-cache")){
					noCache = true;
				}else if(component.equals("public")){
					publicCache = true;
				}else if(component.equals("private")){
					privateCache = true;
				}
			}catch(RuntimeException ex){
				continue;
			}
			
			
		}
	}

	public Date getMaxAge() {
		return maxAge;
	}

	public Date getsMaxAge() {
		return sMaxAge;
	}

	public boolean isMustRevalidate() {
		return mustRevalidate;
	}

	public boolean isNoCache() {
		return noCache;
	}

	public boolean isNoStore() {
		return noStore;
	}

	public boolean isProxyRevalidate() {
		return proxyRevalidate;
	}

	public boolean isPublicCache() {
		return publicCache;
	}

	public boolean isPrivateCache() {
		return privateCache;
	}
}

package cache;

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

public class MyCacheRequest extends CacheRequest{

	private ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
	
	//返回從協議處理器獲得的輸出流
	@Override
	public OutputStream getBody() throws IOException {
		return outputStream;
	}

	//當協議處理器把從服務器讀取的數據複製到OutputStream時發生意外中斷,協議處理器就調用該方法刪除緩存中這個請求的所有數據
	@Override
	public void abort() {
		outputStream.reset();
	}
	
	//返回請求的數據
	public byte[] getData(){
		if(outputStream.size() == 0){
			return null;
		}else{
			return outputStream.toByteArray();
		}
	}

}


package cache;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.CacheResponse;
import java.net.URLConnection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;

import java7_URLConnection.CacheControl;

public class MyCacheResponse extends CacheResponse{
	private MyCacheRequest myCacheRequest = null;//請求的數據
	private Map<String,List<String>> headers = null;//請求的數據的首部信息
	private CacheControl control;//請求的數據的緩存策略
	private Date expires;//請求的數據的過期時間
	
	public MyCacheResponse(MyCacheRequest myCacheRequest, URLConnection conn,
			Map<String,List<String>> headers,CacheControl control){
		this.headers = headers;//獲取所有首部信息的鍵值對形式
		this.myCacheRequest = myCacheRequest;
		this.control = control;
		this.expires = new Date(conn.getExpiration());
	}

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

	@Override
	public InputStream getBody() throws IOException {
		return new ByteArrayInputStream(myCacheRequest.getData());
	}
	
	//返回緩存的數據協議信息
	public CacheControl getControl() {
		return control;
	}
	
	//緩存是否過期
	public boolean isExpires(){
		Date now = new Date();
		if(control.getMaxAge().before(now)){
			return true;
		}else if(expires != null && control.getMaxAge() != null){
			return expires.before(now);
		}else{
			return false;
		}
	}

}


package cache;

import java.io.IOException;
import java.net.CacheRequest;
import java.net.CacheResponse;
import java.net.ResponseCache;
import java.net.URI;
import java.net.URLConnection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import java7_URLConnection.CacheControl;

public class MyResponseCache extends ResponseCache{
	//維護一個Map用以緩存所有的URI請求
	private Map<URI,MyCacheResponse> responses = new ConcurrentHashMap<URI, MyCacheResponse>();
	private int maxEntries;
	
	public MyResponseCache(){
		this(100);
	}
	
	public MyResponseCache(int maxEntries){
		this.maxEntries = maxEntries;
	}

	@Override
	public CacheResponse get(URI uri, String rqstMethod,
			Map<String, List<String>> rqstHeaders) throws IOException {
		if("GET".equals(rqstMethod)){
			MyCacheResponse responseCache = responses.get(uri);
			//檢查過期時間
			if(responseCache != null && responseCache.isExpires()){
				responses.remove(uri);
				responseCache = null;
			}
			return responseCache;
		}else{
			return null;
		}
	}

	@Override
	public CacheRequest put(URI uri, URLConnection conn) throws IOException {
		CacheControl control = new CacheControl(conn.getHeaderField("Cache-Control"));
		//如果需要緩存的uri超過100則不允許緩存
		if(responses.size() >= maxEntries){
			return null;
		}
		//判斷是否需要緩存
		if(control.isNoStore()){
			return null;
		}else if(!conn.getHeaderField(0).startsWith("GET")){//只緩存get請求
			return null;
		}
		
		//從服務器中讀取需要緩存的信息
		MyCacheRequest cacheRequest = new MyCacheRequest();
		//將信息保存到MyCacheResponse中,請求的數據,請求頭,緩存策略
		MyCacheResponse cacheResponse = new MyCacheResponse(cacheRequest, conn,
				Collections.unmodifiableMap(conn.getHeaderFields()), control);
		//緩存該信息
		responses.put(uri, cacheResponse);
		return cacheRequest;
	}

}



java要求一次只能有一個URL緩存。要安裝或改變緩存,需要使用靜態方法ResponseCache.setDefault()和ResponseCache.getDefault():
public static ResponseCache getDefault()
public static void setDefault(ResponseCache responseCache )
這些方法會在設置同一個java虛擬機中運行的所有程序所使用的緩存。例如,在下面這行代碼中會在應用中安裝:
ResponseCache.getDefault(new MyResponseCache):
一旦安裝了類似示例的緩存,HTTP URLConnection就會一直使用這個緩存。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章