生活随笔
收集整理的這篇文章主要介紹了
Redis整合Spring结合使用缓存实例
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
2019獨角獸企業重金招聘Python工程師標準>>>
摘要:本文介紹了如何在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
????<!--Redis?start?-->?? <dependency>?? ????<groupId>org.springframework.data</groupId>?? ????<artifactId>spring-data-redis</artifactId>?? ????<version>1.6.1.RELEASE</version>?? </dependency>?? <dependency>?? ????<groupId>redis.clients</groupId>?? ????<artifactId>jedis</artifactId>?? ????<version>2.7.3</version>?? </dependency>?? ???<!--Redis?end?-->??
2、配置bean
在application.xml加入如下配置
[html]? view plain copy
<!--?jedis?配置?-->?? ???<bean?id="poolConfig"?class="redis.clients.jedis.JedisPoolConfig"?>?? ?????????<property?name="maxIdle"?value="${redis.maxIdle}"?/>?? ?????????<property?name="maxWaitMillis"?value="${redis.maxWait}"?/>?? ?????????<property?name="testOnBorrow"?value="${redis.testOnBorrow}"?/>?? ???</bean?>?? ??<!--?redis服務器中心?-->?? ???<bean?id="connectionFactory"??class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"?>?? ?????????<property?name="poolConfig"?ref="poolConfig"?/>?? ?????????<property?name="port"?value="${redis.port}"?/>?? ?????????<property?name="hostName"?value="${redis.host}"?/>?? ?????????<property?name="password"?value="${redis.password}"?/>?? ?????????<property?name="timeout"?value="${redis.timeout}"?></property>?? ???</bean?>?? ???<bean?id="redisTemplate"?class="org.springframework.data.redis.core.RedisTemplate"?>?? ?????????<property?name="connectionFactory"?ref="connectionFactory"?/>?? ?????????<property?name="keySerializer"?>?? ?????????????<bean?class="org.springframework.data.redis.serializer.StringRedisSerializer"?/>?? ?????????</property>?? ?????????<property?name="valueSerializer"?>?? ?????????????<bean?class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"?/>?? ?????????</property>?? ???</bean?>?? ????? ????<!--?cache配置?-->?? ???<bean?id="methodCacheInterceptor"?class="com.mucfc.msm.common.MethodCacheInterceptor"?>?? ?????????<property?name="redisUtil"?ref="redisUtil"?/>?? ???</bean?>?? ???<bean?id="redisUtil"?class="com.mucfc.msm.common.RedisUtil"?>?? ?????????<property?name="redisTemplate"?ref="redisTemplate"?/>?? ???</bean?>?? 其中配置文件redis一些配置數據redis.properties如下:
[plain]? view plain copy
#redis中心?? redis.host=10.75.202.11?? redis.port=6379?? redis.password=123456?? redis.maxIdle=100?? redis.maxActive=300?? redis.maxWait=1000?? redis.testOnBorrow=true?? redis.timeout=100000?? ?? #?不需要加入緩存的類?? targetNames=xxxRecordManager,xxxSetRecordManager,xxxStatisticsIdentificationManager?? #?不需要緩存的方法?? methodNames=?? ?? #設置緩存失效時間?? com.service.impl.xxxRecordManager=?60?? com.service.impl.xxxSetRecordManager=?60?? defaultCacheExpireTime=3600?? ?? fep.local.cache.capacity?=10000?? 要掃這些properties文件,在application.xml加入如下配置
[plain]? view plain copy
?<!--?引入properties配置文件?-->???? ?<bean?id="propertyConfigurer"?class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">?? ????<property?name="locations">?? ????????<list>?? ???????????<value>classpath:properties/*.properties</value>?? ????????????<!--要是有多個配置文件,只需在這里繼續添加即可?-->?? ????????</list>?? ????</property>?? </bean>?? 3、一些工具類
(1)RedisUtil
上面的bean中,RedisUtil是用來緩存和去除數據的實例
[java]? view plain copy
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?RedisTemplate<Serializable,?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)?{?? ????????Set<Serializable>?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;?? ????????ValueOperations<Serializable,?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?{?? ????????????ValueOperations<Serializable,?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?{?? ????????????ValueOperations<Serializable,?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(?? ????????????RedisTemplate<Serializable,?Object>?redisTemplate)?{?? ????????this.redisTemplate?=?redisTemplate;?? ????}?? }?? (2)MethodCacheInterceptor
切面MethodCacheInterceptor,這是用來給不同的方法來加入判斷如果緩存存在數據,從緩存取數據。否則第一次從數據庫取,并將結果保存到緩存 中去。
[java]? view plain copy
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?List<String>?targetNamesList;?//?不加入緩存的service名稱?? ????private?List<String>?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?ArrayList<String>(targetNames.length);?? ????????????methodNamesList?=?new?ArrayList<String>(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 plain copy
<!--?需要加入緩存的類或方法?-->?? <bean?id="methodCachePointCut"??class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"?>?? ??????<property?name="advice"?>?? ??????????<ref?local="methodCacheInterceptor"?/>?? ??????</property>?? ??????<property?name="patterns"?>?? ??????????<list>?? ???????????<!--?確定正則表達式列表?-->?? ?????????????<value>com\.mucfc\.msm\.service\.impl\...*ServiceImpl.*</value?>?? ??????????</list>?? ??????</property>?? </bean?>?? 5、執行結果:
寫了一個簡單的單元測試如下:
[java]? view plain copy
@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。但是它確實是從緩存中去取的,這點我們從上面的斷點可以看到。
轉載于:https://my.oschina.net/zhanghaiyang/blog/592542
總結
以上是生活随笔為你收集整理的Redis整合Spring结合使用缓存实例的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。