林炳文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包

  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加入如下配置

  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. <!-- cache配置 -->
  25. <bean id="methodCacheInterceptor" class="com.mucfc.msm.common.MethodCacheInterceptor" >
  26. <property name="redisUtil" ref="redisUtil" />
  27. </bean >
  28. <bean id="redisUtil" class="com.mucfc.msm.common.RedisUtil" >
  29. <property name="redisTemplate" ref="redisTemplate" />
  30. </bean >

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

  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. targetNames=xxxRecordManager,xxxSetRecordManager,xxxStatisticsIdentificationManager
  12. # 不需要缓存的方法
  13. methodNames=
  14. #设置缓存失效时间
  15. com.service.impl.xxxRecordManager= 60
  16. com.service.impl.xxxSetRecordManager= 60
  17. defaultCacheExpireTime=3600
  18. fep.local.cache.capacity =10000

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

  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是用来缓存和去除数据的实例

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

(2)MethodCacheInterceptor

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

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

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

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

  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、执行结果:

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

  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. //  localSecondFIFOCache.put("configSettUnit", configSettUnit.getBusinessType());
  11. //  String string = localSecondFIFOCache.get("configSettUnit");
  12. logger.info("查找结果" + string);
  13. }

这是第一次执行单元测试的过程:

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

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

打印了出来的SQL语句:

第二次执行:

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

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

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

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

Redis整合Spring结合使用缓存实例(转)的更多相关文章

  1. Redis整合Spring结合使用缓存实例

    林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 摘要:本文介绍了如何在Spring中配置redis,并通过Spring中AOP的思想,将缓存的 ...

  2. Redis学习总结(3)——Redis整合Spring结合使用缓存实例

    摘要:本文介绍了如何在Spring中配置redis,并通过Spring中AOP的思想,将缓存的方法切入到有需要进入缓存的类或方法前面. 一.Redis介绍 什么是Redis? redis是一个key- ...

  3. Redis整合Spring结合使用缓存实例(三)

    一.Redis介绍 什么是Redis? redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set( ...

  4. Redis windows环境安装 以及 redis整合spring

    Redis对于Linux是官方支持的,安装和使用没有什么好说的,普通使用按照官方指导,5分钟以内就能搞定.详情请参考: http://redis.io/download Redis官方是不支持wind ...

  5. 分布式数据存储 之 Redis(二) —— spring中的缓存抽象

    分布式数据存储 之 Redis(二) -- spring中的缓存抽象 一.spring boot 中的 StringRedisTemplate 1.StringRedisTemplate Demo 第 ...

  6. Redis整合spring总结

    一:Redis简介: Redis是一个开源(BSD许可)的内存数据结构存储,用作数据库,缓存和消息代理. 简单来说,它是一个以(key,value)的形式存储数据的数据库. 官网:https://re ...

  7. redis整合Spring入门

    首先 衷心感谢这篇博客给我入门时的启发  三颗心脏 你需要知道,spring的官方文档中已经注明,与redis整合时,spring的jar包版本不能低于4.2.6,否则不支持,会报错的哟 测试的时候请 ...

  8. Redis整合Spring实现缓存

    一.Redis介绍 什么是Redis? redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set( ...

  9. Redis整合Spring实现分布式锁

    spring把专门的数据操作独立封装在spring-data系列中,spring-data-redis是对Redis的封装 <dependencies> <!-- 添加spring- ...

随机推荐

  1. fck编辑器的使用

    FCK编辑器的使用 注意:编辑器有浏览器缓存,所以修改配置后,一定要删一下缓存 这个编辑器是采用 html+javascript 开发出来的 通常作为插件来使用: 1,下载插件包 2,解压,加压之后看 ...

  2. Eclipse Tips

    一.取消拼写检查 Window -> Preferences -> General -> Editors -> Text Editors -> Spelling -> ...

  3. Java并发编程--Fork/Join框架使用

    上篇博客我们介绍了通过CyclicBarrier使线程同步,可是上述方法存在一个问题,那就是假设一个大任务跑了2个线程去完毕.假设线程2耗时比线程1多2倍.线程1完毕后必须等待线程2完毕.等待的过程线 ...

  4. Virtualbox mouse move in and out and file share with windows

    How to use Virstalbox to share files with Linux and Windows, and to move the mouse in and out Virtua ...

  5. VSTO学习笔记(四)从SharePoint 2010中下载文件

    原文:VSTO学习笔记(四)从SharePoint 2010中下载文件 上一次我们开发了一个简单的64位COM加载项,虽然功能很简单,但是包括了开发一个64位COM加载项的大部分过程.本次我们来给CO ...

  6. jquery.ui.accordion的修改(支持展开多个)

    原文:jquery.ui.accordion的修改(支持展开多个) 背景:原jquery.ui.accordion插件,最多只能展开一个,不能展开多个,后来在网上找到了一个基于它的一个修改版(http ...

  7. Struts2 后台action接收 jsp页面中checkbox中的值

    如前端页面jsp中的标签为: <form action="myurl"> <input type="checkbox" name=" ...

  8. SE 2014年4月5日

    背景需求: 缺省情况下,Level-1路由器只将去往其它区域的报文发送到最近的Level-1-2路由器. 路由渗透使Level-1-2路由器将Level-2区域的路由信息发布到Level-1区域. 4 ...

  9. linux中怎样设置DHCP

    linux怎样设置DHCP 环境:RH linux 9.0 使用linux下经常使用的dhcpd包. 最新版本号 dhcp3.0.5 下载地址: 下载 1.安装: 先拷贝dhcp-3.0.5.tar. ...

  10. B桥接模式ridge

    1.一个简短的引论 1)模式概述:将抽象部分与实现部分分离.使它们都能够独立的变化.让抽象类和派生类各自实现自己的对象.当一个系统有多维度的变化时,将各个维度分离出来让它们独立于变化(多角度地分类实现 ...