重庆网站建设的目的,wordpress 首页调用页面,做网站开创和中企动力哪家强,网站开发工程师专业林炳文Evankaka原创作品。转载请注明出处http://blog.csdn.net/evankaka 摘要#xff1a;本文介绍了如何在Spring中配置redis#xff0c;并通过Spring中AOP的思想#xff0c;将缓存的方法切入到有需要进入缓存的类或方法前面。 一、Redis介绍 什么是Redis#xff1f; redis… 林炳文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(主从)同步。 它有什么特点 1Redis数据库完全在内存中使用磁盘仅用于持久性。2相比许多键值数据存储Redis拥有一套较为丰富的数据类型。3Redis可以将数据复制到任意数量的从服务器。 Redis 优势 1异常快速Redis的速度非常快每秒能执行约11万集合每秒约81000条记录。 2支持丰富的数据类型Redis支持最大多数开发人员已经知道像列表集合有序集合散列数据类型。这使得它非常容易解决各种各样的问题因为我们知道哪些问题是可以处理通过它的数据类型更好。3操作都是原子性所有Redis操作是原子的这保证了如果两个客户端同时访问的Redis服务器将获得更新后的值。4多功能实用工具Redis是一个多实用的工具可以在多个用例如缓存消息队列使用(Redis原生支持发布/订阅)任何短暂的数据应用程序如Web应用程序会话网页命中计数等。 Redis 缺点 1单线程 2耗内存 二、使用实例 本文使用maveneclipsesping 1、引入jar包 [html] view plaincopy !--Redis start -- dependency groupIdorg.springframework.data/groupId artifactIdspring-data-redis/artifactId version1.6.1.RELEASE/version /dependency dependency groupIdredis.clients/groupId artifactIdjedis/artifactId version2.7.3/version /dependency !--Redis end -- 2、配置bean 在application.xml加入如下配置 [html] view plaincopy !-- jedis 配置 -- bean idpoolConfig classredis.clients.jedis.JedisPoolConfig property namemaxIdle value${redis.maxIdle} / property namemaxWaitMillis value${redis.maxWait} / property nametestOnBorrow value${redis.testOnBorrow} / /bean !-- redis服务器中心 -- bean idconnectionFactory classorg.springframework.data.redis.connection.jedis.JedisConnectionFactory property namepoolConfig refpoolConfig / property nameport value${redis.port} / property namehostName value${redis.host} / property namepassword value${redis.password} / property nametimeout value${redis.timeout} /property /bean bean idredisTemplate classorg.springframework.data.redis.core.RedisTemplate property nameconnectionFactory refconnectionFactory / property namekeySerializer bean classorg.springframework.data.redis.serializer.StringRedisSerializer / /property property namevalueSerializer bean classorg.springframework.data.redis.serializer.JdkSerializationRedisSerializer / /property /bean !-- cache配置 -- bean idmethodCacheInterceptor classcom.mucfc.msm.common.MethodCacheInterceptor property nameredisUtil refredisUtil / /bean bean idredisUtil classcom.mucfc.msm.common.RedisUtil property nameredisTemplate refredisTemplate / /bean 其中配置文件redis一些配置数据redis.properties如下 [plain] view plaincopy #redis中心 redis.host10.75.202.11 redis.port6379 redis.password123456 redis.maxIdle100 redis.maxActive300 redis.maxWait1000 redis.testOnBorrowtrue redis.timeout100000 # 不需要加入缓存的类 targetNamesxxxRecordManager,xxxSetRecordManager,xxxStatisticsIdentificationManager # 不需要缓存的方法 methodNames #设置缓存失效时间 com.service.impl.xxxRecordManager 60 com.service.impl.xxxSetRecordManager 60 defaultCacheExpireTime3600 fep.local.cache.capacity 10000 要扫这些properties文件在application.xml加入如下配置 [plain] view plaincopy !-- 引入properties配置文件 -- bean idpropertyConfigurer classorg.springframework.beans.factory.config.PropertyPlaceholderConfigurer property namelocations list valueclasspath:properties/*.properties/value !--要是有多个配置文件只需在这里继续添加即可 -- /list /property /bean 3、一些工具类 1RedisUtil 上面的bean中RedisUtil是用来缓存和去除数据的实例 [java] view plaincopy package com.mucfc.msm.common; import java.io.Serializable; import java.util.Set; import java.util.concurrent.TimeUnit; import org.apache.log4j.Logger; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; /** * redis cache 工具类 * */ public final class RedisUtil { private Logger logger Logger.getLogger(RedisUtil.class); private RedisTemplateSerializable, Object redisTemplate; /** * 批量删除对应的value * * param keys */ public void remove(final String... keys) { for (String key : keys) { remove(key); } } /** * 批量删除key * * param pattern */ public void removePattern(final String pattern) { SetSerializable keys redisTemplate.keys(pattern); if (keys.size() 0) redisTemplate.delete(keys); } /** * 删除对应的value * * param key */ public void remove(final String key) { if (exists(key)) { redisTemplate.delete(key); } } /** * 判断缓存中是否有对应的value * * param key * return */ public boolean exists(final String key) { return redisTemplate.hasKey(key); } /** * 读取缓存 * * param key * return */ public Object get(final String key) { Object result null; ValueOperationsSerializable, Object operations redisTemplate .opsForValue(); result operations.get(key); return result; } /** * 写入缓存 * * param key * param value * return */ public boolean set(final String key, Object value) { boolean result false; try { ValueOperationsSerializable, Object operations redisTemplate .opsForValue(); operations.set(key, value); result true; } catch (Exception e) { e.printStackTrace(); } return result; } /** * 写入缓存 * * param key * param value * return */ public boolean set(final String key, Object value, Long expireTime) { boolean result false; try { ValueOperationsSerializable, Object operations redisTemplate .opsForValue(); operations.set(key, value); redisTemplate.expire(key, expireTime, TimeUnit.SECONDS); result true; } catch (Exception e) { e.printStackTrace(); } return result; } public void setRedisTemplate( RedisTemplateSerializable, Object redisTemplate) { this.redisTemplate redisTemplate; } } 2MethodCacheInterceptor 切面MethodCacheInterceptor这是用来给不同的方法来加入判断如果缓存存在数据从缓存取数据。否则第一次从数据库取并将结果保存到缓存 中去。 [java] view plaincopy package com.mucfc.msm.common; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.Properties; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.apache.log4j.Logger; public class MethodCacheInterceptor implements MethodInterceptor { private Logger logger Logger.getLogger(MethodCacheInterceptor.class); private RedisUtil redisUtil; private ListString targetNamesList; // 不加入缓存的service名称 private ListString methodNamesList; // 不加入缓存的方法名称 private Long defaultCacheExpireTime; // 缓存默认的过期时间 private Long xxxRecordManagerTime; // private Long xxxSetRecordManagerTime; // /** * 初始化读取不需要加入缓存的类名和方法名称 */ public MethodCacheInterceptor() { try { File f new File(D:\\lunaJee-workspace\\msm\\msm_core\\src\\main\\java\\com\\mucfc\\msm\\common\\cacheConf.properties); //配置文件位置直接被写死有需要自己修改下 InputStream in new FileInputStream(f); // InputStream in getClass().getClassLoader().getResourceAsStream( // D:\\lunaJee-workspace\\msm\\msm_core\\src\\main\\java\\com\\mucfc\\msm\\common\\cacheConf.properties); Properties p new Properties(); p.load(in); // 分割字符串 String[] targetNames p.getProperty(targetNames).split(,); String[] methodNames p.getProperty(methodNames).split(,); // 加载过期时间设置 defaultCacheExpireTime Long.valueOf(p.getProperty(defaultCacheExpireTime)); xxxRecordManagerTime Long.valueOf(p.getProperty(com.service.impl.xxxRecordManager)); xxxSetRecordManagerTime Long.valueOf(p.getProperty(com.service.impl.xxxSetRecordManager)); // 创建list targetNamesList new ArrayListString(targetNames.length); methodNamesList new ArrayListString(methodNames.length); Integer maxLen targetNames.length methodNames.length ? targetNames.length : methodNames.length; // 将不需要缓存的类名和方法名添加到list中 for (int i 0; i maxLen; i) { if (i targetNames.length) { targetNamesList.add(targetNames[i]); } if (i methodNames.length) { methodNamesList.add(methodNames[i]); } } } catch (Exception e) { e.printStackTrace(); } } Override public Object invoke(MethodInvocation invocation) throws Throwable { Object value null; String targetName invocation.getThis().getClass().getName(); String methodName invocation.getMethod().getName(); // 不需要缓存的内容 //if (!isAddCache(StringUtil.subStrForLastDot(targetName), methodName)) { if (!isAddCache(targetName, methodName)) { // 执行方法返回结果 return invocation.proceed(); } Object[] arguments invocation.getArguments(); String key getCacheKey(targetName, methodName, arguments); System.out.println(key); try { // 判断是否有缓存 if (redisUtil.exists(key)) { return redisUtil.get(key); } // 写入缓存 value invocation.proceed(); if (value ! null) { final String tkey key; final Object tvalue value; new Thread(new Runnable() { Override public void run() { if (tkey.startsWith(com.service.impl.xxxRecordManager)) { redisUtil.set(tkey, tvalue, xxxRecordManagerTime); } else if (tkey.startsWith(com.service.impl.xxxSetRecordManager)) { redisUtil.set(tkey, tvalue, xxxSetRecordManagerTime); } else { redisUtil.set(tkey, tvalue, defaultCacheExpireTime); } } }).start(); } } catch (Exception e) { e.printStackTrace(); if (value null) { return invocation.proceed(); } } return value; } /** * 是否加入缓存 * * return */ private boolean isAddCache(String targetName, String methodName) { boolean flag true; if (targetNamesList.contains(targetName) || methodNamesList.contains(methodName)) { flag false; } return flag; } /** * 创建缓存key * * param targetName * param methodName * param arguments */ private String getCacheKey(String targetName, String methodName, Object[] arguments) { StringBuffer sbu new StringBuffer(); sbu.append(targetName).append(_).append(methodName); if ((arguments ! null) (arguments.length ! 0)) { for (int i 0; i arguments.length; i) { sbu.append(_).append(arguments[i]); } } return sbu.toString(); } public void setRedisUtil(RedisUtil redisUtil) { this.redisUtil redisUtil; } } 4、配置需要缓存的类或方法 在application.xml加入如下配置有多个类或方法可以配置多个 [html] view plaincopy !-- 需要加入缓存的类或方法 -- bean idmethodCachePointCut classorg.springframework.aop.support.RegexpMethodPointcutAdvisor property nameadvice ref localmethodCacheInterceptor / /property property namepatterns list !-- 确定正则表达式列表 -- valuecom\.mucfc\.msm\.service\.impl\...*ServiceImpl.*/value /list /property /bean 5、执行结果: 写了一个简单的单元测试如下 [java] view plaincopy Test public void getSettUnitBySettUnitIdTest() { String systemId CES; String merchantId 133; SettUnit configSettUnit settUnitService.getSettUnitBySettUnitId(systemId, merchantId, ESP); SettUnit configSettUnit1 settUnitService.getSettUnitBySettUnitId(systemId, merchantId, ESP); boolean flag (configSettUnit configSettUnit1); System.out.println(configSettUnit); logger.info(查找结果 configSettUnit.getBusinessType()); // localSecondFIFOCache.put(configSettUnit, configSettUnit.getBusinessType()); // String string localSecondFIFOCache.get(configSettUnit); logger.info(查找结果 string); } 这是第一次执行单元测试的过程 MethodCacheInterceptor这个类中打了断点然后每次查询前都会先进入这个方法 依次运行发现没有缓存所以会直接去查数据库 打印了出来的SQL语句 第二次执行 因为第一次执行时已经写入缓存了。所以第二次直接从缓存中取数据 3、取两次的结果进行地址的对比 发现两个不是同一个对象没错是对的。如果是使用ehcache的话那么二者的内存地址会是一样的。那是因为redis和ehcache使用的缓存机制是不一样的。ehcache是基于本地电脑的内存使用缓存所以使用缓存取数据时直接在本地电脑上取。转换成java对象就会是同一个内存地址而redis它是在装有redis服务的电脑上一般是另一台电脑所以取数据时经过传输到本地会对应到不同的内存地址所以用来比较会返回false。但是它确实是从缓存中去取的这点我们从上面的断点可以看到。