配色: 字号:
Redis整合Spring结合使用缓存实例
2017-03-31 | 阅:  转:  |  分享 
  
Redis整合Spring结合使用缓存实例摘要:本文介绍了如何在Spring中配置redis,并通过Spring中AOP的思想,将缓存的方法
切入到有需要进入缓存的类或方法前面。一、Redis介绍什么是Redis?redis是一个key-value存储系统。和Memcac
hed类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorte
dset–有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富
的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓
存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-s
lave(主从)同步。它有什么特点?(1)Redis数据库完全在内存中,使用磁盘仅用于持久性。(2)相比许多键值数据存储,Redi
s拥有一套较为丰富的数据类型。(3)Redis可以将数据复制到任意数量的从服务器。Redis优势?(1)异常快速:Redis的速
度非常快,每秒能执行约11万集合,每秒约81000+条记录。(2)支持丰富的数据类型:Redis支持最大多数开发人员已经知道像列表
,集合,有序集合,散列数据类型。这使得它非常容易解决各种各样的问题,因为我们知道哪些问题是可以处理通过它的数据类型更好。(3)操作
都是原子性:所有Redis操作是原子的,这保证了如果两个客户端同时访问的Redis服务器将获得更新后的值。(4)多功能实用工具:R
edis是一个多实用的工具,可以在多个用例如缓存,消息,队列使用(Redis原生支持发布/订阅),任何短暂的数据,应用程序,如We
b应用程序会话,网页命中计数等。Redis缺点?(1)单线程(2)耗内存二、使用实例本文使用maven+eclipse+spin
g1、引入jar包org.springframe
work.data
spring-data-redission>1.6.1.RELEASE
red
is.clients
jedis2.7.3<
/version>
2、配置bean在application.xml加
入如下配置s.jedis.JedisPoolConfig">s.maxIdle}"/>ait}"/>w}"/>class="org.springframework.data.redis.connection.jedis.JedisConn
ectionFactory">
stName"value="${redis.host}"/>"${redis.password}"/>meout}">.springframework.data.redis.core.RedisTemplate">"connectionFactory"ref="connectionFactory"/>ySerializer">izer.StringRedisSerializer"/>erializer">er.JdkSerializationRedisSerializer"/>m.common.MethodCacheInterceptor">="redisUtil"/>m.common.RedisUtil">mplate"/>其中配置文件redis一些配置数据redis.properties如下:#redis中心re
dis.host=10.75.202.11redis.port=6379redis.password=123456redis.ma
xIdle=100redis.maxActive=300redis.maxWait=1000redis.testOnBorrow=
trueredis.timeout=100000#不需要加入缓存的类targetNames=xxxRecordManager,x
xxSetRecordManager,xxxStatisticsIdentificationManager#不需要缓存的方法me
thodNames=#设置缓存失效时间com.service.impl.xxxRecordManager=60com.servi
ce.impl.xxxSetRecordManager=60defaultCacheExpireTime=3600fep.loc
al.cache.capacity=10000要扫这些properties文件,在application.xml加入如下配置<
!--引入properties配置文件-->g.springframework.beans.factory.config.PropertyPlaceholderConfigu
rer">classpath:propert
ies/.properties
property>3、一些工具类(1)RedisUtil上面的bean中,RedisUtil是用来缓存和去除数据的
实例packagecom.mucfc.msm.common;importjava.io.Serializable;import
java.util.Set;importjava.util.concurrent.TimeUnit;importorg.ap
ache.log4j.Logger;importorg.springframework.data.redis.core.Redi
sTemplate;importorg.springframework.data.redis.core.ValueOperati
ons;/rediscache工具类/publicfinalclassRedisUtil{priv
ateLoggerlogger=Logger.getLogger(RedisUtil.class);privateRed
isTemplateredisTemplate;/批量删除对应的value
@paramkeys/publicvoidremove(finalString...keys){for
(Stringkey:keys){remove(key);}}/批量删除key@parampat
tern/publicvoidremovePattern(finalStringpattern){Setlizable>keys=redisTemplate.keys(pattern);if(keys.size()>0)r
edisTemplate.delete(keys);}/删除对应的value@paramkey/pub
licvoidremove(finalStringkey){if(exists(key)){redisTemplat
e.delete(key);}}/判断缓存中是否有对应的value@paramkey@return
/publicbooleanexists(finalStringkey){returnredisTemplate.h
asKey(key);}/读取缓存@paramkey@return/publicObject
get(finalStringkey){Objectresult=null;ValueOperationslizable,Object>operations=redisTemplate.opsForValue();result
=operations.get(key);returnresult;}/写入缓存@paramkey
@paramvalue@return/publicbooleanset(finalStringkey,Ob
jectvalue){booleanresult=false;try{ValueOperationsable,Object>operations=redisTemplate.opsForValue();operations
.set(key,value);result=true;}catch(Exceptione){e.printStac
kTrace();}returnresult;}/写入缓存@paramkey@paramvalu
e@return/publicbooleanset(finalStringkey,Objectvalue,
LongexpireTime){booleanresult=false;try{ValueOperationsializable,Object>operations=redisTemplate.opsForValue();opera
tions.set(key,value);redisTemplate.expire(key,expireTime,TimeU
nit.SECONDS);result=true;}catch(Exceptione){e.printStackTra
ce();}returnresult;}publicvoidsetRedisTemplate(RedisTemplateerializable,Object>redisTemplate){this.redisTemplate=redisTe
mplate;}}(2)MethodCacheInterceptor切面MethodCacheInterceptor,这是用来给不
同的方法来加入判断如果缓存存在数据,从缓存取数据。否则第一次从数据库取,并将结果保存到缓存中去。packagecom.mucf
c.msm.common;importjava.io.File;importjava.io.FileInputStream;i
mportjava.io.InputStream;importjava.util.ArrayList;importjava.
util.List;importjava.util.Properties;importorg.aopalliance.inte
rcept.MethodInterceptor;importorg.aopalliance.intercept.MethodIn
vocation;importorg.apache.log4j.Logger;publicclassMethodCacheI
nterceptorimplementsMethodInterceptor{privateLoggerlogger=
Logger.getLogger(MethodCacheInterceptor.class);privateRedisUtil
redisUtil;privateListtargetNamesList;//不加入缓存的service名
称privateListmethodNamesList;//不加入缓存的方法名称privateLong
defaultCacheExpireTime;//缓存默认的过期时间privateLongxxxRecordManager
Time;//privateLongxxxSetRecordManagerTime;///初始化读取不需要加入缓
存的类名和方法名称/publicMethodCacheInterceptor(){try{Filef=newF
ile("D:\\lunaJee-workspace\\msm\\msm_core\\src\\main\\java\\com\\
mucfc\\msm\\common\\cacheConf.properties");//配置文件位置直接被写死,有需要自己修
改下InputStreamin=newFileInputStream(f);//InputStreamin=ge
tClass().getClassLoader().getResourceAsStream(//"D:\\lunaJee-work
space\\msm\\msm_core\\src\\main\\java\\com\\mucfc\\msm\\common\\c
acheConf.properties");Propertiesp=newProperties();p.load(in);
//分割字符串String[]targetNames=p.getProperty("targetNames").split
(",");String[]methodNames=p.getProperty("methodNames").split("
,");//加载过期时间设置defaultCacheExpireTime=Long.valueOf(p.getPropert
y("defaultCacheExpireTime"));xxxRecordManagerTime=Long.valueOf(
p.getProperty("com.service.impl.xxxRecordManager"));xxxSetRecordM
anagerTime=Long.valueOf(p.getProperty("com.service.impl.xxxSetR
ecordManager"));//创建listtargetNamesList=newArrayList(
targetNames.length);methodNamesList=newArrayList(metho
dNames.length);IntegermaxLen=targetNames.length>methodNames.
length?targetNames.length:methodNames.length;//将不需要缓存的类名和方法名添
加到list中for(inti=0;ith){targetNamesList.add(targetNames[i]);}if(igth){methodNamesList.add(methodNames[i]);}}}catch(Exceptione)
{e.printStackTrace();}}@OverridepublicObjectinvoke(MethodInvoc
ationinvocation)throwsThrowable{Objectvalue=null;Stringta
rgetName=invocation.getThis().getClass().getName();Stringmetho
dName=invocation.getMethod().getName();//不需要缓存的内容//if(!isAddC
ache(StringUtil.subStrForLastDot(targetName),methodName)){if(!
isAddCache(targetName,methodName)){//执行方法返回结果returninvocation
.proceed();}Object[]arguments=invocation.getArguments();String
key=getCacheKey(targetName,methodName,arguments);System.out.
println(key);try{//判断是否有缓存if(redisUtil.exists(key)){returnre
disUtil.get(key);}//写入缓存value=invocation.proceed();if(value!
=null){finalStringtkey=key;finalObjecttvalue=value;new
Thread(newRunnable(){@Overridepublicvoidrun(){if(tkey.start
sWith("com.service.impl.xxxRecordManager")){redisUtil.set(tkey,
tvalue,xxxRecordManagerTime);}elseif(tkey.startsWith("com.ser
vice.impl.xxxSetRecordManager")){redisUtil.set(tkey,tvalue,xxx
SetRecordManagerTime);}else{redisUtil.set(tkey,tvalue,default
CacheExpireTime);}}}).start();}}catch(Exceptione){e.printStac
kTrace();if(value==null){returninvocation.proceed();}}return
value;}/是否加入缓存@return/privatebooleanisAddCache(St
ringtargetName,StringmethodName){booleanflag=true;if(targ
etNamesList.contains(targetName)||methodNamesList.contains(metho
dName)){flag=false;}returnflag;}/创建缓存key@paramtarg
etName@parammethodName@paramarguments/privateStringge
tCacheKey(StringtargetName,StringmethodName,Object[]arguments
){StringBuffersbu=newStringBuffer();sbu.append(targetName).a
ppend("_").append(methodName);if((arguments!=null)&&(argumen
ts.length!=0)){for(inti=0;i.append("_").append(arguments[i]);}}returnsbu.toString();}public
voidsetRedisUtil(RedisUtilredisUtil){this.redisUtil=redisUt
il;}}4、配置需要缓存的类或方法在application.xml加入如下配置,有多个类或方法可以配置多个work.aop.support.RegexpMethodPointcutAdvisor">dvice">ertyname="patterns">com\.mucf
c\.msm\.service\.impl\...ServiceImpl.
ty>5、执行结果:写了一个简单的单元测试如下:@TestpublicvoidgetSettUnitBy
SettUnitIdTest(){StringsystemId="CES";StringmerchantId="
133";SettUnitconfigSettUnit=settUnitService.getSettUnitBySett
UnitId(systemId,merchantId,"ESP");SettUnitconfigSettUnit1=s
ettUnitService.getSettUnitBySettUnitId(systemId,merchantId,"ESP
");booleanflag=(configSettUnit==configSettUnit1);System.out
.println(configSettUnit);logger.info("查找结果"+configSettUnit.getBusinessType());//localSecondFIFOCache.put("configSettUnit",configSettUnit.getBusinessType());//Stringstring=localSecondFIFOCache.get("configSettUnit");//logger.info("查找结果"+string);}这是第一次执行单元测试的过程:MethodCacheInterceptor这个类中打了断点,然后每次查询前都会先进入这个方法依次运行,发现没有缓存,所以会直接去查数据库打印了出来的SQL语句:第二次执行:因为第一次执行时,已经写入缓存了。所以第二次直接从缓存中取数据3、取两次的结果进行地址的对比:发现两个不是同一个对象,没错,是对的。如果是使用http://www.codeceo.com/article/java-ehcache.htmlEhcache的话,那么二者的内存地址会是一样的。那是因为redis和ehcache使用的缓存机制是不一样的。ehcache是基于本地电脑的内存使用缓存,所以使用缓存取数据时直接在本地电脑上取。转换成java对象就会是同一个内存地址,而redis它是在装有redis服务的电脑上(一般是另一台电脑),所以取数据时经过传输到本地,会对应到不同的内存地址,所以用==来比较会返回false。但是它确实是从缓存中去取的,这点我们从上面的断点可以看到。
献花(0)
+1
(本文系关平藏书首藏)