开发者社区> developerguy> 正文

Redis整合Spring结合使用缓存实例(转)

简介:          林炳文Evankaka原创作品。转载请注明出处http://blog.csdn.net/evankaka          摘要:本文介绍了如何在Spring中配置redis,并通过Spring中AOP的思想,将缓存的方法切入到有需要进入缓存的类或方法前面。
+关注继续查看

         林炳文Evankaka原创作品。转载请注明出处http://blog.csdn.net/evankaka

         摘要:本文介绍了如何在Spring中配置redis,并通过Spring中AOP的思想,将缓存的方法切入到有需要进入缓存的类或方法前面。

一、Redis介绍

什么是Redis?

      redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。

它有什么特点?

(1)Redis数据库完全在内存中,使用磁盘仅用于持久性。
(2)相比许多键值数据存储,Redis拥有一套较为丰富的数据类型。
(3)Redis可以将数据复制到任意数量的从服务器。

Redis 优势?
 (1)异常快速:Redis的速度非常快,每秒能执行约11万集合,每秒约81000+条记录。
 (2)支持丰富的数据类型:Redis支持最大多数开发人员已经知道像列表,集合,有序集合,散列数据类型。这使得它非常容易解决各种各样的问题,因为我们知道哪些问题是可以处理通过它的数据类型更好。
(3)操作都是原子性:所有Redis操作是原子的,这保证了如果两个客户端同时访问的Redis服务器将获得更新后的值。
(4)多功能实用工具:Redis是一个多实用的工具,可以在多个用例如缓存,消息,队列使用(Redis原生支持发布/订阅),任何短暂的数据,应用程序,如Web应用程序会话,网页命中计数等。

Redis 缺点?

(1)单线程

(2)耗内存

二、使用实例

本文使用maven+eclipse+sping

1、引入jar包

 

[html] view plain copy
 
  1.     <!--Redis start -->  
  2. <dependency>  
  3.     <groupId>org.springframework.data</groupId>  
  4.     <artifactId>spring-data-redis</artifactId>  
  5.     <version>1.6.1.RELEASE</version>  
  6. </dependency>  
  7. <dependency>  
  8.     <groupId>redis.clients</groupId>  
  9.     <artifactId>jedis</artifactId>  
  10.     <version>2.7.3</version>  
  11. </dependency>  
  12.    <!--Redis end -->  

2、配置bean

 

在application.xml加入如下配置

 

[html] view plain copy
 
  1. <!-- jedis 配置 -->  
  2.    <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig" >  
  3.          <property name="maxIdle" value="${redis.maxIdle}" />  
  4.          <property name="maxWaitMillis" value="${redis.maxWait}" />  
  5.          <property name="testOnBorrow" value="${redis.testOnBorrow}" />  
  6.    </bean >  
  7.   <!-- redis服务器中心 -->  
  8.    <bean id="connectionFactory"  class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" >  
  9.          <property name="poolConfig" ref="poolConfig" />  
  10.          <property name="port" value="${redis.port}" />  
  11.          <property name="hostName" value="${redis.host}" />  
  12.          <property name="password" value="${redis.password}" />  
  13.          <property name="timeout" value="${redis.timeout}" ></property>  
  14.    </bean >  
  15.    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" >  
  16.          <property name="connectionFactory" ref="connectionFactory" />  
  17.          <property name="keySerializer" >  
  18.              <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />  
  19.          </property>  
  20.          <property name="valueSerializer" >  
  21.              <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />  
  22.          </property>  
  23.    </bean >  
  24.      
  25.     <!-- cache配置 -->  
  26.    <bean id="methodCacheInterceptor" class="com.mucfc.msm.common.MethodCacheInterceptor" >  
  27.          <property name="redisUtil" ref="redisUtil" />  
  28.    </bean >  
  29.    <bean id="redisUtil" class="com.mucfc.msm.common.RedisUtil" >  
  30.          <property name="redisTemplate" ref="redisTemplate" />  
  31.    </bean >  

其中配置文件redis一些配置数据redis.properties如下:

 

 

[plain] view plain copy
 
  1. #redis中心  
  2. redis.host=10.75.202.11  
  3. redis.port=6379  
  4. redis.password=123456  
  5. redis.maxIdle=100  
  6. redis.maxActive=300  
  7. redis.maxWait=1000  
  8. redis.testOnBorrow=true  
  9. redis.timeout=100000  
  10.   
  11. # 不需要加入缓存的类  
  12. targetNames=xxxRecordManager,xxxSetRecordManager,xxxStatisticsIdentificationManager  
  13. # 不需要缓存的方法  
  14. methodNames=  
  15.   
  16. #设置缓存失效时间  
  17. com.service.impl.xxxRecordManager= 60  
  18. com.service.impl.xxxSetRecordManager= 60  
  19. defaultCacheExpireTime=3600  
  20.   
  21. fep.local.cache.capacity =10000  

要扫这些properties文件,在application.xml加入如下配置

 

 

[plain] view plain copy
 
  1.  <!-- 引入properties配置文件 -->    
  2.  <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  
  3.     <property name="locations">  
  4.         <list>  
  5.            <value>classpath:properties/*.properties</value>  
  6.             <!--要是有多个配置文件,只需在这里继续添加即可 -->  
  7.         </list>  
  8.     </property>  
  9. </bean>  

3、一些工具类

(1)RedisUtil

上面的bean中,RedisUtil是用来缓存和去除数据的实例

 

[java] view plain copy
 
  1. package com.mucfc.msm.common;  
  2.   
  3. import java.io.Serializable;  
  4. import java.util.Set;  
  5. import java.util.concurrent.TimeUnit;  
  6.   
  7. import org.apache.log4j.Logger;  
  8. import org.springframework.data.redis.core.RedisTemplate;  
  9. import org.springframework.data.redis.core.ValueOperations;  
  10.   
  11. /** 
  12.  * redis cache 工具类 
  13.  *  
  14.  */  
  15. public final class RedisUtil {  
  16.     private Logger logger = Logger.getLogger(RedisUtil.class);  
  17.     private RedisTemplate<Serializable, Object> redisTemplate;  
  18.   
  19.     /** 
  20.      * 批量删除对应的value 
  21.      *  
  22.      * @param keys 
  23.      */  
  24.     public void remove(final String... keys) {  
  25.         for (String key : keys) {  
  26.             remove(key);  
  27.         }  
  28.     }  
  29.   
  30.     /** 
  31.      * 批量删除key 
  32.      *  
  33.      * @param pattern 
  34.      */  
  35.     public void removePattern(final String pattern) {  
  36.         Set<Serializable> keys = redisTemplate.keys(pattern);  
  37.         if (keys.size() > 0)  
  38.             redisTemplate.delete(keys);  
  39.     }  
  40.   
  41.     /** 
  42.      * 删除对应的value 
  43.      *  
  44.      * @param key 
  45.      */  
  46.     public void remove(final String key) {  
  47.         if (exists(key)) {  
  48.             redisTemplate.delete(key);  
  49.         }  
  50.     }  
  51.   
  52.     /** 
  53.      * 判断缓存中是否有对应的value 
  54.      *  
  55.      * @param key 
  56.      * @return 
  57.      */  
  58.     public boolean exists(final String key) {  
  59.         return redisTemplate.hasKey(key);  
  60.     }  
  61.   
  62.     /** 
  63.      * 读取缓存 
  64.      *  
  65.      * @param key 
  66.      * @return 
  67.      */  
  68.     public Object get(final String key) {  
  69.         Object result = null;  
  70.         ValueOperations<Serializable, Object> operations = redisTemplate  
  71.                 .opsForValue();  
  72.         result = operations.get(key);  
  73.         return result;  
  74.     }  
  75.   
  76.     /** 
  77.      * 写入缓存 
  78.      *  
  79.      * @param key 
  80.      * @param value 
  81.      * @return 
  82.      */  
  83.     public boolean set(final String key, Object value) {  
  84.         boolean result = false;  
  85.         try {  
  86.             ValueOperations<Serializable, Object> operations = redisTemplate  
  87.                     .opsForValue();  
  88.             operations.set(key, value);  
  89.             result = true;  
  90.         } catch (Exception e) {  
  91.             e.printStackTrace();  
  92.         }  
  93.         return result;  
  94.     }  
  95.   
  96.     /** 
  97.      * 写入缓存 
  98.      *  
  99.      * @param key 
  100.      * @param value 
  101.      * @return 
  102.      */  
  103.     public boolean set(final String key, Object value, Long expireTime) {  
  104.         boolean result = false;  
  105.         try {  
  106.             ValueOperations<Serializable, Object> operations = redisTemplate  
  107.                     .opsForValue();  
  108.             operations.set(key, value);  
  109.             redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);  
  110.             result = true;  
  111.         } catch (Exception e) {  
  112.             e.printStackTrace();  
  113.         }  
  114.         return result;  
  115.     }  
  116.   
  117.     public void setRedisTemplate(  
  118.             RedisTemplate<Serializable, Object> redisTemplate) {  
  119.         this.redisTemplate = redisTemplate;  
  120.     }  
  121. }  
(2)MethodCacheInterceptor

 

切面MethodCacheInterceptor,这是用来给不同的方法来加入判断如果缓存存在数据,从缓存取数据。否则第一次从数据库取,并将结果保存到缓存 中去。

 

[java] view plain copy
 
  1. package com.mucfc.msm.common;  
  2.   
  3. import java.io.File;  
  4. import java.io.FileInputStream;  
  5. import java.io.InputStream;  
  6. import java.util.ArrayList;  
  7. import java.util.List;  
  8. import java.util.Properties;  
  9.   
  10. import org.aopalliance.intercept.MethodInterceptor;  
  11. import org.aopalliance.intercept.MethodInvocation;  
  12. import org.apache.log4j.Logger;  
  13.   
  14.   
  15. public class MethodCacheInterceptor implements MethodInterceptor {  
  16.     private Logger logger = Logger.getLogger(MethodCacheInterceptor.class);  
  17.     private RedisUtil redisUtil;  
  18.     private List<String> targetNamesList; // 不加入缓存的service名称  
  19.     private List<String> methodNamesList; // 不加入缓存的方法名称  
  20.     private Long defaultCacheExpireTime; // 缓存默认的过期时间  
  21.     private Long xxxRecordManagerTime; //  
  22.     private Long xxxSetRecordManagerTime; //  
  23.   
  24.     /** 
  25.      * 初始化读取不需要加入缓存的类名和方法名称 
  26.      */  
  27.     public MethodCacheInterceptor() {  
  28.         try {  
  29.              File f = new File("D:\\lunaJee-workspace\\msm\\msm_core\\src\\main\\java\\com\\mucfc\\msm\\common\\cacheConf.properties");   
  30.              //配置文件位置直接被写死,有需要自己修改下  
  31.              InputStream in = new FileInputStream(f);   
  32. //          InputStream in = getClass().getClassLoader().getResourceAsStream(  
  33. //                  "D:\\lunaJee-workspace\\msm\\msm_core\\src\\main\\java\\com\\mucfc\\msm\\common\\cacheConf.properties");  
  34.             Properties p = new Properties();  
  35.             p.load(in);  
  36.             // 分割字符串  
  37.             String[] targetNames = p.getProperty("targetNames").split(",");  
  38.             String[] methodNames = p.getProperty("methodNames").split(",");  
  39.   
  40.             // 加载过期时间设置  
  41.             defaultCacheExpireTime = Long.valueOf(p.getProperty("defaultCacheExpireTime"));  
  42.             xxxRecordManagerTime = Long.valueOf(p.getProperty("com.service.impl.xxxRecordManager"));  
  43.             xxxSetRecordManagerTime = Long.valueOf(p.getProperty("com.service.impl.xxxSetRecordManager"));  
  44.             // 创建list  
  45.             targetNamesList = new ArrayList<String>(targetNames.length);  
  46.             methodNamesList = new ArrayList<String>(methodNames.length);  
  47.             Integer maxLen = targetNames.length > methodNames.length ? targetNames.length  
  48.                     : methodNames.length;  
  49.             // 将不需要缓存的类名和方法名添加到list中  
  50.             for (int i = 0; i < maxLen; i++) {  
  51.                 if (i < targetNames.length) {  
  52.                     targetNamesList.add(targetNames[i]);  
  53.                 }  
  54.                 if (i < methodNames.length) {  
  55.                     methodNamesList.add(methodNames[i]);  
  56.                 }  
  57.             }  
  58.         } catch (Exception e) {  
  59.             e.printStackTrace();  
  60.         }  
  61.     }  
  62.   
  63.     @Override  
  64.     public Object invoke(MethodInvocation invocation) throws Throwable {  
  65.         Object value = null;  
  66.   
  67.         String targetName = invocation.getThis().getClass().getName();  
  68.         String methodName = invocation.getMethod().getName();  
  69.         // 不需要缓存的内容  
  70.         //if (!isAddCache(StringUtil.subStrForLastDot(targetName), methodName)) {  
  71.         if (!isAddCache(targetName, methodName)) {  
  72.             // 执行方法返回结果  
  73.             return invocation.proceed();  
  74.         }  
  75.         Object[] arguments = invocation.getArguments();  
  76.         String key = getCacheKey(targetName, methodName, arguments);  
  77.         System.out.println(key);  
  78.   
  79.         try {  
  80.             // 判断是否有缓存  
  81.             if (redisUtil.exists(key)) {  
  82.                 return redisUtil.get(key);  
  83.             }  
  84.             // 写入缓存  
  85.             value = invocation.proceed();  
  86.             if (value != null) {  
  87.                 final String tkey = key;  
  88.                 final Object tvalue = value;  
  89.                 new Thread(new Runnable() {  
  90.                     @Override  
  91.                     public void run() {  
  92.                         if (tkey.startsWith("com.service.impl.xxxRecordManager")) {  
  93.                             redisUtil.set(tkey, tvalue, xxxRecordManagerTime);  
  94.                         } else if (tkey.startsWith("com.service.impl.xxxSetRecordManager")) {  
  95.                             redisUtil.set(tkey, tvalue, xxxSetRecordManagerTime);  
  96.                         } else {  
  97.                             redisUtil.set(tkey, tvalue, defaultCacheExpireTime);  
  98.                         }  
  99.                     }  
  100.                 }).start();  
  101.             }  
  102.         } catch (Exception e) {  
  103.             e.printStackTrace();  
  104.             if (value == null) {  
  105.                 return invocation.proceed();  
  106.             }  
  107.         }  
  108.         return value;  
  109.     }  
  110.   
  111.     /** 
  112.      * 是否加入缓存 
  113.      *  
  114.      * @return 
  115.      */  
  116.     private boolean isAddCache(String targetName, String methodName) {  
  117.         boolean flag = true;  
  118.         if (targetNamesList.contains(targetName)  
  119.                 || methodNamesList.contains(methodName)) {  
  120.             flag = false;  
  121.         }  
  122.         return flag;  
  123.     }  
  124.   
  125.     /** 
  126.      * 创建缓存key 
  127.      * 
  128.      * @param targetName 
  129.      * @param methodName 
  130.      * @param arguments 
  131.      */  
  132.     private String getCacheKey(String targetName, String methodName,  
  133.             Object[] arguments) {  
  134.         StringBuffer sbu = new StringBuffer();  
  135.         sbu.append(targetName).append("_").append(methodName);  
  136.         if ((arguments != null) && (arguments.length != 0)) {  
  137.             for (int i = 0; i < arguments.length; i++) {  
  138.                 sbu.append("_").append(arguments[i]);  
  139.             }  
  140.         }  
  141.         return sbu.toString();  
  142.     }  
  143.   
  144.     public void setRedisUtil(RedisUtil redisUtil) {  
  145.         this.redisUtil = redisUtil;  
  146.     }  
  147. }  

4、配置需要缓存的类或方法

 

在application.xml加入如下配置,有多个类或方法可以配置多个

 

[html] view plain copy
 
  1. <!-- 需要加入缓存的类或方法 -->  
  2. <bean id="methodCachePointCut"  class="org.springframework.aop.support.RegexpMethodPointcutAdvisor" >  
  3.       <property name="advice" >  
  4.           <ref local="methodCacheInterceptor" />  
  5.       </property>  
  6.       <property name="patterns" >  
  7.           <list>  
  8.            <!-- 确定正则表达式列表 -->  
  9.              <value>com\.mucfc\.msm\.service\.impl\...*ServiceImpl.*</value >  
  10.           </list>  
  11.       </property>  
  12. </bean >  

5、执行结果:

 

写了一个简单的单元测试如下:

 

[java] view plain copy
 
  1. @Test  
  2. public void getSettUnitBySettUnitIdTest() {  
  3.     String systemId = "CES";  
  4.     String merchantId = "133";  
  5.     SettUnit configSettUnit = settUnitService.getSettUnitBySettUnitId(systemId, merchantId, "ESP");  
  6.     SettUnit configSettUnit1 = settUnitService.getSettUnitBySettUnitId(systemId, merchantId, "ESP");  
  7.     boolean flag= (configSettUnit == configSettUnit1);  
  8.     System.out.println(configSettUnit);  
  9.     logger.info("查找结果" + configSettUnit.getBusinessType());  
  10.     
  11.   //  localSecondFIFOCache.put("configSettUnit", configSettUnit.getBusinessType());  
  12.  //  String string = localSecondFIFOCache.get("configSettUnit");  
  13.       logger.info("查找结果" + string);  
  14. }  
这是第一次执行单元测试的过程:

 

MethodCacheInterceptor这个类中打了断点,然后每次查询前都会先进入这个方法

 


 

依次运行,发现没有缓存,所以会直接去查数据库

打印了出来的SQL语句:

第二次执行:

因为第一次执行时,已经写入缓存了。所以第二次直接从缓存中取数据

3、取两次的结果进行地址的对比:

发现两个不是同一个对象,没错,是对的。如果是使用ehcache的话,那么二者的内存地址会是一样的。那是因为redis和ehcache使用的缓存机制是不一样的。ehcache是基于本地电脑的内存使用缓存,所以使用缓存取数据时直接在本地电脑上取。转换成java对象就会是同一个内存地址,而redis它是在装有redis服务的电脑上(一般是另一台电脑),所以取数据时经过传输到本地,会对应到不同的内存地址,所以用==来比较会返回false。但是它确实是从缓存中去取的,这点我们从上面的断点可以看到。

http://blog.csdn.net/evankaka/article/details/50396325

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
[转]注释驱动的 Spring cache 缓存介绍
原文:http://www.ibm.com/developerworks/cn/opensource/os-cn-spring-cache/ 概述 Spring 3.1 引入了激动人心的基于注释(annotation)的缓存(cache)技术,它本质上不是一个具体的缓存实现方案(例如 EHCache 或者 OSCache),而是一个对缓存使用的抽象,通过在既有代码中添加少量它定义的各种 annotation,即能够达到缓存方法的返回对象的效果。
1055 0
Zuul整合Swagger,使用ZuulFilter解决下游服务context-path
问题起因:使用Zuul网关服务,需要整合下游系统的swagger,但是下游服务存在context-path配置,无法正确跳转,最后使用ZuulFilter解决。 1.Zuul整合下游swagger 首先介绍一下Zuul如何整合下游服务swagger,很好理解,就是通过Zuul的swagger地址,实现将下游服务的swagger都放入同一个页面内,流转图如下: 1.
371 0
MVC3缓存之一:使用页面缓存
在以前的WebForm的开发中,在页面的头部加上OutputCache即可启用页面缓存,而在MVC3中,使用了Razor模板引擎的话,该如何使用页面缓存呢? 如何启用 在MVC3中要如果要启用页面缓存,在页面对应的Action前面加上一个OutputCache属性即可。
586 0
SpringMVC的JSON处理及FastJSON的整合使用(七)上
SpringMVC的JSON处理及FastJSON的整合使用(七)
32 0
要点摘炼Redis篇|JavaEE互联网轻量级框架整合开发
学习JavaEE互联网轻量级框架整合开发此书,了解redis这一NoSQL技术,在此做点基础知识简单记录
2196 0
redis整合Spring集群搭建及业务中的使用
1.redis安装   Redis是c语言开发的。   安装redis需要c语言的编译环境。如果没有gcc需要在线安装。yum install gcc-c++   安装步骤:   第一步:redis的源码包上传到linux系统。
1122 0
+关注
developerguy
A code cleanliness code farmers, A programmer concentrate on elegant design, A want to do with the product architect
1663
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
OceanBase 入门到实战教程
立即下载
阿里云图数据库GDB,加速开启“图智”未来.ppt
立即下载
实时数仓Hologres技术实战一本通2.0版(下)
立即下载