最近項目有一個需求,就是用戶在查詢界面,輸入不少查詢條件以後,查詢出了須要的信息,而後點擊查看詳細以後,但願查詢列表頁面時還能保存上一次的查詢條件。通過同事之間的簡單討論以後,肯定了實現方案。java
用spring的攔截器,攔截到用戶的全部list.do請求,保存下list.do,把裏面的request。paramaterMap轉換成字符串(注意中文轉碼),以ip+username+功能模塊url爲key,保存下來,用戶在詳細信息頁面點擊返回時,返回鏈接須要帶goback參數,攔截器監測到請求參數裏包含goback時,就用ip+username+功能模塊url把保存的值拿出來,以後response.sendRedirect(request.getRequestURL()+str).web
上面只是大致實現的歸納,下面看代碼。spring
定義spring攔截器,項目的spring版本是2.5的,不支持mvc:interceptors標籤訂義攔截器。apache
Html代碼 編程
<util:list id="interceptors"> 瀏覽器
<bean class="com.netqin.common.cache.SearchCacheInterceptor"/> 緩存
</util:list> mvc
<!-- 定義註解URL映射處理器 --> app
<bean id="urlMapping" 框架
class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors" ref="interceptors" />
<property name="order" value="1"></property>
</bean>
使用的是DefaultAnnotationHandlerMapping這個spring默認的基於註解的請求攔截器類。
ehcache用的是1.72版本,配置文件ehcache.xml爲:
Html代碼
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true"
monitoring="autodetect">
<diskStore path="F:/appstore/ehcache"/>
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="1800"
overflowToDisk="true"
maxElementsOnDisk="10000000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
/>
<cache name="UrlCache"
maxElementsInMemory="8000"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="true"
diskSpoolBufferSizeMB="20"
memoryStoreEvictionPolicy="LFU"
/>
</ehcache>
而且對ehcache進行了簡單封裝(不是我封裝的):
Java代碼
package com.netqin.common.cache;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
/**
* cache管理器
*
* @author HANQUNFENG
*
*/
public class CacheStore {
private static CacheManager manager;
private static Cache cache;
static {
CacheStore cs = new CacheStore();
cs.init();
}
/**
* 初試化cache
*/
private void init() {
URL url = getClass().getResource("/config/context/ehcache.xml");
manager = new CacheManager(url);
cache = manager.getCache("UrlCache");
}
/**
* 清除cache
*/
@SuppressWarnings("unused")
private void destory() {
manager.shutdown();
}
/**
* 獲得某個key的cache值
*
* @param key
* @return
*/
public static Element get(String key) {
return cache.get(key);
}
/**
* 清楚key的cache
*
* @param key
*/
public static void remove(String key) {
cache.remove(key);
}
/**
* 設置或更新某個cache值
*
* @param element
*/
public static void put(Element element) {
cache.put(element);
}
public static void removeAll(){
cache.removeAll();
}
public static void main(String[] args) {
Map m = new HashMap();
m.put("1", "1");
m.put("2", "2");
m.put("3", "3");
Map m1 = new HashMap();
m1.put("11", "11");
m1.put("21", "21");
m1.put("31", "31");
CacheStore.remove("1");
System.out.println(CacheStore.get("1"));
System.out.println(CacheStore.get("2"));
System.out.println(CacheStore.get("2"));
CacheStore.removeAll();
System.out.println(CacheStore.get("2"));
System.out.println(CacheStore.get("3"));
System.out.println("------end-----");
}
}
下載ehcache.jar
攔截器代碼:
Java代碼
package com.netqin.common.cache;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.sf.ehcache.Element;
import org.apache.log4j.Logger;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import com.netqin.common.util.AuthenticationUtils;
/**
* 查詢條件緩存的攔截器
*
* @author KingViker
*
*/
public class SearchCacheInterceptor extends HandlerInterceptorAdapter
{
private static final Logger logger = Logger
.getLogger(SearchCacheInterceptor.class);
@SuppressWarnings("unchecked")
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler)throws Exception {
logger.info("記錄查詢參數攔截器******begin");
String url = request.getServletPath();
String ip = request.getRemoteAddr();
Map<String, String[]> paramsMap = request.getParameterMap();
String userName = AuthenticationUtils.getUsername();
if (url.indexOf("list.do") != -1) {
if (paramsMap.get("goBack") != null) {
Element e = CacheStore.get(ip
+ userName + url);
if (e != null) {
logger.info("取出緩存的查詢參數,重定向鏈接");
response.sendRedirect(request.getRequestURL()+(String)e.getValue());
return false;
}
} else {
logger.info("更新查詢參數");
CacheStore.put(new Element(ip+ userName + url, changeMapToString(paramsMap)));
}
}
logger.info("記錄查詢參數攔截器******begin");
return true;
}
private String changeMapToString(Map<String, String[]> paramsMap) {
StringBuffer url = new StringBuffer();
url.append("?");
for(Map.Entry<String, String[]> entry :paramsMap.entrySet()){
String key = entry.getKey();
String [] value = entry.getValue();
url.append(key+"="+encodeUrl(Arrays.toString(value)));
url.append("&");
}
System.out.println(url);
return url.toString();
}
private String encodeUrl(String value) {
String result = "";
result = value.replaceAll("\\[", "").replaceAll("\\]", "");
try {
byte temp[]=result.getBytes("UTF-8");
result=new String(temp,"ISO-8859-1");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return result;
}
}
攔截器類繼承自HandlerInterceptorAdapter,重寫了prehandle方法。prehandle是在請求前執行,還能夠重寫afterCompletion和postHandle兩個方法,分別是請求後和請求先後執行。
個人攔截器的代碼原先的邏輯不是這樣。我原先的邏輯是利用反射直接更改request的parameterMap保存的值,不須要其餘的操做很簡單,也不須要重定向。可是這個思路有兩個問題:
1.我用ehcache保存時直接保存的是parameterMap的引用,每次請求過來以後spring都會清空而且從新初始化這個map,致使ehcache未能緩存到真正的數據。
2.在測試時發現,spring框架從請求接受參數後而且封裝到了bean裏面以後請求才能攔截到,也就是說我更改了parameterMap的值,controller收到的請求仍是未更改的數據。
因此我改變了思路,把每次請求的parameterMap對象封裝成字符串而後在緩存,須要使用緩存時,直接取出並重定向sendredirectt。
可是sendreditect是get方式的請求,瀏覽器通常會把請求鏈接中的中文參數進行轉碼,轉成ISO-8859-1,產生了亂碼問題,因此須要在連接後面拼接參數時須要對中文轉碼。這也就是上面的encodeUrl函數的做用。
寫這篇文章的目的只是介紹一下實現思路和用到的一些現有框架,以及其中遇到的一些問題。給一些須要實現相似功能的道友一個實現思路而已。這裏不提供源碼,我我的是很不喜歡伸手黨的。就是由於編程界有太多的伸手黨,致使了不少人能作一些項目,可是出現問題就不知道怎麼改,或者明明性能上只須要1各單位,恰恰不知因此然的用了10各單位來實現功能。我我的提倡,用框架的時候多想一想實現,別一味的用,要否則用到老,仍是在用別人的框架,永遠寫不出本身的框架。