javascript
Springboot整合Redis(RedisConfig等工具类编写)
我們使用的是上一期創(chuàng)建的Spring boot項(xiàng)目,沒看過那篇文章的可以去看看Springboot整合數(shù)據(jù)庫 +JpaRepository實(shí)現(xiàn)簡單數(shù)據(jù)查詢
目錄
- Redis介紹
- 1.添加依賴
- 2.在`application.yml`配置Redis
- 3.在項(xiàng)目中直接引入Redis的問題記錄
- 4.使用注解來實(shí)現(xiàn)redis緩存
- @Cacheable
- @CacheEvict
- @CachePut
- @Caching
- 5.編寫RedisConfig
- 定義key的序列化與反序列化
- 定義value的序列化與反序列化
- RedisConfig的編寫
- 后記
- 若有錯誤,歡迎指正,互相學(xué)習(xí),相互交流
Redis介紹
Redis 是目前業(yè)界使用最廣泛的內(nèi)存數(shù)據(jù)存儲。相比 Memcached,Redis 支持更豐富的數(shù)據(jù)結(jié)構(gòu),例如 hashes, lists, sets 等,同時(shí)支持?jǐn)?shù)據(jù)持久化。
本文介紹 Spring boot 集成 Redis 來實(shí)現(xiàn)數(shù)據(jù)緩存
1.添加依賴
在pom文件中添加redis常用依賴
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>2.在application.yml配置Redis
redis:#數(shù)據(jù)庫索引database: 0#服務(wù)器地址host: 127.0.0.1#端口號port: 6379#redis密碼(默認(rèn)為空)password:#連接redis超時(shí)時(shí)間timeout: 30003.在項(xiàng)目中直接引入Redis的問題記錄
在application.yml文件中配置了redis相關(guān)配置了之后呢,我們就可以在使用的類上直接引入RedisTemplate,具體怎么引入看下述代碼
@Autowiredprivate RedisTemplate<Object,Object> redisTemplate;我這里就在Service實(shí)現(xiàn)類上引入了RedisTemplate
第一個(gè)問題:存進(jìn)value的Object,必須序列化要不然會報(bào)錯
序列化的話在User類上實(shí)現(xiàn)Serializable接口即可,看下圖
第二個(gè)問題:存進(jìn)redis的key也需要序列化增加其可讀性,我們先來看看不序列化的key
上述的代碼執(zhí)行步驟呢就是先去數(shù)據(jù)庫查詢名稱為userInfoKey的key,然后如果查到了直接返回結(jié)果即可,沒查到就去數(shù)據(jù)庫查詢,并把查詢到的結(jié)果放到緩存里,再返回結(jié)果
執(zhí)行完上面的步驟,在redis里的key應(yīng)該是userInfoKey,但是并不是這樣的,我們?nèi)edis里看看
我框出來的這段就是我們存進(jìn)redis的key可以觀察到,它是一個(gè)序列化后的字符串,這樣當(dāng)key比較多的時(shí)候,是非常不方便我們觀察的,所以我們在往redis里插入數(shù)據(jù)時(shí),就得設(shè)置key的序列化對象為字符串,加上下面這段代碼即可
將redis現(xiàn)在的key刪除,執(zhí)行完上面的代碼,我們再來看看redis中的key是怎么樣的
所以,當(dāng)我們設(shè)置了key的序列化屬性后,存進(jìn)redis里的就是我們設(shè)置的key,在數(shù)據(jù)庫中也更方便我們的觀察
第三個(gè)問題:所生成的key必須是不同的查詢返回的結(jié)果所對應(yīng)的key必須是不同的,如果像我們上面那樣寫出來的代碼,無論我查詢name = "張三"還是name = "李四"返回的結(jié)果是一樣的,因?yàn)槲覀僰ey是寫死的,所以針對不同的查詢,我們必須生成不同的key,例如根據(jù)類名稱、方法名稱、包名稱和方法參數(shù)來生成一個(gè)唯一的key,這樣就可以根據(jù)不同的查詢所生成對應(yīng)的key去緩存查詢不同的結(jié)果
上面說了這么多,其實(shí)直接使用RedisTemplate還是有很多問題的,一般我們都會使用注解的方式+RedisConfig 來定義貼合我們項(xiàng)目的緩存
4.使用注解來實(shí)現(xiàn)redis緩存
針對上面的問題,spring boot引入的redis的包,里面包含了有關(guān)redis的相關(guān)注解,我們來看看
@Cacheable
我給大家解釋一下這個(gè)@AliasFor注解,它的意思是別名的意思,看著上面的圖片,也就是key和value互為別名,也就是說@AliasFor必須成對出現(xiàn),且@AliasFor標(biāo)注的字段必須設(shè)置默認(rèn)值,要不然會報(bào)錯
@Cacheable可以標(biāo)記在一個(gè)方法上,也可以標(biāo)記在一個(gè)類上。當(dāng)標(biāo)記在一個(gè)方法上時(shí)表示該方法是支持緩存的,當(dāng)標(biāo)記在一個(gè)類上時(shí)則表示該類所有的方法都是支持緩存的
// @Cacheable(value = "'userInfo' + #p0") // @Cacheable(value = "userInfo",key = "#p0")//參數(shù)為user的話key可以使用這個(gè)屬性獲取到user內(nèi)的屬性//盡量使用數(shù)據(jù)的唯一變量,不能保證user.name是唯一的 // @Cacheable(value = "userInfo",key = "#user.name") // @Cacheable(value = "userInfo",key = "#p0.name")public User getUserInfo(String name) {return userRepository.findByName(name);}我們來看看redis中生成的key
就是我們通過了傳進(jìn)來的參數(shù)作為key,保證了每次傳進(jìn)來的參數(shù)不同,查詢緩存返回的結(jié)果不同,這都是Redis內(nèi)部封裝好的key生成策略,我們也可以自定義key的生成策略
我們來重點(diǎn)看看這個(gè)condition
@Cacheable(value = "userInfo",key = "#p0",condition = "#p0.equals('張三')")public User getUserInfo(String name) {return userRepository.findByName(name);}這個(gè)字段就是為我們做了一些過濾的操作,也就是說,加上這個(gè)字段,就可以寫一些表達(dá)式,向上面這段代碼,當(dāng)我們傳進(jìn)來的參數(shù)為張三時(shí)才會進(jìn)行緩存,傳進(jìn)其它的參數(shù)不進(jìn)行緩存,這里可以使用SpringEL表達(dá)式來盡行操作
還有一些其他的屬性,具體我不展開,感興趣的在下面評論我們交流交流
@CacheEvict
這個(gè)注解其實(shí)就是類似清除緩存的一個(gè)觸發(fā)器,當(dāng)它標(biāo)注在類上則說明該類的方法只要執(zhí)行,都會觸發(fā)清除緩存的操作,可以指定清除緩存的key(value)以及使用condition來匹配清除什么樣的key
這里說一下allEntries和beforeInvocation
allEntries表示是否需要清除緩存中的所有元素,屬性為boolean,若為true則無視key(value)所設(shè)置的值,表示清除所有的元素,默認(rèn)為false
beforeInvocation表示是在方法執(zhí)行之前清除緩存還是執(zhí)行之后清除緩存,若為true則表示方法執(zhí)行前清楚緩存,默認(rèn)為false,即方法執(zhí)行成功后才清除緩存,若方法執(zhí)行中拋出異常沒執(zhí)行完則不會清除緩存
@CachePut
與**@Cacheable**的區(qū)別是
@CachePut不會檢查緩存中的數(shù)據(jù),而是每次都會把方法執(zhí)行完,把返回的結(jié)果存到指定緩存中
@Cacheable會去緩存查,如果緩存有,直接從緩存中取出來返回
@Caching
這個(gè)注解是可以同時(shí)設(shè)置多組**@Cacheable、@CachePut和@CacheEvict**
5.編寫RedisConfig
首先我們要明白RedisConfig中需要包含什么,首先看看我們直接使用RedisTemplate的問題,我們就知道RedisConfig要包含什么了,我們在RedisConfig需要規(guī)定好根據(jù)不同的查詢生成的key,key和value的序列化和反序列化
我們在RedisConfig中配置的自定義方法,最終通過注解引用的就是我們自定義的方法
定義key的序列化與反序列化
因?yàn)閗ey在redis通常都是String類型的,我們定義一個(gè)將key序列化成String的一個(gè)類
實(shí)現(xiàn)其RedisSerializer<Object>接口,主要重寫的方法是序列化的過程,因?yàn)閗ey不一定是String類型的,還有可能是json類型,如果key為json我們先將key,轉(zhuǎn)化為String類型,再序列化,防止序列化出現(xiàn)問題
public class StringRedisSerializer implements RedisSerializer<Object> {private final Charset charset;private final String target = "\"";private final String replacement = "";public StringRedisSerializer() {this(Charset.forName("UTF8"));}public StringRedisSerializer(Charset charset) {Assert.notNull(charset, "Charset must not be null!");this.charset = charset;}@Overridepublic String deserialize(byte[] bytes) {return (bytes == null ? null : new String(bytes, charset));}//@Overridepublic byte[] serialize(Object object) {String string = JSON.toJSONString(object);if (string == null) {return null;}string = string.replace(target, replacement);return string.getBytes(charset);} }定義value的序列化與反序列化
與原Redis默認(rèn)的value序列化方法不同的是,我們選擇的是FastJson來value進(jìn)行序列化,而默認(rèn)采用Jackson2Json來盡行序列化
默認(rèn):
重寫方法:
PS:關(guān)于FastJson有很多爭議,具體感興趣的朋友可以去查查看,生產(chǎn)環(huán)境下不推薦使用fastjson,自定義JacksonJson也可以
RedisConfig的編寫
最后的RedisConfig.class,繼承了CachingConfigurerSupport類,重寫了KeyGenerator方法,也就是key的生成策略
@Configuration @ConditionalOnClass(RedisOperations.class) @EnableConfigurationProperties(RedisProperties.class) public class RedisConfig extends CachingConfigurerSupport {/*** 設(shè)置 redis 數(shù)據(jù)默認(rèn)過期時(shí)間,默認(rèn)1天* 設(shè)置@cacheable 序列化方式* @return*/@Beanpublic RedisCacheConfiguration redisCacheConfiguration(){FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig();//設(shè)置key的時(shí)效性,一般為一天就過期configuration = configuration.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer)).entryTtl(Duration.ofDays(1));//最后返回我們的配置類return configuration;}//忽略所有類型的警告@SuppressWarnings("all")//spring管理的一個(gè)bean,使用@Autowired注入到使用的類即可@Bean(name = "redisTemplate")//注冊相同類型的bean,就不會成功,保持當(dāng)前只有一個(gè)名為"redisTemplate"的bean@ConditionalOnMissingBean(name = "redisTemplate")public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplate<Object, Object> template = new RedisTemplate<>();//序列化FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer(Object.class);// value值的序列化采用fastJsonRedisSerializer,也就是上面我們編寫的fastJsonRedisSerializertemplate.setValueSerializer(fastJsonRedisSerializer);template.setHashValueSerializer(fastJsonRedisSerializer);// 全局開啟AutoType,不建議使用//ParserConfig.getGlobalInstance().setAutoTypeSupport(true);// 建議使用這種方式,小范圍指定白名單ParserConfig.getGlobalInstance().addAccept("com.hecl.zhenghe.domain");// key的序列化采用StringRedisSerializer,也就是我們剛剛編寫的StringRedisSerializertemplate.setKeySerializer(new StringRedisSerializer());template.setHashKeySerializer(new StringRedisSerializer());template.setConnectionFactory(redisConnectionFactory);return template;}/*** 自定義緩存key生成策略* 使用方法 @Cacheable(keyGenerator="keyGenerator")* @return*/@Bean@Override @Bean@Overridepublic KeyGenerator keyGenerator() {return (target, method, params) -> {StringBuilder stringBuilder = new StringBuilder();//加入類名stringBuilder.append(target.getClass().getName());//加入方法名stringBuilder.append(method.getName());//加入包名stringBuilder.append(target.getClass().getPackage());//加入查詢參數(shù)for (Object obj : params) {stringBuilder.append(JSON.toJSONString(obj).hashCode());}//最終我們生成的key就是這四種的結(jié)合,這就能保證我們生成key的唯一性return stringBuilder.toString();};} }key的過期時(shí)間通過Duration設(shè)置還可以設(shè)置多少小時(shí)、分鐘過期,下圖為該類的部分方法
當(dāng)我們重寫完上邊的方法之后,redis就會從默認(rèn)使用的序列化反序列化、key的生成策略等都會使用我們自定義的方式了,當(dāng)然還可以重寫當(dāng)緩存報(bào)錯時(shí)的處理方式,重寫下列紅框的方法即可
其實(shí)編寫RedisConfig方法就是對一些現(xiàn)有的Redis方法進(jìn)行重寫 ,讓Redis使用我們重寫的方法,使其更貼合我們的項(xiàng)目,特別是一些高并發(fā)的項(xiàng)目更要注重這些細(xì)節(jié)
轉(zhuǎn)載請標(biāo)注原作者,謝謝你們的支持,能給個(gè)小心心嗎?
完成:2021/4/08 22:38 ALiangXLogic
后記
若有錯誤,歡迎指正,互相學(xué)習(xí),相互交流
總結(jié)
以上是生活随笔為你收集整理的Springboot整合Redis(RedisConfig等工具类编写)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。