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

服務(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教程 - Spring學(xué)習(xí)筆記之RedisTemplate的配置與使用教程

Spring學(xué)習(xí)筆記之RedisTemplate的配置與使用教程

2021-05-08 12:18一灰灰 Java教程

這篇文章主要給大家介紹了關(guān)于Spring學(xué)習(xí)筆記之RedisTemplate配置與使用的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用spring具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

前言

spring針對(duì)redis的使用,封裝了一個(gè)比較強(qiáng)大的template以方便使用;之前在spring的生態(tài)圈中也使用過(guò)redis,但直接使用jedis進(jìn)行相應(yīng)的交互操作,現(xiàn)在正好來(lái)看一下redistemplate是怎么實(shí)現(xiàn)的,以及使用起來(lái)是否更加便利

i. 基本配置

 

1. 依賴

依然是采用jedis進(jìn)行連接池管理,因此除了引入 spring-data-redis之外,再加上jedis依賴,pom文件中添加

?
1
2
3
4
5
6
7
8
9
10
11
<dependency>
 <groupid>org.springframework.data</groupid>
 <artifactid>spring-data-redis</artifactid>
 <version>1.8.4.release</version>
</dependency>
 
<dependency>
 <groupid>redis.clients</groupid>
 <artifactid>jedis</artifactid>
 <version>2.9.0</version>
</dependency>

如果需要指定序列化相關(guān)參數(shù),也可以引入jackson,本篇為簡(jiǎn)單入門級(jí),就不加這個(gè)了

2. 配置文件

準(zhǔn)備redis相關(guān)的配置參數(shù),常見(jiàn)的有host, port, password, timeout…,下面是一份簡(jiǎn)單的配置,并給出了相應(yīng)的含義

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
redis.hostname=127.0.0.1
redis.port=6379
redis.password=https://blog.hhui.top
# 連接超時(shí)時(shí)間
redis.timeout=10000
 
#最大空閑數(shù)
redis.maxidle=300
#控制一個(gè)pool可分配多少個(gè)jedis實(shí)例,用來(lái)替換上面的redis.maxactive,如果是jedis 2.4以后用該屬性
redis.maxtotal=1000
#最大建立連接等待時(shí)間。如果超過(guò)此時(shí)間將接到異常。設(shè)為-1表示無(wú)限制。
redis.maxwaitmillis=1000
#連接的最小空閑時(shí)間 默認(rèn)1800000毫秒(30分鐘)
redis.minevictableidletimemillis=300000
#每次釋放連接的最大數(shù)目,默認(rèn)3
redis.numtestsperevictionrun=1024
#逐出掃描的時(shí)間間隔(毫秒) 如果為負(fù)數(shù),則不運(yùn)行逐出線程, 默認(rèn)-1
redis.timebetweenevictionrunsmillis=30000
#是否在從池中取出連接前進(jìn)行檢驗(yàn),如果檢驗(yàn)失敗,則從池中去除連接并嘗試取出另一個(gè)
redis.testonborrow=true
#在空閑時(shí)檢查有效性, 默認(rèn)false
redis.testwhileidle=true

說(shuō)明

redis密碼請(qǐng)一定記得設(shè)置,特別是在允許遠(yuǎn)程訪問(wèn)的時(shí)候,如果沒(méi)有密碼,默認(rèn)端口號(hào),很容易就被是掃描注入腳本,然后開始給人挖礦(親身經(jīng)歷…)

ii. 使用與測(cè)試

 

根據(jù)一般的思路,首先是得加載上面的配置,創(chuàng)建redis連接池,然后再實(shí)例化redistemplate對(duì)象,最后持有這個(gè)實(shí)力開始各種讀寫操作

1. 配置類

使用javaconfig的方式來(lái)配置,主要是兩個(gè)bean,讀取配置文件設(shè)置各種參數(shù)的redisconnectionfactory以及預(yù)期的redistemplate

?
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
@configuration
@propertysource("classpath:redis.properties")
public class redisconfig extends jcacheconfigurersupport {
 @autowired
 private environment environment;
 
 @bean
 public redisconnectionfactory redisconnectionfactory() {
 jedisconnectionfactory fac = new jedisconnectionfactory();
 fac.sethostname(environment.getproperty("redis.hostname"));
 fac.setport(integer.parseint(environment.getproperty("redis.port")));
 fac.setpassword(environment.getproperty("redis.password"));
 fac.settimeout(integer.parseint(environment.getproperty("redis.timeout")));
 fac.getpoolconfig().setmaxidle(integer.parseint(environment.getproperty("redis.maxidle")));
 fac.getpoolconfig().setmaxtotal(integer.parseint(environment.getproperty("redis.maxtotal")));
 fac.getpoolconfig().setmaxwaitmillis(integer.parseint(environment.getproperty("redis.maxwaitmillis")));
 fac.getpoolconfig().setminevictableidletimemillis(
 integer.parseint(environment.getproperty("redis.minevictableidletimemillis")));
 fac.getpoolconfig()
 .setnumtestsperevictionrun(integer.parseint(environment.getproperty("redis.numtestsperevictionrun")));
 fac.getpoolconfig().settimebetweenevictionrunsmillis(
 integer.parseint(environment.getproperty("redis.timebetweenevictionrunsmillis")));
 fac.getpoolconfig().settestonborrow(boolean.parseboolean(environment.getproperty("redis.testonborrow")));
 fac.getpoolconfig().settestwhileidle(boolean.parseboolean(environment.getproperty("redis.testwhileidle")));
 return fac;
 }
 
 @bean
 public redistemplate<string, string> redistemplate(redisconnectionfactory redisconnectionfactory) {
 redistemplate<string, string> redis = new redistemplate<>();
 redis.setconnectionfactory(redisconnectionfactory);
 redis.afterpropertiesset();
 return redis;
 }
}

2. 測(cè)試與使用

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@runwith(springjunit4classrunner.class)
@contextconfiguration(classes = {redisconfig.class})
public class redistest {
 
 @autowired
 private redistemplate<string, string> redistemplate;
 
 @test
 public void testredisobj() {
 map<string, object> properties = new hashmap<>();
 properties.put("123", "hello");
 properties.put("abc", 456);
 
 redistemplate.opsforhash().putall("hash", properties);
 
 map<object, object> ans = redistemplate.opsforhash().entries("hash");
 system.out.println("ans: " + ans);
 }
}

執(zhí)行后輸出如下

?
1
ans: {123=hello, abc=456}

從上面的配置與實(shí)現(xiàn)來(lái)看,是很簡(jiǎn)單的了,基本上沒(méi)有繞什么圈子,但是使用redis-cli連上去,卻查詢不到 hash 這個(gè)key的內(nèi)容

?
1
2
3
4
127.0.0.1:6379> get hash
(nil)
127.0.0.1:6379> keys *
1) "\xac\xed\x00\x05t\x00\x04hash"

使用代碼去查沒(méi)問(wèn)題,直接控制臺(tái)連接,發(fā)現(xiàn)這個(gè)key和我們預(yù)期的不一樣,多了一些前綴,why ?

3. 序列化問(wèn)題

為了解決上面的問(wèn)題,只能debug進(jìn)去,看下是什么引起的了

對(duì)應(yīng)源碼位置:

?
1
2
3
4
5
6
// org.springframework.data.redis.core.abstractoperations#rawkey
 
byte[] rawkey(object key) {
 assert.notnull(key, "non null key required");
 return this.keyserializer() == null && key instanceof byte[] ? (byte[])((byte[])key) : this.keyserializer().serialize(key);
}

可以看到這個(gè)key不是我們預(yù)期的 key.getbytes(), 而是調(diào)用了this.keyserializer().serialize(key),而debug的結(jié)果,默認(rèn)serializer是jdkserializationredisserializer

Spring學(xué)習(xí)筆記之RedisTemplate的配置與使用教程

然后就是順藤摸瓜一步一步深入進(jìn)去,鏈路如下

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// org.springframework.core.serializer.support.serializingconverter#convert
 
// org.springframework.core.serializer.defaultserializer#serialize
 
public class defaultserializer implements serializer<object> {
 public defaultserializer() {
 }
 
 public void serialize(object object, outputstream outputstream) throws ioexception {
 if (!(object instanceof serializable)) {
 throw new illegalargumentexception(this.getclass().getsimplename() + " requires a serializable payload but received an object of type [" + object.getclass().getname() + "]");
 } else {
 objectoutputstream objectoutputstream = new objectoutputstream(outputstream);
 objectoutputstream.writeobject(object);
 objectoutputstream.flush();
 }
 }
}

所以具體的實(shí)現(xiàn)很清晰了,就是 objectoutputstream,這個(gè)東西就是java中最原始的序列化反序列流工具,會(huì)包含類型信息,所以會(huì)帶上那串前綴了

所以要解決這個(gè)問(wèn)題,也比較明確了,替換掉原生的jdkserializationredisserializer,改為string的方式,正好提供了一個(gè)stringredisserializer,所以在redistemplate的配置處,稍稍修改

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@bean
public redistemplate<string, string> redistemplate(redisconnectionfactory redisconnectionfactory) {
 redistemplate<string, string> redis = new redistemplate<>();
 redis.setconnectionfactory(redisconnectionfactory);
 
 // 設(shè)置redis的string/value的默認(rèn)序列化方式
 stringredisserializer stringredisserializer = new stringredisserializer();
 redis.setkeyserializer(stringredisserializer);
 redis.setvalueserializer(stringredisserializer);
 redis.sethashkeyserializer(stringredisserializer);
 redis.sethashvalueserializer(stringredisserializer);
 
 redis.afterpropertiesset();
 return redis;
}

再次執(zhí)行,結(jié)果尷尬的事情出現(xiàn)了,拋異常了,類型轉(zhuǎn)換失敗

?
1
2
3
4
5
6
java.lang.classcastexception: java.lang.integer cannot be cast to java.lang.string
 
 at org.springframework.data.redis.serializer.stringredisserializer.serialize(stringredisserializer.java:33)
 at org.springframework.data.redis.core.abstractoperations.rawhashvalue(abstractoperations.java:171)
 at org.springframework.data.redis.core.defaulthashoperations.putall(defaulthashoperations.java:129)
 ...

看前面的測(cè)試用例,map中的value有integer,而stringredisserializer接收的參數(shù)必須是string,所以不用這個(gè),自己照樣子重新寫一個(gè)兼容掉

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class defaultstrserializer implements redisserializer<object> {
 private final charset charset;
 
 public defaultstrserializer() {
 this(charset.forname("utf8"));
 }
 
 public defaultstrserializer(charset charset) {
 assert.notnull(charset, "charset must not be null!");
 this.charset = charset;
 }
 
 
 @override
 public byte[] serialize(object o) throws serializationexception {
 return o == null ? null : string.valueof(o).getbytes(charset);
 }
 
 @override
 public object deserialize(byte[] bytes) throws serializationexception {
 return bytes == null ? null : new string(bytes, charset);
 
 }
}

然后可以開始愉快的玩耍了,執(zhí)行完之后測(cè)試

?
1
2
3
4
5
6
7
8
keys *
1) "\xac\xed\x00\x05t\x00\x04hash"
2) "hash"
127.0.0.1:6379> hgetall hash
1) "123"
2) "hello"
3) "abc"
4) "456"

iii. redistemplate使用姿勢(shì)

 

1. opsforxxx

簡(jiǎn)單過(guò)來(lái)一下redistemplate的使用姿勢(shì),針對(duì)不同的數(shù)據(jù)結(jié)構(gòu)(string, list, zset, hash)讀封裝了比較使用的調(diào)用方式 opsforxxx

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// hash 數(shù)據(jù)結(jié)構(gòu)操作
org.springframework.data.redis.core.redistemplate#opsforhash
 
// list
org.springframework.data.redis.core.redistemplate#opsforlist
 
// string
org.springframework.data.redis.core.redistemplate#opsforvalue
 
// set
org.springframework.data.redis.core.redistemplate#opsforset
 
// zset
org.springframework.data.redis.core.redistemplate#opsforzset

2. execute

除了上面的這種使用方式之外,另外一種常見(jiàn)的就是直接使用execute了,一個(gè)簡(jiǎn)單的case如下

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@test
public void testredis() {
 string key = "hello";
 string value = "world";
 redistemplate.execute((rediscallback<void>) con -> {
 con.set(key.getbytes(), value.getbytes());
 return null;
 });
 
 string asn = redistemplate.execute((rediscallback<string>) con -> new string(con.get(key.getbytes())));
 system.out.println(asn);
 
 
 string hkey = "hkey";
 redistemplate.execute((rediscallback<void>) con -> {
 con.hset(hkey.getbytes(), "23".getbytes(), "what".getbytes());
 return null;
 });
 
 map<byte[], byte[]> map = redistemplate.execute((rediscallback<map<byte[], byte[]>>) con -> con.hgetall(hkey.getbytes()));
 for (map.entry<byte[], byte[]> entry : map.entryset()) {
 system.out.println("key: " + new string(entry.getkey()) + " | value: " + new string(entry.getvalue()));
 }
}

輸出結(jié)果如下

world
key: 23 | value: what

3. 區(qū)別

一個(gè)自然而然能想到的問(wèn)題就是上面的兩種方式有什么區(qū)別?

opsforxxx 的底層,就是通過(guò)調(diào)用execute方式來(lái)做的,其主要就是封裝了一些使用姿勢(shì),定義了序列化,使用起來(lái)更加的簡(jiǎn)單和便捷;這種方式下,帶來(lái)的小號(hào)就是每次都需要新建一個(gè)defaultxxxoperations對(duì)象,多繞了一步,基于此是否會(huì)帶來(lái)額外的性能和內(nèi)存開銷呢?沒(méi)測(cè)過(guò),但個(gè)人感覺(jué)量小的情況下,應(yīng)該沒(méi)什么明顯的影響;而qps很高的情況下,這方便的優(yōu)化能帶來(lái)的幫助,估計(jì)也不太大

iv. 其他

 

0. 項(xiàng)目

study-demo/spring-redis

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)服務(wù)器之家的支持。

原文鏈接:https://liuyueyi.github.io/hexblog/2018/06/11/180611-SpringRedisTemplate配置與使用/

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 啪啪免费入口网站 | 欧美综合在线 | 果冻传媒在线完整免费观 | 久久精品WWW人人爽人人 | 变态女王麻麻小说在线阅读 | 国产精品成人 | 青青国产成人久久激情91麻豆 | 9420高清完整版在线观看国语 | 黑人性xxxⅹxxbbbbb | 国产a不卡片精品免费观看 国产aaa伦理片 | 日韩欧美一区二区三区四区 | 午夜爽喷水无码成人18禁三级 | 国产悠悠视频在线播放 | 日韩不卡一区二区三区 | 跪在老师脚下吃丝袜脚 | 学校捏奶揉下面污文h | 我将她侵犯1~6樱花动漫在线看 | 任我行视频在线观看国语 | 日本高清在线看免费观看 | 精品一区二区三区 不卡高清 | 色琪琪原网站亚洲香蕉 | 欧洲网色偷偷亚洲男人的天堂 | 亚洲精品乱码蜜桃久久久 | 日韩日韩日韩手机看片自拍 | 男女做受快插大片 | 欧美综合国产精品日韩一 | 欧美3d怪物交videos网站 | 高肉h护士办公室play | 久久视频这有精品63在线国产 | 深夜视频免费看 | 私人影院在线播放 | 国产午夜精品一区二区三区不卡 | tobu8在线观看免费高清 | 国产91第一页 | 色cccwww在线播放 | 欧美国产合集在线视频 | 男插女的下面免费视频夜色 | 甜蜜调教| www.5151淫| 亚洲一区二区成人 | 国产亚洲精aa在线观看不卡 |