spring+ehcache实现的缓存查询参数

openkk 12年前

最近项目有一个需求,就是用户在查询界面,输入很多查询条件之后,查询出了需要的信息,然后点击查看详细之后,希望查询列表页面时还能保存上一次的查询条件。经过同事之间的简单讨论之后,确定了实现方案。

用spring的拦截器,拦截到用户的所有list.do请求,保存下list.do,把里面的request。paramaterMap转换成字符串(注意中文转码),以ip+username+功能模块url为key,保存下来,用户在详细信息页面点击返回时,返回连接需要带goback参数,拦截器监测到请求参数里包含goback时,就用ip+username+功能模块url把保存的值拿出来,之后response.sendRedirect(request.getRequestURL()+str).

上面只是大体实现的概括,下面看代码。

定义spring拦截器,项目的spring版本是2.5的,不支持mvc:interceptors标签定义拦截器。

 <util:list id="interceptors">     <bean class="com.netqin.common.cache.SearchCacheInterceptor"/>     </util:list>     <!-- 定义注解URL映射处理器 -->   <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为:

<?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进行了简单封装(不是我封装的):
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


拦截器代码:

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各单位来实现功能。我个人提倡,用框架的时候多想想实现,别一味的用,要不然用到老,还是在用别人的框架,永远写不出自己的框架。
转自:http://blog.csdn.net/qq413041153/article/details/7631676