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

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - springboot中使用自定義兩級緩存的方法

springboot中使用自定義兩級緩存的方法

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

這篇文章主要介紹了springboot中使用自定義兩級緩存的方法,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

工作中用到了springboot的緩存,使用起來挺方便的,直接引入redis或者ehcache這些緩存依賴包和相關緩存的starter依賴包,然后在啟動類中加入@enablecaching注解,然后在需要的地方就可以使用@cacheable和@cacheevict使用和刪除緩存了。這個使用很簡單,相信用過springboot緩存的都會玩,這里就不再多說了。美中不足的是,springboot使用了插件式的集成方式,雖然用起來很方便,但是當你集成ehcache的時候就是用ehcache,集成redis的時候就是用redis。如果想兩者一起用,ehcache作為本地一級緩存,redis作為集成式的二級緩存,使用默認的方式據我所知是沒法實現的(如果有高人可以實現,麻煩指點下我)。畢竟很多服務需要多點部署,如果單獨選擇ehcache可以很好地實現本地緩存,但是如果在多機之間共享緩存又需要比較費時的折騰,如果選用集中式的redis緩存,因為每次取數據都要走網絡,總感覺性能不會太好。本話題主要就是討論如何在springboot的基礎上,無縫集成ehcache和redis作為一二級緩存,并且實現緩存同步。

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

?
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 "";
 
}

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

回到正題,有了注解還需要有個注解處理類,這里我使用aop的切面來進行攔截處理,原生的實現其實也大同小異。切面處理類如下:

?
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;
 
/**
 * 多級緩存切面
 * @author rongdi
 */
@aspect
@component
public class multicacheaspect {
 
  private static final logger logger = loggerfactory.getlogger(multicacheaspect.class);
 
  @autowired
  private cachefactory cachefactory;
 
  //這里通過一個容器初始化監聽器,根據外部配置的@enablecaching注解控制緩存開關
  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) {
 
    //得到被切面修飾的方法的參數列表
    object[] args = joinpoint.getargs();
    // result是方法的最終返回結果
    object result = null;
    //如果沒有開啟緩存,直接調用處理方法返回
    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);
    //獲得經過el解析后的key值
    string key = parsekey(ca.key(),method,args);
    class<?> elementclass = ca.type();
    //從注解中獲取緩存名稱
    string name = ca.value();
 
    try {
      //先從ehcache中取數據
      string cachevalue = cachefactory.ehget(name,key);
      if(stringutils.isempty(cachevalue)) {
        //如果ehcache中沒數據,從redis中取數據
        cachevalue = cachefactory.redisget(name,key);
        if(stringutils.isempty(cachevalue)) {
          //如果redis中沒有數據
          // 調用業務方法得到結果
          result = joinpoint.proceed(args);
          //將結果序列化后放入redis
          cachefactory.redisput(name,key,serialize(result));
        } else {
          //如果redis中可以取到數據
          //將緩存中獲取到的數據反序列化后返回
          if(elementclass == exception.class) {
            result = deserialize(cachevalue, returntype);
          } else {
            result = deserialize(cachevalue, returntype,elementclass);
          }
        }
        //將結果序列化后放入ehcache
        cachefactory.ehput(name,key,serialize(result));
      } else {
        //將緩存中獲取到的數據反序列化后返回
        if(elementclass == exception.class) {
          result = deserialize(cachevalue, returntype);
        } else {
          result = deserialize(cachevalue, returntype,elementclass);
        }
      }
 
    } catch (throwable throwable) {
      logger.error("",throwable);
    }
 
    return result;
  }
 
  /**
   * 在方法調用前清除緩存,然后調用業務方法
   * @param joinpoint
   * @return
   * @throws throwable
   *
   */
  @around("cacheevict()")
  public object evictcache(proceedingjoinpoint joinpoint) throws throwable {
    // 得到被代理的方法
    method method = ((methodsignature) joinpoint.getsignature()).getmethod();
    //得到被切面修飾的方法的參數列表
    object[] args = joinpoint.getargs();
    // 得到被代理的方法上的注解
    cacheevict ce = method.getannotation(cacheevict.class);
    //獲得經過el解析后的key值
    string key = parsekey(ce.key(),method,args);
    //從注解中獲取緩存名稱
    string name = ce.value();
    // 清除對應緩存
    cachefactory.cachedel(name,key);
    return joinpoint.proceed(args);
  }
 
  /**
   * 獲取緩存的key
   * key 定義在注解上,支持spel表達式
   * @return
   */
  private string parsekey(string key,method method,object [] args){
 
    if(stringutils.isempty(key)) return null;
 
    //獲取被攔截方法參數名列表(使用spring支持類庫)
    localvariabletableparameternamediscoverer u = new localvariabletableparameternamediscoverer();
    string[] paranamearr = u.getparameternames(method);
 
    //使用spel進行key的解析
    expressionparser parser = new spelexpressionparser();
    //spel上下文
    standardevaluationcontext context = new standardevaluationcontext();
    //把方法參數放入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;
  }
 
}

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

?
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加載完成后,找到項目中是否有開啟緩存的注解@enablecaching
 * @author rongdi
 */
@component
public class contextrefreshedlistener implements applicationlistener<contextrefreshedevent> {
 
  @override
  public void onapplicationevent(contextrefreshedevent event) {
    // 判斷根容器為spring容器,防止出現調用兩次的情況(mvc加載也會觸發一次)
    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);
      }
 
    }
  }
}

實現了無縫接入,還需要考慮多點部署的時候,多點的ehcache怎么和redis緩存保持一致的問題。在正常應用中,一般redis適合長時間的集中式緩存,ehcache適合短時間的本地緩存,假設現在有a,b和c服務器,a和b部署了業務服務,c部署了redis服務。當請求進來,前端入口不管是用lvs或者nginx等負載軟件,請求都會轉發到某一個具體服務器,假設轉發到了a服務器,修改了某個內容,而這個內容在redis和ehcache中都有,這時候,a服務器的ehcache緩存,和c服務器的redis不管控制緩存失效也好,刪除也好,都比較容易,但是這時候b服務器的ehcache怎么控制失效或者刪除呢?一般比較常用的方式就是使用發布訂閱模式,當需要刪除緩存的時候在一個固定的通道發布一個消息,然后每個業務服務器訂閱這個通道,收到消息后刪除或者過期本地的ehcache緩存(最好是使用過期,但是redis目前只支持對key的過期操作,沒辦法操作key下的map里的成員的過期,如果非要強求用過期,可以自己加時間戳自己實現,不過用刪除出問題的幾率也很小,畢竟加緩存的都是讀多寫少的應用,這里為了方便都是直接刪除緩存)??偨Y起來流程就是更新某條數據,先刪除redis中對應的緩存,然后發布一個緩存失效的消息在redis的某個通道中,本地的業務服務去訂閱這個通道的消息,當業務服務收到這個消息后去刪除本地對應的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);
   //設置緩存過期時間(秒)
   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消息監聽器容器
  * 可以添加多個監聽不同話題的redis監聽器,只需要把消息監聽器和相應的消息訂閱處理器綁定,該消息監聽器
  * 通過反射技術調用消息訂閱處理器的相關方法進行一些業務處理
  * @param connectionfactory
  * @param listeneradapter
  * @return
  */
  @bean
  public redismessagelistenercontainer container(redisconnectionfactory connectionfactory,
                         messagelisteneradapter listeneradapter) {
   redismessagelistenercontainer container = new redismessagelistenercontainer();
   container.setconnectionfactory(connectionfactory);
   //訂閱了一個叫redis.uncache的通道
   container.addmessagelistener(listeneradapter, new patterntopic("redis.uncache"));
   //這個container 可以添加多個 messagelistener
   return container;
  }
 
  /**
  * 消息監聽器適配器,綁定消息處理器,利用反射技術調用消息處理器的業務方法
  * @param receiver
  * @return
  */
  @bean
  messagelisteneradapter listeneradapter(messagesubscriber receiver) {
   //這個地方 是給messagelisteneradapter 傳入一個消息接受的處理器,利用反射的方法調用“handle”
   return new messagelisteneradapter(receiver, "handle");
  }
 
}

消息發布類如下:

?
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;
 
 
/**
 * 多級緩存切面
 * @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對應的緩存
    redisdel(name,key);
    //刪除本地的ehcache緩存,可以不需要,訂閱器那里會刪除
   //  ehdel(name,key);
    if(cachemanager != null) {
      //發布一個消息,告訴訂閱的服務該緩存失效
      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) {
      //連接失敗,不拋錯,直接不用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) {
      //連接失敗,不拋錯,直接不用redis緩存了
      logger.error("connect redis error ",e);
    }
  }
 
  public void ehdel(string name,string key) {
    if(cachemanager == null) return;
    if(cachemanager.cacheexists(name)) {
      //如果key為空,直接根據緩存名刪除
      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為空,直接根據緩存名刪除
      if(stringutils.isempty(key)) {
        redistemplate.delete(name);
      } else {
        oper.delete(name,key);
      }
    } catch (redisconnectionfailureexception e) {
      //連接失敗,不拋錯,直接不用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);
  }
  
 
  /**
   * 將對象序列化成json
   *
   * @param obj 待序列化的對象
   * @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);
  }
 
  /**
    帶泛型的反序列化,比如一個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字符串反序列化成對象
   * @param src 待反序列化的json字符串
   * @param t  反序列化成為的對象的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);
  }
 
}

具體使用緩存,和之前一樣只需要關注@cacheable和@cacheevict注解,同樣也支持spring的el表達式。而且這里的value屬性表示的緩存名稱也沒有上面說的那個問題,完全可以用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"

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

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

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 热久久最新视频 | 热99精品视频 | 大叔在线观看 | 亚洲日本在线观看网址 | 调教全程肉动画片在线观看 | 美女靠逼免费网站 | 国产一级视频在线观看 | 国产午夜精品理论片 | 黄篇网站在线观看 | 被教官揉了一晚上的奶小说 | 欧美 变态 另类 人妖班 | 午夜综合 | avtt在线播放 | 19+韩国女主播激情vip视频在线 | 四虎在线网站 | 办公室恋情在线 | 好紧好爽的午夜寂寞视频 | 羞羞答答影院在线 | 日本道三区播放区 | 亚洲 欧美 国产 综合久久 | 无码一区二区三区视频 | 麻豆最新 | 国产亚洲一级精品久久 | 国产区1 | 日本草草视频在线观看 | 日本黄a三级三级三级 | 成人深夜视频 | 武侠古典久久亚洲精品 | 俄罗斯bbbbbbxxxxxx | 亚洲高清一区二区三区久久 | 小早川怜子亚洲综合中文字幕 | 热伊人99re久久精品最新地 | dyav午夜片 | 鸥美毛片| 特黄视频免费看 | 日本护士撒尿xxxxhd | 色漫在线观看 | 国产高清免费午夜在线视频 | 欧美三级做爰全过程 | 无遮挡h肉动漫在线观看电车 | 白丝萝莉喷水 |