一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語(yǔ)言|JavaScript|易語(yǔ)言|vb.net|

服務(wù)器之家 - 編程語(yǔ)言 - Java教程 - springboot中使用自定義兩級(jí)緩存的方法

springboot中使用自定義兩級(jí)緩存的方法

2021-05-04 12:30最后Q淚滴 Java教程

這篇文章主要介紹了springboot中使用自定義兩級(jí)緩存的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧

工作中用到了springboot的緩存,使用起來(lái)挺方便的,直接引入redis或者ehcache這些緩存依賴包和相關(guān)緩存的starter依賴包,然后在啟動(dòng)類中加入@enablecaching注解,然后在需要的地方就可以使用@cacheable和@cacheevict使用和刪除緩存了。這個(gè)使用很簡(jiǎn)單,相信用過(guò)springboot緩存的都會(huì)玩,這里就不再多說(shuō)了。美中不足的是,springboot使用了插件式的集成方式,雖然用起來(lái)很方便,但是當(dāng)你集成ehcache的時(shí)候就是用ehcache,集成redis的時(shí)候就是用redis。如果想兩者一起用,ehcache作為本地一級(jí)緩存,redis作為集成式的二級(jí)緩存,使用默認(rèn)的方式據(jù)我所知是沒(méi)法實(shí)現(xiàn)的(如果有高人可以實(shí)現(xiàn),麻煩指點(diǎn)下我)。畢竟很多服務(wù)需要多點(diǎn)部署,如果單獨(dú)選擇ehcache可以很好地實(shí)現(xiàn)本地緩存,但是如果在多機(jī)之間共享緩存又需要比較費(fèi)時(shí)的折騰,如果選用集中式的redis緩存,因?yàn)槊看稳?shù)據(jù)都要走網(wǎng)絡(luò),總感覺(jué)性能不會(huì)太好。本話題主要就是討論如何在springboot的基礎(chǔ)上,無(wú)縫集成ehcache和redis作為一二級(jí)緩存,并且實(shí)現(xiàn)緩存同步。

為了不要侵入springboot原本使用緩存的方式,這里自己定義了兩個(gè)緩存相關(guān)的注解,如下

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@target({elementtype.method})
@retention(retentionpolicy.runtime)
public @interface cacheable {
 
  string value() default "";
 
  string key() default "";
 
  //泛型的class類型
  class<?> type() default exception.class;
 
}
 
@target({elementtype.method})
@retention(retentionpolicy.runtime)
public @interface cacheevict {
 
  string value() default "";
 
  string key() default "";
 
}

如上兩個(gè)注解和spring中緩存的注解基本一致,只是去掉了一些不常用的屬性。說(shuō)到這里,不知道有沒(méi)有朋友注意過(guò),當(dāng)你在springboot中單獨(dú)使用redis緩存的時(shí)候,cacheable和cacheevict注解的value屬性,實(shí)際上在redis中變成了一個(gè)zset類型的值的key,而且這個(gè)zset里面還是空的,比如@cacheable(value="cache1",key="key1"),正常情況下redis中應(yīng)該是出現(xiàn)cache1 -> map(key1,value1)這種形式,其中cache1作為緩存名稱,map作為緩存的值,key作為map里的鍵,可以有效的隔離不同的緩存名稱下的緩存。但是實(shí)際上redis里確是cache1 -> 空(zset)和key1 -> value1,兩個(gè)獨(dú)立的鍵值對(duì),試驗(yàn)得知不同的緩存名稱下的緩存完全是共用的,如果有感興趣的朋友可以去試驗(yàn)下,也就是說(shuō)這個(gè)value屬性實(shí)際上是個(gè)擺設(shè),鍵的唯一性只由key屬性保證。我只能認(rèn)為這是spring的緩存實(shí)現(xiàn)的bug,或者是特意這么設(shè)計(jì)的,(如果有知道啥原因的歡迎指點(diǎn))。

回到正題,有了注解還需要有個(gè)注解處理類,這里我使用aop的切面來(lái)進(jìn)行攔截處理,原生的實(shí)現(xiàn)其實(shí)也大同小異。切面處理類如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
import com.xuanwu.apaas.core.multicache.annotation.cacheevict;
import com.xuanwu.apaas.core.multicache.annotation.cacheable;
import com.xuanwu.apaas.core.utils.jsonutil;
import org.apache.commons.lang3.stringutils;
import org.aspectj.lang.proceedingjoinpoint;
import org.aspectj.lang.annotation.around;
import org.aspectj.lang.annotation.aspect;
import org.aspectj.lang.annotation.pointcut;
import org.aspectj.lang.reflect.methodsignature;
import org.json.jsonarray;
import org.json.jsonobject;
import org.slf4j.logger;
import org.slf4j.loggerfactory;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.core.localvariabletableparameternamediscoverer;
import org.springframework.expression.expressionparser;
import org.springframework.expression.spel.standard.spelexpressionparser;
import org.springframework.expression.spel.support.standardevaluationcontext;
import org.springframework.stereotype.component;
 
import java.lang.reflect.method;
 
/**
 * 多級(jí)緩存切面
 * @author rongdi
 */
@aspect
@component
public class multicacheaspect {
 
  private static final logger logger = loggerfactory.getlogger(multicacheaspect.class);
 
  @autowired
  private cachefactory cachefactory;
 
  //這里通過(guò)一個(gè)容器初始化監(jiān)聽(tīng)器,根據(jù)外部配置的@enablecaching注解控制緩存開(kāi)關(guān)
  private boolean cacheenable;
 
  @pointcut("@annotation(com.xuanwu.apaas.core.multicache.annotation.cacheable)")
  public void cacheableaspect() {
  }
 
  @pointcut("@annotation(com.xuanwu.apaas.core.multicache.annotation.cacheevict)")
  public void cacheevict() {
  }
 
  @around("cacheableaspect()")
  public object cache(proceedingjoinpoint joinpoint) {
 
    //得到被切面修飾的方法的參數(shù)列表
    object[] args = joinpoint.getargs();
    // result是方法的最終返回結(jié)果
    object result = null;
    //如果沒(méi)有開(kāi)啟緩存,直接調(diào)用處理方法返回
    if(!cacheenable){
      try {
        result = joinpoint.proceed(args);
      } catch (throwable e) {
        logger.error("",e);
      }
      return result;
    }
 
    // 得到被代理方法的返回值類型
    class returntype = ((methodsignature) joinpoint.getsignature()).getreturntype();
    // 得到被代理的方法
    method method = ((methodsignature) joinpoint.getsignature()).getmethod();
    // 得到被代理的方法上的注解
    cacheable ca = method.getannotation(cacheable.class);
    //獲得經(jīng)過(guò)el解析后的key值
    string key = parsekey(ca.key(),method,args);
    class<?> elementclass = ca.type();
    //從注解中獲取緩存名稱
    string name = ca.value();
 
    try {
      //先從ehcache中取數(shù)據(jù)
      string cachevalue = cachefactory.ehget(name,key);
      if(stringutils.isempty(cachevalue)) {
        //如果ehcache中沒(méi)數(shù)據(jù),從redis中取數(shù)據(jù)
        cachevalue = cachefactory.redisget(name,key);
        if(stringutils.isempty(cachevalue)) {
          //如果redis中沒(méi)有數(shù)據(jù)
          // 調(diào)用業(yè)務(wù)方法得到結(jié)果
          result = joinpoint.proceed(args);
          //將結(jié)果序列化后放入redis
          cachefactory.redisput(name,key,serialize(result));
        } else {
          //如果redis中可以取到數(shù)據(jù)
          //將緩存中獲取到的數(shù)據(jù)反序列化后返回
          if(elementclass == exception.class) {
            result = deserialize(cachevalue, returntype);
          } else {
            result = deserialize(cachevalue, returntype,elementclass);
          }
        }
        //將結(jié)果序列化后放入ehcache
        cachefactory.ehput(name,key,serialize(result));
      } else {
        //將緩存中獲取到的數(shù)據(jù)反序列化后返回
        if(elementclass == exception.class) {
          result = deserialize(cachevalue, returntype);
        } else {
          result = deserialize(cachevalue, returntype,elementclass);
        }
      }
 
    } catch (throwable throwable) {
      logger.error("",throwable);
    }
 
    return result;
  }
 
  /**
   * 在方法調(diào)用前清除緩存,然后調(diào)用業(yè)務(wù)方法
   * @param joinpoint
   * @return
   * @throws throwable
   *
   */
  @around("cacheevict()")
  public object evictcache(proceedingjoinpoint joinpoint) throws throwable {
    // 得到被代理的方法
    method method = ((methodsignature) joinpoint.getsignature()).getmethod();
    //得到被切面修飾的方法的參數(shù)列表
    object[] args = joinpoint.getargs();
    // 得到被代理的方法上的注解
    cacheevict ce = method.getannotation(cacheevict.class);
    //獲得經(jīng)過(guò)el解析后的key值
    string key = parsekey(ce.key(),method,args);
    //從注解中獲取緩存名稱
    string name = ce.value();
    // 清除對(duì)應(yīng)緩存
    cachefactory.cachedel(name,key);
    return joinpoint.proceed(args);
  }
 
  /**
   * 獲取緩存的key
   * key 定義在注解上,支持spel表達(dá)式
   * @return
   */
  private string parsekey(string key,method method,object [] args){
 
    if(stringutils.isempty(key)) return null;
 
    //獲取被攔截方法參數(shù)名列表(使用spring支持類庫(kù))
    localvariabletableparameternamediscoverer u = new localvariabletableparameternamediscoverer();
    string[] paranamearr = u.getparameternames(method);
 
    //使用spel進(jìn)行key的解析
    expressionparser parser = new spelexpressionparser();
    //spel上下文
    standardevaluationcontext context = new standardevaluationcontext();
    //把方法參數(shù)放入spel上下文中
    for(int i=0;i<paranamearr.length;i++){
      context.setvariable(paranamearr[i], args[i]);
    }
    return parser.parseexpression(key).getvalue(context,string.class);
  }
 
  //序列化
  private string serialize(object obj) {
 
    string result = null;
    try {
      result = jsonutil.serialize(obj);
    } catch(exception e) {
      result = obj.tostring();
    }
    return result;
 
  }
 
  //反序列化
  private object deserialize(string str,class clazz) {
 
    object result = null;
    try {
      if(clazz == jsonobject.class) {
        result = new jsonobject(str);
      } else if(clazz == jsonarray.class) {
        result = new jsonarray(str);
      } else {
        result = jsonutil.deserialize(str,clazz);
      }
    } catch(exception e) {
    }
    return result;
 
  }
 
  //反序列化,支持list<xxx>
  private object deserialize(string str,class clazz,class elementclass) {
 
    object result = null;
    try {
      if(clazz == jsonobject.class) {
        result = new jsonobject(str);
      } else if(clazz == jsonarray.class) {
        result = new jsonarray(str);
      } else {
        result = jsonutil.deserialize(str,clazz,elementclass);
      }
    } catch(exception e) {
    }
    return result;
 
  }
 
  public void setcacheenable(boolean cacheenable) {
    this.cacheenable = cacheenable;
  }
 
}

上面這個(gè)界面使用了一個(gè)cacheenable變量控制是否使用緩存,為了實(shí)現(xiàn)無(wú)縫的接入springboot,必然需要受到原生@enablecaching注解的控制,這里我使用一個(gè)spring容器加載完成的監(jiān)聽(tīng)器,然后在監(jiān)聽(tīng)器里找到是否有被@enablecaching注解修飾的類,如果有就從spring容器拿到multicacheaspect對(duì)象,然后將cacheenable設(shè)置成true。這樣就可以實(shí)現(xiàn)無(wú)縫接入springboot,不知道朋友們還有沒(méi)有更加優(yōu)雅的方法呢?歡迎交流!監(jiān)聽(tīng)器類如下

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import com.xuanwu.apaas.core.multicache.cachefactory;
import com.xuanwu.apaas.core.multicache.multicacheaspect;
import org.springframework.cache.annotation.enablecaching;
import org.springframework.context.applicationlistener;
import org.springframework.context.event.contextrefreshedevent;
import org.springframework.stereotype.component;
 
import java.util.map;
 
/**
 * 用于spring加載完成后,找到項(xiàng)目中是否有開(kāi)啟緩存的注解@enablecaching
 * @author rongdi
 */
@component
public class contextrefreshedlistener implements applicationlistener<contextrefreshedevent> {
 
  @override
  public void onapplicationevent(contextrefreshedevent event) {
    // 判斷根容器為spring容器,防止出現(xiàn)調(diào)用兩次的情況(mvc加載也會(huì)觸發(fā)一次)
    if(event.getapplicationcontext().getparent()==null){
      //得到所有被@enablecaching注解修飾的類
      map<string,object> beans = event.getapplicationcontext().getbeanswithannotation(enablecaching.class);
      if(beans != null && !beans.isempty()) {
        multicacheaspect multicache = (multicacheaspect)event.getapplicationcontext().getbean("multicacheaspect");
        multicache.setcacheenable(true);
      }
 
    }
  }
}

實(shí)現(xiàn)了無(wú)縫接入,還需要考慮多點(diǎn)部署的時(shí)候,多點(diǎn)的ehcache怎么和redis緩存保持一致的問(wèn)題。在正常應(yīng)用中,一般redis適合長(zhǎng)時(shí)間的集中式緩存,ehcache適合短時(shí)間的本地緩存,假設(shè)現(xiàn)在有a,b和c服務(wù)器,a和b部署了業(yè)務(wù)服務(wù),c部署了redis服務(wù)。當(dāng)請(qǐng)求進(jìn)來(lái),前端入口不管是用lvs或者nginx等負(fù)載軟件,請(qǐng)求都會(huì)轉(zhuǎn)發(fā)到某一個(gè)具體服務(wù)器,假設(shè)轉(zhuǎn)發(fā)到了a服務(wù)器,修改了某個(gè)內(nèi)容,而這個(gè)內(nèi)容在redis和ehcache中都有,這時(shí)候,a服務(wù)器的ehcache緩存,和c服務(wù)器的redis不管控制緩存失效也好,刪除也好,都比較容易,但是這時(shí)候b服務(wù)器的ehcache怎么控制失效或者刪除呢?一般比較常用的方式就是使用發(fā)布訂閱模式,當(dāng)需要?jiǎng)h除緩存的時(shí)候在一個(gè)固定的通道發(fā)布一個(gè)消息,然后每個(gè)業(yè)務(wù)服務(wù)器訂閱這個(gè)通道,收到消息后刪除或者過(guò)期本地的ehcache緩存(最好是使用過(guò)期,但是redis目前只支持對(duì)key的過(guò)期操作,沒(méi)辦法操作key下的map里的成員的過(guò)期,如果非要強(qiáng)求用過(guò)期,可以自己加時(shí)間戳自己實(shí)現(xiàn),不過(guò)用刪除出問(wèn)題的幾率也很小,畢竟加緩存的都是讀多寫少的應(yīng)用,這里為了方便都是直接刪除緩存)。總結(jié)起來(lái)流程就是更新某條數(shù)據(jù),先刪除redis中對(duì)應(yīng)的緩存,然后發(fā)布一個(gè)緩存失效的消息在redis的某個(gè)通道中,本地的業(yè)務(wù)服務(wù)去訂閱這個(gè)通道的消息,當(dāng)業(yè)務(wù)服務(wù)收到這個(gè)消息后去刪除本地對(duì)應(yīng)的ehcache緩存,redis的各種配置如下

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
import com.fasterxml.jackson.annotation.jsonautodetect;
import com.fasterxml.jackson.annotation.propertyaccessor;
import com.fasterxml.jackson.databind.objectmapper;
import com.xuanwu.apaas.core.multicache.subscriber.messagesubscriber;
import org.springframework.cache.cachemanager;
import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;
import org.springframework.data.redis.cache.rediscachemanager;
import org.springframework.data.redis.connection.redisconnectionfactory;
import org.springframework.data.redis.core.redistemplate;
import org.springframework.data.redis.core.stringredistemplate;
import org.springframework.data.redis.listener.patterntopic;
import org.springframework.data.redis.listener.redismessagelistenercontainer;
import org.springframework.data.redis.listener.adapter.messagelisteneradapter;
import org.springframework.data.redis.serializer.jackson2jsonredisserializer;
 
@configuration
public class redisconfig {
 
  @bean
  public cachemanager cachemanager(redistemplate redistemplate) {
   rediscachemanager rcm = new rediscachemanager(redistemplate);
   //設(shè)置緩存過(guò)期時(shí)間(秒)
   rcm.setdefaultexpiration(600);
   return rcm;
  }
 
 
  @bean
  public redistemplate<string, string> redistemplate(redisconnectionfactory factory) {
   stringredistemplate template = new stringredistemplate(factory);
   jackson2jsonredisserializer jackson2jsonredisserializer = new jackson2jsonredisserializer(object.class);
   objectmapper om = new objectmapper();
   om.setvisibility(propertyaccessor.all, jsonautodetect.visibility.any);
   om.enabledefaulttyping(objectmapper.defaulttyping.non_final);
   jackson2jsonredisserializer.setobjectmapper(om);
   template.setvalueserializer(jackson2jsonredisserializer);
   template.afterpropertiesset();
   return template;
  }
 
  /**
  * redis消息監(jiān)聽(tīng)器容器
  * 可以添加多個(gè)監(jiān)聽(tīng)不同話題的redis監(jiān)聽(tīng)器,只需要把消息監(jiān)聽(tīng)器和相應(yīng)的消息訂閱處理器綁定,該消息監(jiān)聽(tīng)器
  * 通過(guò)反射技術(shù)調(diào)用消息訂閱處理器的相關(guān)方法進(jìn)行一些業(yè)務(wù)處理
  * @param connectionfactory
  * @param listeneradapter
  * @return
  */
  @bean
  public redismessagelistenercontainer container(redisconnectionfactory connectionfactory,
                         messagelisteneradapter listeneradapter) {
   redismessagelistenercontainer container = new redismessagelistenercontainer();
   container.setconnectionfactory(connectionfactory);
   //訂閱了一個(gè)叫redis.uncache的通道
   container.addmessagelistener(listeneradapter, new patterntopic("redis.uncache"));
   //這個(gè)container 可以添加多個(gè) messagelistener
   return container;
  }
 
  /**
  * 消息監(jiān)聽(tīng)器適配器,綁定消息處理器,利用反射技術(shù)調(diào)用消息處理器的業(yè)務(wù)方法
  * @param receiver
  * @return
  */
  @bean
  messagelisteneradapter listeneradapter(messagesubscriber receiver) {
   //這個(gè)地方 是給messagelisteneradapter 傳入一個(gè)消息接受的處理器,利用反射的方法調(diào)用“handle”
   return new messagelisteneradapter(receiver, "handle");
  }
 
}

消息發(fā)布類如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import com.xuanwu.apaas.core.multicache.cachefactory;
import org.apache.commons.lang3.stringutils;
import org.slf4j.logger;
import org.slf4j.loggerfactory;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.stereotype.component;
 
@component
public class messagesubscriber {
 
  private static final logger logger = loggerfactory.getlogger(messagesubscriber.class);
 
  @autowired
  private cachefactory cachefactory;
 
  /**
   * 接收到redis訂閱的消息后,將ehcache的緩存失效
   * @param message 格式為name_key
   */
  public void handle(string message){
 
    logger.debug("redis.ehcache:"+message);
    if(stringutils.isempty(message)) {
      return;
    }
    string[] strs = message.split("#");
    string name = strs[0];
    string key = null;
    if(strs.length == 2) {
      key = strs[1];
    }
    cachefactory.ehdel(name,key);
 
  }
 
}

具體操作緩存的類如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import com.xuanwu.apaas.core.multicache.publisher.messagepublisher;
import net.sf.ehcache.cache;
import net.sf.ehcache.cachemanager;
import net.sf.ehcache.element;
import org.apache.commons.lang3.stringutils;
import org.slf4j.logger;
import org.slf4j.loggerfactory;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.data.redis.redisconnectionfailureexception;
import org.springframework.data.redis.core.hashoperations;
import org.springframework.data.redis.core.redistemplate;
import org.springframework.stereotype.component;
 
import java.io.inputstream;
 
 
/**
 * 多級(jí)緩存切面
 * @author rongdi
 */
@component
public class cachefactory {
 
  private static final logger logger = loggerfactory.getlogger(cachefactory.class);
 
  @autowired
  private redistemplate redistemplate;
 
  @autowired
  private messagepublisher messagepublisher;
 
  private cachemanager cachemanager;
 
  public cachefactory() {
    inputstream is = this.getclass().getresourceasstream("/ehcache.xml");
    if(is != null) {
      cachemanager = cachemanager.create(is);
    }
  }
 
  public void cachedel(string name,string key) {
    //刪除redis對(duì)應(yīng)的緩存
    redisdel(name,key);
    //刪除本地的ehcache緩存,可以不需要,訂閱器那里會(huì)刪除
   //  ehdel(name,key);
    if(cachemanager != null) {
      //發(fā)布一個(gè)消息,告訴訂閱的服務(wù)該緩存失效
      messagepublisher.publish(name, key);
    }
  }
 
  public string ehget(string name,string key) {
    if(cachemanager == null) return null;
    cache cache=cachemanager.getcache(name);
    if(cache == null) return null;
    cache.acquirereadlockonkey(key);
    try {
      element ele = cache.get(key);
      if(ele == null) return null;
      return (string)ele.getobjectvalue();
    } finally {
      cache.releasereadlockonkey(key);
    }
 
 
  }
 
  public string redisget(string name,string key) {
    hashoperations<string,string,string> oper = redistemplate.opsforhash();
    try {
      return oper.get(name, key);
    } catch(redisconnectionfailureexception e) {
      //連接失敗,不拋錯(cuò),直接不用redis緩存了
      logger.error("connect redis error ",e);
      return null;
    }
  }
 
  public void ehput(string name,string key,string value) {
    if(cachemanager == null) return;
    if(!cachemanager.cacheexists(name)) {
      cachemanager.addcache(name);
    }
    cache cache=cachemanager.getcache(name);
    //獲得key上的寫鎖,不同key互相不影響,類似于synchronized(key.intern()){}
    cache.acquirewritelockonkey(key);
    try {
      cache.put(new element(key, value));
    } finally {
      //釋放寫鎖
      cache.releasewritelockonkey(key);
    }
  }
 
  public void redisput(string name,string key,string value) {
    hashoperations<string,string,string> oper = redistemplate.opsforhash();
    try {
      oper.put(name, key, value);
    } catch (redisconnectionfailureexception e) {
      //連接失敗,不拋錯(cuò),直接不用redis緩存了
      logger.error("connect redis error ",e);
    }
  }
 
  public void ehdel(string name,string key) {
    if(cachemanager == null) return;
    if(cachemanager.cacheexists(name)) {
      //如果key為空,直接根據(jù)緩存名刪除
      if(stringutils.isempty(key)) {
        cachemanager.removecache(name);
      } else {
        cache cache=cachemanager.getcache(name);
        cache.remove(key);
      }
    }
  }
 
  public void redisdel(string name,string key) {
    hashoperations<string,string,string> oper = redistemplate.opsforhash();
    try {
      //如果key為空,直接根據(jù)緩存名刪除
      if(stringutils.isempty(key)) {
        redistemplate.delete(name);
      } else {
        oper.delete(name,key);
      }
    } catch (redisconnectionfailureexception e) {
      //連接失敗,不拋錯(cuò),直接不用redis緩存了
      logger.error("connect redis error ",e);
    }
  }
}

工具類如下

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import com.fasterxml.jackson.core.type.typereference;
import com.fasterxml.jackson.databind.deserializationfeature;
import com.fasterxml.jackson.databind.javatype;
import com.fasterxml.jackson.databind.objectmapper;
import org.apache.commons.lang3.stringutils;
import org.json.jsonarray;
import org.json.jsonobject;
 
import java.util.*;
 
public class jsonutil {
 
  private static objectmapper mapper;
 
  static {
    mapper = new objectmapper();
    mapper.configure(deserializationfeature.fail_on_unknown_properties,
        false);
  }
  
 
  /**
   * 將對(duì)象序列化成json
   *
   * @param obj 待序列化的對(duì)象
   * @return
   * @throws exception
   */
  public static string serialize(object obj) throws exception {
 
    if (obj == null) {
      throw new illegalargumentexception("obj should not be null");
    }
    return mapper.writevalueasstring(obj);
  }
 
  /**
    帶泛型的反序列化,比如一個(gè)jsonarray反序列化成list<user>
  */
  public static <t> t deserialize(string jsonstr, class<?> collectionclass,
                  class<?>... elementclasses) throws exception {
    javatype javatype = mapper.gettypefactory().constructparametrizedtype(
        collectionclass, collectionclass, elementclasses);
    return mapper.readvalue(jsonstr, javatype);
  }
  
  /**
   * 將json字符串反序列化成對(duì)象
   * @param src 待反序列化的json字符串
   * @param t  反序列化成為的對(duì)象的class類型
   * @return
   * @throws exception
   */
  public static <t> t deserialize(string src, class<t> t) throws exception {
    if (src == null) {
      throw new illegalargumentexception("src should not be null");
    }
    if("{}".equals(src.trim())) {
      return null;
    }
    return mapper.readvalue(src, t);
  }
 
}

具體使用緩存,和之前一樣只需要關(guān)注@cacheable和@cacheevict注解,同樣也支持spring的el表達(dá)式。而且這里的value屬性表示的緩存名稱也沒(méi)有上面說(shuō)的那個(gè)問(wèn)題,完全可以用value隔離不同的緩存,例子如下

?
1
2
3
@cacheable(value = "bo",key="#session.productversioncode+''+#session.tenantcode+''+#objectcode")
 
@cacheevict(value = "bo",key="#session.productversioncode+''+#session.tenantcode+''+#objectcode")

附上主要的依賴包

  1. "org.springframework.boot:spring-boot-starter-redis:1.4.2.release",
  2. 'net.sf.ehcache:ehcache:2.10.4',
  3. "org.json:json:20160810"

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持服務(wù)器之家。

原文鏈接:http://www.cnblogs.com/rongdi/p/9057208.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 日本花季传媒2020旧版安卓 | 黄德维| 精品视频在线观看 | 日韩不卡一区二区三区 | 日本一道高清不卡免费 | 精品久久久久久久国产潘金莲 | 91精品国产色综合久久不卡蜜 | 强行扒开美女大腿挺进 | 亚洲va精品中文字幕 | 色老头oldmoneyvideos | 亚洲高清无码在线 视频 | 久久久精品免费免费直播 | 五月婷婷丁香在线视频 | 人人澡 人人澡碰人人看软件 | 91国内精品久久久久怡红院 | 含羞草国产亚洲精品岁国产精品 | 乳女教师欲乱动漫无修版动画3d | 亚洲国产自 | 欧美一级片免费在线观看 | 日韩在线一区二区 | 男人天堂新 | 草莓香蕉绿巨人丝瓜榴莲污在线观看 | bestialityvideo另类| 国产精品刺激好大好爽视频 | 1024毛片| 国产成人高清视频 | 亚洲av欧美在我 | 99热资源 | 风间由美在线 | 国产普通话对白露脸流出 | 国产福利视频一区二区微拍视频 | chinese壮直男gay老年人 | 99久久99热久久精品免 | 大陆日韩欧美 | 私人黄色影院 | 四虎永久在线精品国产馆v视影院 | 国产成人在线小视频 | 农夫69小说恋老妇小说 | 午夜毛片在线观看 | 国产福利一区二区在线精品 | 久久精品国产久精国产果冻传媒 |