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

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介:          林炳文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

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
25天前
|
存储 XML 缓存
【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache功能的开发实战指南(一)
【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache功能的开发实战指南
57 0
|
8天前
|
缓存 NoSQL Java
使用Redis进行Java缓存策略设计
【4月更文挑战第16天】在高并发Java应用中,Redis作为缓存中间件提升性能。本文探讨如何使用Redis设计缓存策略。Redis是开源内存数据结构存储系统,支持多种数据结构。Java中常用Redis客户端有Jedis和Lettuce。缓存设计遵循一致性、失效、雪崩、穿透和预热原则。常见缓存模式包括Cache-Aside、Read-Through、Write-Through和Write-Behind。示例展示了使用Jedis实现Cache-Aside模式。优化策略包括分布式锁、缓存预热、随机过期时间、限流和降级,以应对缓存挑战。
|
15天前
|
存储 缓存 NoSQL
使用redis进行缓存加速
使用redis进行缓存加速
26 0
|
16天前
|
存储 缓存 NoSQL
Java手撸一个缓存类似Redis
`LocalExpiringCache`是Java实现的一个本地缓存类,使用ConcurrentHashMap存储键值对,并通过ScheduledExecutorService定时清理过期的缓存项。类中包含`put`、`get`、`remove`等方法操作缓存,并有`clearCache`方法来清除过期的缓存条目。初始化时,会注册一个定时任务,每500毫秒检查并清理一次过期缓存。单例模式确保了类的唯一实例。
13 0
|
1月前
|
缓存 NoSQL Java
spring cache整合redis实现springboot项目中的缓存功能
spring cache整合redis实现springboot项目中的缓存功能
45 1
|
1月前
|
存储 缓存 NoSQL
[Redis]——缓存击穿和缓存穿透及解决方案(图解+代码+解释)
[Redis]——缓存击穿和缓存穿透及解决方案(图解+代码+解释)
143 0
|
1月前
|
缓存 NoSQL 数据库
[Redis]——数据一致性,先操作数据库,还是先更新缓存?
[Redis]——数据一致性,先操作数据库,还是先更新缓存?
|
1月前
|
存储 NoSQL Java
[Redis]——Spring整合Redis(SpringDataRedis)
[Redis]——Spring整合Redis(SpringDataRedis)
|
NoSQL Java 数据库
|
Java Spring 数据格式
spring 整合redis
用的是最新的jedis-2.6.2.jar这个包,这个和以前的有点不同。还需要添加spring-data-redis-1.2.1.RELEASE.jar和commons-pool2-2.3.jar。 在类路径下创建spring-redis-config.
923 0