SpringBoot整合Ehcache3的實現(xiàn)步驟_第1頁
SpringBoot整合Ehcache3的實現(xiàn)步驟_第2頁
SpringBoot整合Ehcache3的實現(xiàn)步驟_第3頁
SpringBoot整合Ehcache3的實現(xiàn)步驟_第4頁
SpringBoot整合Ehcache3的實現(xiàn)步驟_第5頁
已閱讀5頁,還剩9頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

第SpringBoot整合Ehcache3的實現(xiàn)步驟公司部門老項目要遷移升級java版本,需要進(jìn)行緩存相關(guān)操作,原框架未支持這部分,經(jīng)過調(diào)研java相關(guān)緩存方案大致分為ehcache和redis兩種,redis的value最大值為500mb且超過1mb會對存取有性能影響,業(yè)務(wù)系統(tǒng)需要支持列表查詢緩存就不可避免的涉及到大量的數(shù)據(jù)存取過濾,ehcache支持內(nèi)存+磁盤緩存不用擔(dān)心緩存容量問題,所以框架初步版本決定集成ehcache3,設(shè)計流程結(jié)構(gòu)如下圖所示

maven引用

dependency

groupIdorg.springframework.boot/groupId

artifactIdspring-boot-starter-cache/artifactId

/dependency

dependency

groupIdorg.ehcache/groupId

artifactIdehcache/artifactId

/dependency

個性化配置

#緩存配置

cache:

ehcache:

heap:1000

offheap:100

disk:500

diskDir:tempfiles/cache/

@Component

@ConfigurationProperties(frmae.cache.ehcache)

publicclassEhcacheConfiguration{

/**

*ehcacheheap大小

*jvm內(nèi)存中緩存的key數(shù)量

*/

privateintheap;

/**

*ehcacheoffheap大小

*堆外內(nèi)存大小,單位:MB

*/

privateintoffheap;

/**

*磁盤持久化目錄

*/

privateStringdiskDir;

/**

*ehcachedisk

*持久化到磁盤的大小,單位:MB

*diskDir有效時才生效

*/

privateintdisk;

publicEhcacheConfiguration(){

heap=1000;

offheap=100;

disk=500;

diskDir=tempfiles/cache/

}

}

代碼注入配置

因為springboot默認(rèn)緩存優(yōu)先注入redis配置,所以需要手動聲明bean進(jìn)行注入,同時ehcache的value值必須支持序列化接口,不能使用Object代替,這里聲明一個緩存基類,所有緩存value對象必須繼承該類

publicclassBaseSystemObjectimplementsSerializable{

}

@Configuration

@EnableCaching

publicclassEhcacheConfig{

@Autowired

privateEhcacheConfigurationehcacheConfiguration;

@Autowired

privateApplicationContextcontext;

@Bean(name=ehCacheManager)

publicCacheManagergetCacheManager(){

//資源池生成器配置持久化

ResourcePoolsBuilderresourcePoolsBuilder=ResourcePoolsBuilder.newResourcePoolsBuilder()

//堆內(nèi)緩存大小

.heap(ehcacheConfiguration.getHeap(),EntryUnit.ENTRIES)

//堆外緩存大小

.offheap(ehcacheConfiguration.getOffheap(),MemoryUnit.MB)

//文件緩存大小

.disk(ehcacheConfiguration.getDisk(),MemoryUnit.MB);

//生成配置

ExpiryPolicyexpiryPolicy=ExpiryPolicyBuilder.noExpiration();

CacheConfigurationconfig=CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class,BaseSystemObject.class,resourcePoolsBuilder)

//設(shè)置永不過期

.withExpiry(expiryPolicy)

.build();

CacheManagerBuildercacheManagerBuilder=CacheManagerBuilder.newCacheManagerBuilder()

.with(CacheManagerBuilder.persistence(ehcacheConfiguration.getDiskDir()));

returncacheManagerBuilder.build(true);

}

}

針對緩存框架選擇的雙寫策略,即數(shù)據(jù)庫和緩存同時寫入,所以在系統(tǒng)啟動時需要預(yù)先將數(shù)據(jù)庫數(shù)據(jù)加載到緩存中

針對單表聲明自定義注解,個性化緩存定義自定義接口

@Target({ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Inherited

public@interfaceHPCache{

}

publicinterfaceIHPCacheInitService{

StringgetCacheName();

voidinitCache();

}

系統(tǒng)初始化時同步進(jìn)行緩存初始化,掃描注解實體類與接口實現(xiàn)Bean

@Async

publicvoidinitCache(ClassruntimeClass,ListStringextraPackageNameList){

ListClasscacheEntityList=newArrayList();

if(!runtimeClass.getPackage().getName().equals(Application.class.getPackage().getName())){

cacheEntityList.addAll(ScanUtil.getAllClassByPackageName_Annotation(runtimeClass.getPackage(),HPCache.class));

}

for(StringpackageName:extraPackageNameList){

cacheEntityList.addAll(ScanUtil.getAllClassByPackageName_Annotation(packageName,HPCache.class));

}

for(Classclazz:cacheEntityList){

TableNametableName=(TableName)clazz.getAnnotation(TableName.class);

ListLinkedHashMapString,ObjectresultList=commonDTO.selectList(tableName.value(),*,1=1,,newHashMap(),false);

for(LinkedHashMapString,Objectmap:resultList){

Cachecache=cacheManager.getCache(clazz.getName(),String.class,BaseSystemObject.class);

Stringunitguid=ConvertOp.convert2String(map.get(UnitGuid));

try{

Objectobj=clazz.newInstance();

obj=ConvertOp.convertLinkHashMapToBean(map,obj);

cache.put(unitguid,obj);

}catch(Exceptione){

e.printStackTrace();

}

}

}

//自定義緩存

MapString,IHPCacheInitServiceres=context.getBeansOfType(IHPCacheInitService.class);

for(Map.Entryen:res.entrySet()){

IHPCacheInitServiceservice=(IHPCacheInitService)en.getValue();

service.initCache();

}

System.out.println(緩存初始化完畢

}

需要注意,在EhcacheConfig配置類中需要進(jìn)行緩存名稱的提前注冊,否則會導(dǎo)致操作緩存時空指針異常

MapString,ObjectannotatedBeans=context.getBeansWithAnnotation(SpringBootApplication.class);

ClassruntimeClass=annotatedBeans.values().toArray()[0].getClass();

//do,dao掃描

ListStringextraPackageNameList=newArrayListString

extraPackageNameList.add(Application.class.getPackage().getName());

ListClasscacheEntityList=newArrayList();

if(!runtimeClass.getPackage().getName().equals(Application.class.getPackage().getName())){

cacheEntityList.addAll(ScanUtil.getAllClassByPackageName_Annotation(runtimeClass.getPackage(),HPCache.class));

}

for(StringpackageName:extraPackageNameList){

cacheEntityList.addAll(ScanUtil.getAllClassByPackageName_Annotation(packageName,HPCache.class));

}

for(Classclazz:cacheEntityList){

cacheManagerBuilder=cacheManagerBuilder.withCache(clazz.getName(),config);

}

//自定義緩存

MapString,IHPCacheInitServiceres=context.getBeansOfType(IHPCacheInitService.class);

for(Map.Entryen:res.entrySet()){

IHPCacheInitServiceservice=(IHPCacheInitService)en.getValue();

cacheManagerBuilder=cacheManagerBuilder.withCache(service.getCacheName(),config);

}

手動獲取ehcache的bean對象,調(diào)用put,repalce,delete方法進(jìn)行操作

privateCacheManagercacheManager=(CacheManager)SpringBootBeanUtil.getBean(ehCacheManager

publicvoidexecuteUpdateOperation(StringcacheName,Stringkey,BaseSystemObjectvalue){

Cachecache=cacheManager.getCache(cacheName,String.class,BaseSystemObject.class);

if(cache.containsKey(key)){

cache.replace(key,value);

}else{

cache.put(key,value);

}

}

publicvoidexecuteDeleteOperation(StringcacheName,Stringkey){

Cachecache=cacheManager.getCache(cacheName,String.class,BaseSystemObject.class);

cache.remove(key);

}

緩存存儲單表以主鍵object形式存儲,個性化緩存為key-object形式存儲,單條記錄可以通過getCache方法查詢,列表查詢需要取出整個緩存按條件進(jìn)行過濾

publicObjectgetCache(StringcacheName,Stringkey){

Cachecache=cacheManager.getCache(cacheName,String.class,BaseSystemObject.class);

returncache.get(key);

}

publicListObjectgetAllCache(StringcacheName){

Listresult=newArrayList();

Cachecache=cacheManager.getCache(cacheName,String.class,BaseSystemObject.class);

Iteratoriter=cache.iterator();

while(iter.hasNext()){

Cache.Entryentry=(Cache.Entry)iter.next();

result.add(entry.getValue());

}

returnresult;

}

緩存與數(shù)據(jù)庫數(shù)據(jù)一致性

數(shù)據(jù)庫數(shù)據(jù)操作與緩存操作順序為先操作數(shù)據(jù)后操作緩存,在開啟數(shù)據(jù)庫事務(wù)的情況下針對單條數(shù)據(jù)單次操作是沒有問題的,如果是組合操作一旦數(shù)據(jù)庫操作發(fā)生異?;貪L,緩存并沒有回滾就會導(dǎo)致數(shù)據(jù)的不一致,比如執(zhí)行順序為dbop1=》cacheop1=》dbop2=》cacheop2,dbop2異常,cacheop1的操作已經(jīng)更改了緩存

這里選擇的方案是在數(shù)據(jù)庫全部執(zhí)行完畢后統(tǒng)一操作緩存,這個方案有一個缺點是如果緩存操作發(fā)生異常還是會出現(xiàn)上述問題,實際過程中緩存只是對內(nèi)存的操作異常概率較小,對緩存操作持樂觀狀態(tài),同時我們提供手動重置緩存的功能,算是一個折中方案,下面概述該方案的一個實現(xiàn)

聲明自定義緩存事務(wù)注解

@Target({ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Inherited

public@interfaceCacheTransactional{

}

聲明切面監(jiān)聽,在標(biāo)記了CacheTransactional注解的方法執(zhí)行前進(jìn)行Redis標(biāo)識,統(tǒng)一執(zhí)行完方法體后執(zhí)行緩存操作

將緩存操作以線程id區(qū)分放入待執(zhí)行隊列中序列化到redis,提供方法統(tǒng)一操作

publicclassCacheExecuteModelimplementsSerializable{

privateStringobejctClazzName;

privateStringcacheName;

privateStringkey;

privateBaseSystemObjectvalue;

privateStringexecuteType;

privateCacheManagercacheManager=(CacheManager)SpringBootBeanUtil.getBean(ehCacheManager

@Autowired

privateRedisUtilredisUtil;

publicvoidputCacheIntoTransition(){

StringthreadID=Thread.currentThread().getName();

System.out.println(initthreadid:+threadID);

CacheExecuteModelcacheExecuteModel=newCacheExecuteModel();

cacheExecuteModel.setExecuteType(option

redisUtil.redisTemplateSetForCollection(threadID,cacheExecuteModel,GlobalEnum.RedisDBNum.Cache.get_value());

redisUtil.setExpire(threadID,5,TimeUnit.MINUTES,GlobalEnum.RedisDBNum.Cache.get_value());

}

publicvoidputCache(StringcacheName,Stringkey,BaseSystemObjectvalue){

if(checkCacheOptinionInTransition()){

StringthreadID=Thread.currentThread().getName();

CacheExecuteModelcacheExecuteModel=newCacheExecuteModel(update,cacheName,key,value.getClass().getName(),value);

redisUtil.redisTemplateSetForCollection(threadID,cacheExecuteModel,GlobalEnum.RedisDBNum.Cache.get_value());

redisUtil.setExpire(threadID,5,TimeUnit.MINUTES,GlobalEnum.RedisDBNum.Cache.get_value());

}else{

executeUpdateOperation(cacheName,key,value);

}

}

publicvoiddeleteCache(StringcacheName,Stringkey){

if(checkCacheOptinionInTransition()){

StringthreadID=Thread.currentThread().getName();

CacheExecuteModelcacheExecuteModel=newCacheExecuteModel(delete,cacheName,key);

redisUtil.redisTemplateSetForCollection(threadID,cacheExecuteModel,GlobalEnum.RedisDBNum.Cache.get_value());

redisUtil.setExpire(threadID,5,TimeUnit.MINUTES,GlobalEnum.RedisDBNum.Cache.get_value());

}else{

executeDeleteOperation(cacheName,key);

}

}

publicvoidexecuteOperation(){

StringthreadID=Thread.currentThread().getName();

if(checkCacheOptinionInTransition()){

ListLinkedHashMapexecuteList=redisUtil.redisTemplateGetForCollectionAll(threadID,GlobalEnum.RedisDBNum.Cache.get_value());

for(LinkedHashMapobj:executeList){

StringexecuteType=ConvertOp.convert2String(obj.get(executeType));

if(executeType.contains(option)){

continue;

}

StringobejctClazzName

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論