`
mixer_b
  • 浏览: 111753 次
社区版块
存档分类
最新评论

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

阅读更多

 

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

用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标签定义拦截器。

Html代码 
  1. <util:list id="interceptors">  
  2.      <bean class="com.netqin.common.cache.SearchCacheInterceptor"/>    
  3. </util:list>  
  4.   
  5. <!-- 定义注解URL映射处理器 -->  
  6. <bean id="urlMapping"  
  7.     class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">  
  8.     <property name="interceptors" ref="interceptors" />  
  9.     <property name="order" value="1"></property>  
  10. </bean>  

使用的是DefaultAnnotationHandlerMapping这个spring默认的基于注解的请求拦截器类。

ehcache用的是1.72版本,配置文件ehcache.xml为:

Html代码 
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  3.     xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true"  
  4.     monitoring="autodetect">  
  5.     <diskStore path="F:/appstore/ehcache"/>  
  6.     <defaultCache  
  7.             maxElementsInMemory="10000"  
  8.             eternal="false"  
  9.             timeToIdleSeconds="1800"  
  10.             timeToLiveSeconds="1800"  
  11.             overflowToDisk="true"  
  12.             maxElementsOnDisk="10000000"  
  13.             diskPersistent="false"  
  14.             diskExpiryThreadIntervalSeconds="120"  
  15.             memoryStoreEvictionPolicy="LRU"  
  16.             />  
  17.      <cache name="UrlCache"  
  18.            maxElementsInMemory="8000"  
  19.            maxElementsOnDisk="10000000"  
  20.            eternal="false"  
  21.            overflowToDisk="true"  
  22.            diskSpoolBufferSizeMB="20"  
  23.            memoryStoreEvictionPolicy="LFU"  
  24.             />  
  25. </ehcache>  

并且对ehcache进行了简单封装(不是我封装的):

Java代码 
  1. package com.netqin.common.cache;  
  2.   
  3. import java.net.URL;  
  4. import java.util.HashMap;  
  5. import java.util.Map;  
  6.   
  7. import net.sf.ehcache.Cache;  
  8. import net.sf.ehcache.CacheManager;  
  9. import net.sf.ehcache.Element;  
  10.   
  11. /** 
  12.  * cache管理器 
  13.  *  
  14.  * @author HANQUNFENG 
  15.  *  
  16.  */  
  17. public class CacheStore {  
  18.     private static CacheManager manager;  
  19.     private static Cache cache;  
  20.     static {  
  21.         CacheStore cs = new CacheStore();  
  22.         cs.init();  
  23.     }  
  24.   
  25.     /** 
  26.      * 初试化cache 
  27.      */  
  28.     private void init() {  
  29.         URL url = getClass().getResource("/config/context/ehcache.xml");  
  30.         manager = new CacheManager(url);  
  31.         cache = manager.getCache("UrlCache");  
  32.     }  
  33.   
  34.     /** 
  35.      * 清除cache 
  36.      */  
  37.     @SuppressWarnings("unused")  
  38.     private void destory() {  
  39.         manager.shutdown();  
  40.     }  
  41.   
  42.     /** 
  43.      * 得到某个key的cache值 
  44.      *  
  45.      * @param key 
  46.      * @return 
  47.      */  
  48.     public static Element get(String key) {  
  49.         return cache.get(key);  
  50.     }  
  51.   
  52.     /** 
  53.      * 清楚key的cache 
  54.      *  
  55.      * @param key 
  56.      */  
  57.     public static void remove(String key) {  
  58.         cache.remove(key);  
  59.     }  
  60.   
  61.     /** 
  62.      * 设置或更新某个cache值 
  63.      *  
  64.      * @param element 
  65.      */  
  66.     public static void put(Element element) {  
  67.         cache.put(element);  
  68.     }  
  69.       
  70.     public static void removeAll(){  
  71.         cache.removeAll();  
  72.     }  
  73.   
  74.       
  75.   
  76.     public static void main(String[] args) {  
  77.         Map m = new HashMap();  
  78.         m.put("1""1");  
  79.         m.put("2""2");  
  80.         m.put("3""3");  
  81.         Map m1 = new HashMap();  
  82.         m1.put("11""11");  
  83.         m1.put("21""21");  
  84.         m1.put("31""31");  
  85.         CacheStore.remove("1");  
  86.         System.out.println(CacheStore.get("1"));  
  87.         System.out.println(CacheStore.get("2"));  
  88.         System.out.println(CacheStore.get("2"));  
  89.         CacheStore.removeAll();  
  90.         System.out.println(CacheStore.get("2"));  
  91.         System.out.println(CacheStore.get("3"));  
  92.         System.out.println("------end-----");  
  93.     }  
  94. }  

下载ehcache.jar

 


拦截器代码:

Java代码 
  1. package com.netqin.common.cache;  
  2.   
  3. import java.io.UnsupportedEncodingException;  
  4. import java.util.Arrays;  
  5. import java.util.Map;  
  6.   
  7. import javax.servlet.http.HttpServletRequest;  
  8. import javax.servlet.http.HttpServletResponse;  
  9.   
  10. import net.sf.ehcache.Element;  
  11.   
  12. import org.apache.log4j.Logger;  
  13. import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;  
  14.   
  15. import com.netqin.common.util.AuthenticationUtils;  
  16.   
  17. /** 
  18.  * 查询条件缓存的拦截器 
  19.  *  
  20.  * @author KingViker 
  21.  *  
  22.  */  
  23. public class SearchCacheInterceptor extends HandlerInterceptorAdapter  
  24. {  
  25.     private static final Logger logger = Logger  
  26.             .getLogger(SearchCacheInterceptor.class);  
  27.   
  28.     @SuppressWarnings("unchecked")  
  29.     @Override  
  30.     public boolean preHandle(HttpServletRequest request,  
  31.             HttpServletResponse response, Object handler)throws Exception {  
  32.         logger.info("记录查询参数拦截器******begin");  
  33.         String url = request.getServletPath();  
  34.         String ip = request.getRemoteAddr();  
  35.         Map<String, String[]> paramsMap = request.getParameterMap();  
  36.         String userName = AuthenticationUtils.getUsername();  
  37.         if (url.indexOf("list.do") != -1) {  
  38.             if (paramsMap.get("goBack") != null) {  
  39.                 Element e = CacheStore.get(ip  
  40.                 + userName + url);  
  41.                 if (e != null) {  
  42.                     logger.info("取出缓存的查询参数,重定向连接");  
  43.                     response.sendRedirect(request.getRequestURL()+(String)e.getValue());  
  44.                     return false;  
  45.                 }  
  46.             } else {  
  47.                 logger.info("更新查询参数");  
  48.                 CacheStore.put(new Element(ip+ userName + url, changeMapToString(paramsMap)));  
  49.             }  
  50.         }  
  51.         logger.info("记录查询参数拦截器******begin");  
  52.         return true;  
  53.     }  
  54.   
  55.     private String changeMapToString(Map<String, String[]> paramsMap) {  
  56.         StringBuffer url = new StringBuffer();  
  57.         url.append("?");  
  58.         for(Map.Entry<String, String[]> entry :paramsMap.entrySet()){  
  59.             String key = entry.getKey();  
  60.             String [] value = entry.getValue();  
  61.             url.append(key+"="+encodeUrl(Arrays.toString(value)));  
  62.             url.append("&");  
  63.         }  
  64.         System.out.println(url);  
  65.         return url.toString();  
  66.     }  
  67.     private String encodeUrl(String value) {  
  68.         String result = "";  
  69.         result = value.replaceAll("\\[""").replaceAll("\\]""");  
  70.         try {  
  71.             byte temp[]=result.getBytes("UTF-8");  
  72.             result=new String(temp,"ISO-8859-1");      
  73.         } catch (UnsupportedEncodingException e) {  
  74.             e.printStackTrace();  
  75.         }      
  76.         return result;  
  77.     }  
  78. }  

拦截器类继承自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各单位来实现功能。我个人提倡,用框架的时候多想想实现,别一味的用,要不然用到老,还是在用别人的框架,永远写不出自己的框架。

18
6
分享到:
评论
18 楼 blacklab 2012-06-21  
17 楼 szsuyuji 2012-06-21  
使用 cookie 好了,返回页面时重新取出填入输入框然后触发查询。
16 楼 nighty 2012-06-21  
为啥明细页面要在原有页面跳转?这不折腾吗,直接新弹一个页面才符合逻辑,你往往会同时打开好几个明细慢慢看的
15 楼 archy123 2012-06-21  
spring 3.1 的业务缓存难道是摆设,费这劲干嘛!
14 楼 uddjatigmh199 2012-06-20  
又重复制轮子了吧
13 楼 zouruixin 2012-06-20  
是不是有点想复杂了, 重新打开一个页面看明细不就解决问题了
12 楼 xbcoil 2012-06-20  
Cookie +1 ,楼主只是把用户的操作记录存储在服务器的ehcache里了。如果放cookie会更直接。如果能够缓存页面内容,那么才有意义~
11 楼 290845534 2012-06-20  
如果是集群那怎么办
10 楼 yangfuchao418 2012-06-20  
返回list.do链接的时候,楼主仅仅是缓存了完整的URL吧?没有缓存相应的内容?
9 楼 EXvision 2012-06-20  
Spring3.1已经有原生的@cacheable了。。。你们这是闹哪样
8 楼 strivezsh 2012-06-20  
觉着 cookie更好点
7 楼 7454103 2012-06-20  
hellostory 写道
7454103 写道
Spring MVC 有类似的功能! 也是利用 ehcache 而且很实用,可以借鉴下

能否指教下?


可以参考下:http://www.yxif.com/spring3cache/ 
那个页面打开可能有点慢哈,服务器在国外!
我们现在的项目就在使用,有问题可以联系我,Email:jspp@qq.com
6 楼 wangshi_ws 2012-06-20  
7454103 写道
Spring MVC 有类似的功能! 也是利用 ehcache 而且很实用,可以借鉴下

我也想知道
5 楼 hellostory 2012-06-19  
7454103 写道
Spring MVC 有类似的功能! 也是利用 ehcache 而且很实用,可以借鉴下

能否指教下?
4 楼 7454103 2012-06-19  
Spring MVC 有类似的功能! 也是利用 ehcache 而且很实用,可以借鉴下
3 楼 piziwang 2012-06-19  
需要这么麻烦吗? 点击在新窗口查看明细不就得了啊
2 楼 北京-湖南人 2012-06-19  
不用这么复杂吧!直接放Session 不就可以了?
1 楼 aijuans 2012-06-19  
不错.多些分享

相关推荐

Global site tag (gtag.js) - Google Analytics