SpringBoot集成Redis及Redis使用方法
應(yīng)用背景
將一些經(jīng)常展現(xiàn)和不會(huì)頻繁變更的數(shù)據(jù),存放在存取速率更快的地方。 緩存就是一個(gè)存儲(chǔ)器,在技術(shù)選型中,常用 Redis 作為緩存數(shù)據(jù)庫(kù),可以幫我們分散掉數(shù)據(jù)庫(kù)的壓力,有了它能更好的支持并發(fā)性能,主要是在獲取資源方便性能優(yōu)化的關(guān)鍵方面。可以這樣理解redis位于數(shù)據(jù)庫(kù)和springboot框架之間,起到數(shù)據(jù)緩存的作用。
Redis簡(jiǎn)介
- Redis介紹:Redis是現(xiàn)在最受歡迎的NoSQL數(shù)據(jù)庫(kù)之一,Redis是一個(gè)使用ANSI C編寫(xiě)的開(kāi)源、包含多種數(shù)據(jù)結(jié)構(gòu)、支持網(wǎng)絡(luò)、基于內(nèi)存、可選持久性的鍵值對(duì)存儲(chǔ)數(shù)據(jù)庫(kù)。
- Redis使用場(chǎng)景:緩存系統(tǒng)(“熱點(diǎn)”數(shù)據(jù):高頻讀、低頻寫(xiě))、計(jì)數(shù)器、消息隊(duì)列系統(tǒng)、排行榜、社交網(wǎng)絡(luò)和實(shí)時(shí)系統(tǒng)
- Redis數(shù)據(jù)類(lèi)型:Redis提供的數(shù)據(jù)類(lèi)型主要分為5種自有類(lèi)型和一種自定義類(lèi)型,這5種自有類(lèi)型包括:String類(lèi)型、哈希類(lèi)型、列表類(lèi)型、集合類(lèi)型和順序集合類(lèi)型。
更新緩存模式 Cache aside這是最常用最常用的pattern了。其具體邏輯如下:
- 失效:應(yīng)用程序先從cache取數(shù)據(jù),沒(méi)有得到,則從數(shù)據(jù)庫(kù)中取數(shù)據(jù),成功后,放到緩存中。
- 命中:應(yīng)用程序從cache中取數(shù)據(jù),取到后返回。
- 更新:先把數(shù)據(jù)存到數(shù)據(jù)庫(kù)中,成功后,再讓緩存失效。
更新問(wèn)題
- 我們知道,在 springboot 1.5.x版本的默認(rèn)的Redis客戶(hù)端是 Jedis實(shí)現(xiàn)的,需要導(dǎo)入jedis依賴(lài),而springboot 2.x版本中默認(rèn)客戶(hù)端是用 lettuce實(shí)現(xiàn)的,需要導(dǎo)入spring-boot-starter-data-redis依賴(lài)。這兩種方式使用的都是 TCP協(xié)議。可以理解為:咱們通過(guò)程序是不能直接連接 Redis,得利用客戶(hù)端工具才能進(jìn)行連接。比較常用的有兩種:Jedis、Lettuce。既然 Lettuce 和 Jedis 的都是連接 Redis Server 的客戶(hù)端,那么它們有什么區(qū)別呢?
- Jedis使用直連方式連接Redis Server,在多線程環(huán)境下存在線程安全問(wèn)題, 因此需要增加連接池來(lái)解決線程安全的問(wèn)題,同時(shí)可以限制redis客戶(hù)端的數(shù)量, 但這種直連方式基于傳統(tǒng)I/O模式,是阻塞式傳輸。
- 而 Lettuce 是 一種可伸縮,線程安全,完全非阻塞的Redis客戶(hù)端,底層基于netty通信,我們知道netty是基于NIO的非阻塞通信, 天生支持高并發(fā),因此在在多線程環(huán)境下不存在線程安全問(wèn)題,一個(gè)連接實(shí)例就可以滿(mǎn)足多線程環(huán)境下的并發(fā)訪問(wèn), 當(dāng)然實(shí)例不夠的情況下也可以按需增加實(shí)例,保證伸縮性。
- 下面我們通過(guò)源碼的方式解析springboot是如何通過(guò)lettuce方式連接redis server的,以及springboot操作redis的底層原理。
?一:環(huán)境配置
1.1: 在pom.xml文件中添加依賴(lài)
這里說(shuō)說(shuō)為什么要添加 org.apache.commons 依賴(lài),如果不加,它會(huì)報(bào)錯(cuò):Caused by: java.lang.ClassNotFoundException: org.apache.commons.pool2.impl.GenericObjectPoolConfig
<dependencies> <!-- SpringBoot集成Redis的起步依賴(lài) --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <version>1.4.7.RELEASE</version> </dependency> <!--lettuce 依賴(lài)commons-pool--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.8.0</version> </dependency> </dependencies>
1.2:配置SpringBoot核心配置文件application.properties
yml文件格式
spring: redis: open: true # 是否開(kāi)啟redis緩存 true開(kāi)啟 false關(guān)閉 database: 0 host: 127.0.0.1 port: 3304 password: 123456 # 密碼(默認(rèn)為空) timeout: 6000ms # 連接超時(shí)時(shí)長(zhǎng)(毫秒) expire: 3600 #7天不過(guò)期 lettuce: pool: max-active: 100 # 連接池最大連接數(shù)(使用負(fù)值表示沒(méi)有限制) max-wait: -1ms # 連接池最大阻塞等待時(shí)間(使用負(fù)值表示沒(méi)有限制) max-idle: 20 # 連接池中的最大空閑連接 min-idle: 5 # 連接池中的最小空閑連接
properties文件格式
#Redis ##Redis數(shù)據(jù)庫(kù)索引 spring.redis.database=0 ##Redis服務(wù)器地址 spring.redis.host=127.0.0.1 ## Redis服務(wù)器連接端口 spring.redis.port=3304 ## 連接超時(shí)時(shí)間(毫秒) spring.redis.timeout=3 ## Redis服務(wù)器連接密碼(默認(rèn)為空) spring.redis.password=135246 ## 連接池中的最大連接數(shù) (使用復(fù)數(shù)則標(biāo)識(shí)沒(méi)有限制) 默認(rèn) 8 spring.redis.pool.max.active=100 ## 連接池最大阻塞等待時(shí)間(使用負(fù)值表示沒(méi)有限制)默認(rèn) -1 spring.redis.pool.max.wait=-1 ## 連接池中的最大空閑連接 默認(rèn) 8 spring.redis.pool.max.idle=20 ## 連接池中的最小空閑連接 默認(rèn) 0 spring.redis.pool.max.idle=0
二:在Config文件夾中創(chuàng)建RedisConfig配置文件類(lèi)
RedisTemplate 是 Spring 操作 Redis 的重點(diǎn)內(nèi)容。 RedisTemplate是一個(gè)強(qiáng)大的類(lèi),首先它會(huì)自動(dòng)從 RedisConnectionFactory 工廠中獲取連接,然后執(zhí)行對(duì)應(yīng)的 Redis命令,提供了redis各種操作、異常處理及序列化,支持發(fā)布訂閱,并對(duì)spring 3.1 cache進(jìn)行了實(shí)現(xiàn),在最后還會(huì)關(guān)閉 Redis 的連接。
2.1:RedisTemplate中的幾個(gè)角色:
- RedisSerializer:由于與Redis服務(wù)器的通信一定是使用字節(jié)數(shù)組完成的,所以RedisSerializer是將Java對(duì)象編碼解碼的組件
- RedisOperations:封裝了一些Redis操作
- XXXOperations:封裝了指定類(lèi)型或功能的數(shù)據(jù)的操作,如ZSetOperations
2.2:為什么要自定義序列化:
RedisTemplate操作時(shí),默認(rèn)會(huì)采用jdkSerializable序列化機(jī)制,使得插入的值在redis客戶(hù)端看來(lái)會(huì)有亂碼 類(lèi)似于: "\xac\ced\x00\x05t\x00\x03key" ,所以解決這個(gè)問(wèn)題就需要修改默認(rèn)的序列化規(guī)則。
2.2.1:Spring 中提供了以下幾個(gè)序列化器:
- Jackson2JsonRedisSerializer
- JdkSerializationRedisSerializer
- OxmSerializer
- StringRedisSerializer
- GenericToStringRedisSerializer
- GenericJackson2JsonRedisSerializer
本章使用的是StringRedisSerializer, String序列化方式。
RedisConfig 所在結(jié)構(gòu)地址:
package com.lizexin.springbootdemo.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.*; import org.springframework.data.redis.serializer.StringRedisSerializer; /** * 項(xiàng)目名稱(chēng):springboot-demo * 類(lèi)名稱(chēng):RedisConfig * 類(lèi)描述:Redis配置 * 創(chuàng)建時(shí)間:2023/08/04 * @author lzx * @version v1.0 */ @Configuration public class RedisConfig { @Autowired private RedisConnectionFactory factory; @Bean public RedisTemplate<String, Object> redisTemplate() { // 將template 泛型設(shè)置為 <String, Object> RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); // 使用 String 序列化方式,序列化 KEY。 redisTemplate.setKeySerializer(new StringRedisSerializer()); // 使用 String 序列化方式,序列化 VALUE。 redisTemplate.setValueSerializer(new StringRedisSerializer()); // 使用 String 序列化方式,序列化 HashKEY。 redisTemplate.setHashKeySerializer(new StringRedisSerializer()); // 使用 String 序列化方式,序列化 ValueKEY。 redisTemplate.setHashValueSerializer(new StringRedisSerializer()); // 配置連接工廠 redisTemplate.setConnectionFactory(factory); return redisTemplate; } /** * HashOperations * 操作 Hash 類(lèi)型數(shù)據(jù) **/ @Bean public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) { return redisTemplate.opsForHash(); } /** * HashOperations * 操作 String 類(lèi)型數(shù)據(jù) **/ @Bean public ValueOperations<String, String> valueOperations(RedisTemplate<String, String> redisTemplate) { return redisTemplate.opsForValue(); } /** * HashOperations * 操作 List 類(lèi)型數(shù)據(jù) **/ @Bean public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) { return redisTemplate.opsForList(); } /** * HashOperations * 操作 Set 類(lèi)型數(shù)據(jù) **/ @Bean public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) { return redisTemplate.opsForSet(); } /** * HashOperations * 操作 SortedSet 類(lèi)型數(shù)據(jù) **/ @Bean public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) { return redisTemplate.opsForZSet(); } }
四:封裝Redis Utils工具包
Redis工具包分為三個(gè)類(lèi)
1:RedisUtils.java Redis方法類(lèi)主要記錄對(duì)redis的一些操作,增刪改查等。
2:RedisKeys.java Redis自定義Key類(lèi),自定義配置,對(duì)redis操作時(shí)好分辨哪個(gè)key的數(shù)據(jù)
3:UserRedis.java 封裝類(lèi),將RedisUtils和RedisKey進(jìn)行封裝,用戶(hù)直接操作此類(lèi)
redis 工具包 所在結(jié)構(gòu)地址:
4.1:RedisUtils.java
package com.lizexin.springbootdemo.utils.redis; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.*; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import java.util.Collection; import java.util.Date; import java.util.List; import java.util.concurrent.TimeUnit; /** * 項(xiàng)目名稱(chēng):springboot-demo * 類(lèi)名稱(chēng):RedisUtils * 類(lèi)描述:Redis工具類(lèi) * 創(chuàng)建時(shí)間:2023/08/04 * @author lzx * @version v1.0 */ @Component public class RedisUtils { /**日志*/ private static final Logger logger = LoggerFactory.getLogger(RedisUtils.class); @Autowired private RedisTemplate<String, Object> redisTemplate; @Autowired private ValueOperations<String, String> valueOperations; @Autowired private HashOperations<String, String, Object> hashOperations; @Autowired private ListOperations<String, Object> listOperations; @Autowired private SetOperations<String, Object> setOperations; @Autowired private ZSetOperations<String, Object> zSetOperations; /**默認(rèn)過(guò)期時(shí)長(zhǎng),單位: 秒*/ public final static long DEFAULT_EXPIRE = 60 * 10; /**從配置文件獲取 默認(rèn)過(guò)期時(shí)長(zhǎng)*/ @Value("${spring.redis.expire}") public long expire; /**不設(shè)置過(guò)期時(shí)長(zhǎng) */ public final static long NOT_EXPIRE = -1; /**它可以幫助我們快速的進(jìn)行各個(gè)類(lèi)型和Json類(lèi)型的相互轉(zhuǎn)換*/ private static final ObjectMapper MAPPER = new ObjectMapper(); /**給指定key設(shè)置固定時(shí)間的有效期*/ public void expireAt(String key,Date date){ redisTemplate.expireAt(key,date); } /**根據(jù)指定的key,獲取過(guò)期時(shí)間*/ public long getExpire(String key) { return redisTemplate.getExpire(key, TimeUnit.SECONDS);} /**判斷key是否存在*/ public boolean hasKey(String key) { try { return redisTemplate.hasKey(key); } catch (Exception e) { e.printStackTrace(); return false; } } /** * 刪除緩存, @param key可以傳一個(gè)值 或多個(gè) * 該注解屏蔽某些編譯時(shí)的警告信息 * */ @SuppressWarnings("unchecked") public void delete(String... key) { if (key != null && key.length > 0) { if (key.length == 1) { redisTemplate.delete(key[0]); } else { redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(key)); } } } /**將Object值放如緩存并設(shè)置默認(rèn)時(shí)間,調(diào)用下一個(gè)方法將值轉(zhuǎn)為JSON字符串*/ public void set(String key, Object value){ set(key, value, DEFAULT_EXPIRE); } /**將Object值轉(zhuǎn)為JSON字符串放入緩存,并設(shè)置過(guò)期時(shí)長(zhǎng)*/ public void set(String key, Object value, long expire){ valueOperations.set(key, objectToJson(value)); if(expire != NOT_EXPIRE){ redisTemplate.expire(key, expire, TimeUnit.SECONDS); } } /**根據(jù)key和泛型獲取值, 調(diào)用下一個(gè)get方法*/ public <T> T get(String key, Class<T> clazz) { return get(key, clazz, NOT_EXPIRE); } /**根據(jù)key鍵獲取值并轉(zhuǎn)為對(duì)象 并重新設(shè)置過(guò)期時(shí)間*/ public <T> T get(String key, Class<T> clazz, long expire) { String value = valueOperations.get(key); if(expire != NOT_EXPIRE){ redisTemplate.expire(key, expire, TimeUnit.SECONDS); } //將Json字符串轉(zhuǎn)換為bean對(duì)象 return value == null ? null : fromJson(value, clazz); } /**根據(jù)key鍵獲取值返回為String*/ public String get(String key, long expire) { String value = valueOperations.get(key); if(expire != NOT_EXPIRE){ redisTemplate.expire(key, expire, TimeUnit.SECONDS); } return value; } /*根據(jù)key獲取值*/ public String get(String key) { return get(key, NOT_EXPIRE); } /**Object轉(zhuǎn)換為JSON字符串,在存reid的時(shí)候調(diào)用此方法*/ private String objectToJson(Object object){ if(object instanceof Integer || object instanceof Long || object instanceof Float || object instanceof Double || object instanceof Boolean || object instanceof String){ return String.valueOf(object); } return JSONUtil.toJsonStr(object); } /**JSON字符串, 轉(zhuǎn)成javaBean對(duì)象*/ private <T> T fromJson(String json, Class<T> clazz){ return JSONUtil.toBean(json, clazz); //return JSON.parseObject(json,clazz); } /**將JsonObject轉(zhuǎn)為實(shí)體類(lèi)對(duì)象,轉(zhuǎn)換異常將被拋出*/ public static <T> T fromJsonToBean(JSONObject json, Class<T> beanClass) { return null == json ? null : json.toBean(beanClass); } /**將元素添加到指定set集合中*/ public void addToSet(String key,String member){ redisTemplate.opsForSet().add(key,member); } /**批量添加到指定set集合中*/ public void addBatchToSet(String key,List<String> memberList) { Object[] members = memberList.toArray(); redisTemplate.opsForSet().add(key,members); } /**統(tǒng)計(jì)指定set的長(zhǎng)度,當(dāng)指定key的set不存在時(shí),返回null*/ public Long countForSet(String key){ return redisTemplate.opsForSet().size(key); } /**只有當(dāng)key不存在時(shí)才設(shè)置key的值,并返回true;當(dāng)key存在時(shí)不修改key的值,并返回false。*/ public Boolean isMember(String value){ return setOperations.isMember(RedisKeys.AutoKey,value); } /**向集合添加值并設(shè)置過(guò)期時(shí)間*/ public Long addSetDataExpire(String value,String name,long expire){ Long addSet = setOperations.add(name,value); if(expire != NOT_EXPIRE){ redisTemplate.expire(name, expire, TimeUnit.SECONDS); } return addSet; } /**向右邊批量添加元素*/ public boolean addrightPushAll(String key, List<Object> value) { boolean var4; try { this.redisTemplate.opsForList().rightPushAll(key, value); boolean var3 = true; return var3; } catch (Exception var8) { logger.error("", var8); var4 = false; } finally { this.close(); } return var4; } /** * 獲取泛型的Collection Type * @param collectionClass 泛型的Collection * @param elementClasses 元素類(lèi) * @return JavaType Java類(lèi)型 * @since 1.0*/ public static JavaType getCollectionType(Class<?> collectionClass, Class<?>... elementClasses) { return MAPPER.getTypeFactory().constructParametricType(collectionClass, elementClasses); } private void close() { RedisConnectionUtils.unbindConnection(this.redisTemplate.getConnectionFactory()); } }
4.2:RedisKeys.java
package com.zhangtao.moguding.province.utils.redis; /** * 項(xiàng)目名稱(chēng):user-center-service * 類(lèi)名稱(chēng):RedisKeys * 類(lèi)描述:redis所有的key * 創(chuàng)建時(shí)間:2023/7/27 * * @author lzx * @version v1.0 */ public class RedisKeys { //最大蘑菇號(hào)的key public final static String MAX_MOGUNO_KEY = "moguding:user:max_mogu_no"; //短信驗(yàn)證碼的key public static String getSmsCodeKey(String key,Integer type){ return "moguding:user:smsCode:" + key+":"+type; } //權(quán)限列表 public final static String PERMISSIONS_USERAUTH_KEY = "moguding:permissions:permissions_userauth_list:"; //參數(shù)配置 public static String getUserConfigKey(String... key){ return "moguding:user:config:" + key; } //用戶(hù)Token public final static String AUTH_TOKEN_KEY = "moguding:user:authToken:"; //authtoken的key web端 public static String getAuthToken(String type,String userid){ if("web".equals(type)){ return AUTH_TOKEN_KEY+type+":" + userid; }else { return getAuthToken(userid); } } //authtoken的key app端 public static String getAuthToken(String userid){ return AUTH_TOKEN_KEY+ userid; } //緩存活躍蘑菇號(hào)key的 public final static String ACTIVE_MOGU_NO= "moguding:user:active:"; //緩存今日學(xué)校簽到排行榜 public final static String SCHOOL_SIGN_RANK= "moguding:school:sign:rank"; //學(xué)校統(tǒng)計(jì)(實(shí)時(shí)發(fā)送【考勤,上崗,周報(bào)】) public final static String SCHOOL_COUNT= "moguding.school.count"; //自動(dòng)報(bào)告key public final static String AutoKey = "autoReport_set"; //30天最后一次考勤 public final static String LastSign = "moguding.last.sign"; //省平臺(tái)基礎(chǔ)數(shù)據(jù) public static String getProvinceConfigKey(String key){ return "moguding:province:config:" + key; } }
4.3:UserRedis.java
package com.zhangtao.moguding.province.utils.redis; import com.zhangtao.moguding.province.entity.UserConfigEntity; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.List; /** * 項(xiàng)目名稱(chēng):user-center-service * 類(lèi)名稱(chēng):UserRedis * 類(lèi)描述:UserRedis * 創(chuàng)建時(shí)間:2019/5/27 * @author lzx * @version v1.0 */ /* *將RedisUtils的set方法和RedisKeys的key 封裝到一起 * */ @Component public class UserRedis { @Autowired private RedisUtils redisUtils; public void set(String key,String value) { if(key == null){ return ; } String redisKey = RedisKeys.getUserConfigKey(key); redisUtils.set(redisKey, value,redisUtils.expire); } public void delete(String key) { if(key == null){ return ; } String redisKey = RedisKeys.getUserConfigKey(key); redisUtils.delete(redisKey); } public String get(String key){ if(key == null){ return null; } String redisKey = RedisKeys.getUserConfigKey(key); return redisUtils.get(redisKey); } public UserConfigEntity getObject(String key){ if(key == null){ return null; } String redisKey = RedisKeys.getUserConfigKey(key); return redisUtils.get(redisKey, UserConfigEntity.class); } //向Redis添加值,設(shè)置默認(rèn)過(guò)期時(shí)長(zhǎng) 7天, set方法將value進(jìn)行序列化(轉(zhuǎn)為JSON字符串) /* public void set(String key,Object value) { if(key == null){ return ; } String redisKey = RedisKeys.getRedisTestKey(key); redisUtils.set(redisKey, value,redisUtils.expire); } public <T> T get(String key, Class<T> clazz){ if(key == null){ return null; } String redisKey = RedisKeys.getRedisTestKey(key); return redisUtils.get(redisKey,clazz); } //判斷Redis測(cè)試key是否存在 public boolean hasKey(String key){ if(key == null){ return false; } String redisKey = RedisKeys.getRedisTestKey(key); return redisUtils.hasKey(redisKey); };*/ //將今日活躍用戶(hù)的蘑菇號(hào)批量存進(jìn)redis指定Set集合中 public void addUserMoguNosToSet(List<String> moguNos){ redisUtils.addBatchToSet(RedisKeys.ACTIVE_MOGU_NO,moguNos); } //將今日活躍用戶(hù)的蘑菇號(hào)緩存Set集合清空 public void deleteForCacheUserMoguNo(){ redisUtils.delete(RedisKeys.ACTIVE_MOGU_NO); } //判斷Redis測(cè)試key是否存在 public boolean hasKey(String key){ if(key == null){ return false; } String redisKey = RedisKeys.getProvinceConfigKey(key); return redisUtils.hasKey(redisKey); }; //從redis中統(tǒng)計(jì)活躍用戶(hù)數(shù)量 public int countActiveUser(){ Long Lnum = redisUtils.countForSet(RedisKeys.ACTIVE_MOGU_NO); if(Lnum==null){ return 0; }else { return Lnum.intValue(); } } //省級(jí)平臺(tái)獲取redisKey public String getProvinceKey(String key){ if(key == null){ return null; } String redisKey = RedisKeys.getProvinceConfigKey(key); return redisUtils.get(redisKey); } //省級(jí)平臺(tái)向redis插入數(shù)據(jù) public void setProvinceDataToRedis(String key,String list){ String keys = RedisKeys.getProvinceConfigKey(key); //配置文件默認(rèn)保留時(shí)長(zhǎng) redisUtils.setRedis(keys,list,redisUtils.expire); } }
五:流程實(shí)現(xiàn)
模擬SpringBoot項(xiàng)目結(jié)構(gòu)調(diào)用
1.RedisTestController
package com.lizexin.springbootdemo.Controller; import com.lizexin.springbootdemo.entity.Item; import com.lizexin.springbootdemo.service.RedisTestService; import com.zhangtao.common.dto.response.BaseResponse; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @program: springboot-demo * @author: lzx * @Time: 2023/8/9 16:35 * @description: Redis測(cè)試接口 * @version: v1.0 */ @RestController @Api(tags = {"Redis測(cè)試接口"},produces = "RedisTest_controller") @RequestMapping("/redis") public class RedisTestController { @Autowired RedisTestService redisTestService; private Logger logger = LoggerFactory.getLogger(RedisTestController.class); @ApiOperation(value = "Redis測(cè)試,將對(duì)象插入緩存",notes = "") @RequestMapping("/v1/test") public BaseResponse redisInsertBeanController(@RequestBody Item item){ return redisTestService.redisInsertBeanService(item); } @ApiOperation(value = "Redis測(cè)試,將List插入緩存",notes = "") @RequestMapping("/v2/test") public BaseResponse redisInsertListController(@RequestBody Item item){ return redisTestService.redisInsertListService(item); } @ApiOperation(value = "Redis測(cè)試,將Map<String,Set<String>>插入緩存,取出來(lái)轉(zhuǎn)Map<String,JsonArry>",notes = "") @RequestMapping("/v3/test") public BaseResponse redisInsertMapController(@RequestBody Item item){ return redisTestService.redisInsertMapService(item); } }
2.RedisTestService
package com.lizexin.springbootdemo.service; import com.lizexin.springbootdemo.entity.Item; import com.zhangtao.common.dto.response.BaseResponse; /** * @program: springboot-demo * @author: lzx * @Time: 2023/8/9 22:55 * @description: Redis測(cè)試接口Service * @version: v1.0 */ public interface RedisTestService { BaseResponse redisInsertBeanService( Item item); BaseResponse redisInsertListService( Item item); BaseResponse redisInsertMapService( Item item); }
3.RedisTestServiceImpl
package com.lizexin.springbootdemo.service.impl; import cn.hutool.json.JSONUtil; import com.alibaba.fastjson.JSONArray; import com.lizexin.springbootdemo.entity.Item; import com.lizexin.springbootdemo.service.RedisTestService; import com.lizexin.springbootdemo.utils.*; import com.lizexin.springbootdemo.utils.redis.UserRedis; import com.zhangtao.common.dto.response.BaseResponse; import com.zhangtao.common.dto.response.ObjectResponse; import io.swagger.annotations.ApiOperation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.*; import java.util.stream.Collectors; /** * @program: springboot-demo * @author: lzx * @Time: 2023/08/9 22:58 * @description: test * @version: v1.0 */ @Service public class RedisTestServiceImpl implements RedisTestService { @Autowired UserRedis userRedis; private static final Logger logger = LoggerFactory.getLogger(RedisTestServiceImpl.class); @Override @ApiOperation( "通過(guò)key得到值并重新設(shè)置過(guò)期時(shí)間,若值不存在則重新插入緩存。"+ "set方法封裝了 JSONUtil.toJsonStr"+ "get帶泛型的方法封裝了JSONUtil.toBean " ) public BaseResponse redisInsertBeanService(Item item) { String redisKey= "redisInsertBeanService"; System.out.println(redisKey); //判斷key值是否存在,如果存在則優(yōu)先取緩存 if (userRedis.hasKey(redisKey)){ Item jsonString= userRedis.get(redisKey,Item.class); logger.info("存在值"); logger.info(jsonString.toString()); return ObjectResponse.resObj(jsonString); }else{ //不存在則緩存 Item item1= AttributeData.list9(); logger.info("不存在值 插入"); userRedis.set(redisKey,item1); return ObjectResponse.ok(); } } @Override @ApiOperation("get方法不帶泛型默認(rèn)返回Json字符串,需要自行反序列化") public BaseResponse redisInsertListService(Item item) { //通過(guò)key得到值, String redisKey = "redisInsertListService"; System.out.println(redisKey); //判斷key值是否存在,如果存在則優(yōu)先取緩存 if (userRedis.hasKey(redisKey)){ List<Item> list = JSONArray.parseArray(userRedis.get(redisKey),Item.class); logger.info("存在值"); logger.info(list.toString()); return ObjectResponse.resObj(list); }else{ //不存在則緩存 List<Item> list= AttributeData.list8(); logger.info("不存在值 插入"); userRedis.set(redisKey,list); return ObjectResponse.ok(); } } @Override @ApiOperation("") public BaseResponse redisInsertMapService(Item item) { //通過(guò)key得到值, String redisKey= "redisInsertMapService"; System.out.println(redisKey); //判斷key值是否存在,如果存在則優(yōu)先取緩存 if (userRedis.hasKey(redisKey)){ String jsonString= userRedis.get(redisKey); //可以通過(guò)JSonString轉(zhuǎn)對(duì)象方法把Vlue值從Set轉(zhuǎn)為JsonArray Map<String,JSONArray> arrayMap= JSONUtil.toBean(jsonString,Map.class); logger.info("存在值"); logger.info(arrayMap.toString()); return ObjectResponse.resObj(arrayMap); }else{ //不存在則緩存 List<Item> list= AttributeData.list10(); //根據(jù)key轉(zhuǎn)map,之后將Value換成set集合 //將集合添加至Map 指定參數(shù)作為key Map<String,List<Item>> map = new HashMap(); Map<String,Set<String>>setMap =new HashMap<>(); map = list.stream().collect(Collectors.groupingBy(Item::getName,Collectors.toList())); for (Map.Entry<String,List<Item>> key:map.entrySet()){ Set<String> set = new HashSet<>(); key.getValue().forEach(c->{ set.add(c.getValue()); }); setMap.put(key.getKey(), set); } logger.info("不存在值 插入"); userRedis.set(redisKey,setMap); return ObjectResponse.ok(); } } }
4.AttributeData
package com.lizexin.springbootdemo.utils; import com.lizexin.springbootdemo.dto.CommonInterfaceDto; import com.lizexin.springbootdemo.dto.InformationDatasDto; import com.lizexin.springbootdemo.dto.export.ExportGxySchoolFlowDto; import com.lizexin.springbootdemo.entity.Item; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @program: springboot-demo * @author: lzx * @Time: 2023/8/9 22:55 * @description: 常用數(shù)據(jù)集 * @version: v1.0 */ public class AttributeData { public static List<Map<String,Object>> list1 (){ //構(gòu)建List集合1 List<Map<String,Object>> list1 = new ArrayList<>(); Map<String,Object> data=new HashMap<>(); data.put("userId","100001"); data.put("userName","唐僧"); list1.add(data); data=new HashMap<>(); data.put("userId","100002"); data.put("userName","八戒"); list1.add(data); data=new HashMap<>(); data.put("userId","100003"); data.put("userName","悟空"); list1.add(data); data=new HashMap<>(); data.put("userId","100004"); data.put("userName","沙僧"); list1.add(data); return list1; } public static List<Map<String,Object>> list2(){ Map<String,Object> data=new HashMap<>(); List<Map<String,Object>> list2 = new ArrayList<>(); data=new HashMap<>(); data.put("userId","100001"); data.put("gender","男"); data.put("age",20); list2.add(data); data=new HashMap<>(); data.put("userId","100002"); data.put("gender","雄"); data.put("age",1000); list2.add(data); data=new HashMap<>(); data.put("userId","100003"); data.put("gender","雄"); data.put("age",600); list2.add(data); data=new HashMap<>(); data.put("userId","100004"); data.put("gender","男"); data.put("age",800); list2.add(data); return list2; } public static List<InformationDatasDto> list3(){ List<InformationDatasDto> list = new ArrayList<>(); InformationDatasDto info = new InformationDatasDto(); info.setStudentId("10000"); info.setStudent_name("張三"); list.add(info); info = new InformationDatasDto(); info.setStudentId("10001"); info.setStudent_name("里李四"); list.add(info); info = new InformationDatasDto(); info.setStudentId("10002"); info.setStudent_name("王五"); list.add(info); info = new InformationDatasDto(); info.setStudentId("10003"); info.setStudent_name("趙六"); list.add(info); info = new InformationDatasDto(); info.setStudentId("10004"); info.setStudent_name("馬七"); list.add(info); return list; } public static List<InformationDatasDto> list4(){ List<InformationDatasDto> list = new ArrayList<>(); InformationDatasDto info = new InformationDatasDto(); info.setStudentId("北京"); info.setStudent_name("張三"); list.add(info); info = new InformationDatasDto(); info.setStudentId("北京省"); info.setStudent_name("里李四"); list.add(info); info = new InformationDatasDto(); info.setStudentId("湖北省"); info.setStudent_name("王五"); list.add(info); info = new InformationDatasDto(); info.setStudentId("湖北"); info.setStudent_name("趙六"); list.add(info); info = new InformationDatasDto(); info.setStudentId("海南"); info.setStudent_name("馬七"); list.add(info); return list; } public static List<ExportGxySchoolFlowDto> list5(){ List<ExportGxySchoolFlowDto> list = new ArrayList<>(); ExportGxySchoolFlowDto info = new ExportGxySchoolFlowDto(); info.setSchoolName("齊齊哈爾大學(xué)"); info.setDatas("黑龍江省"); info.setValue(10); list.add(info); info = new ExportGxySchoolFlowDto(); info.setSchoolName("齊齊哈爾大學(xué)"); info.setDatas("黑龍江"); info.setValue(20); list.add(info); info = new ExportGxySchoolFlowDto(); info.setSchoolName("齊齊哈爾大學(xué)"); info.setDatas("黑龍江省哈爾濱市"); info.setValue(20); list.add(info); info = new ExportGxySchoolFlowDto(); info.setSchoolName("齊齊哈爾大學(xué)"); info.setDatas("甘肅省"); info.setValue(20); list.add(info); info = new ExportGxySchoolFlowDto(); info.setSchoolName("哈爾濱大學(xué)"); info.setDatas("黑龍江"); info.setValue(20); list.add(info); info = new ExportGxySchoolFlowDto(); info.setSchoolName("武漢職業(yè)大學(xué)"); info.setDatas("北京市"); info.setValue(10); list.add(info); info = new ExportGxySchoolFlowDto(); info.setSchoolName("黑河市大學(xué)"); info.setDatas("北京"); info.setValue(10); list.add(info); return list; } public static List<CommonInterfaceDto.ItemBatchDataDto> list6(){ List<CommonInterfaceDto.ItemBatchDataDto> list =new ArrayList<>(); CommonInterfaceDto.ItemBatchDataDto item1 =new CommonInterfaceDto.ItemBatchDataDto(); item1.setSchoolName("雙高校"); item1.setData(10); item1.setBatchName("19年"); list.add(item1); CommonInterfaceDto.ItemBatchDataDto item2 =new CommonInterfaceDto.ItemBatchDataDto(); item2.setSchoolName("雙高校"); item2.setData(20); item2.setBatchName("20年"); list.add(item2); CommonInterfaceDto.ItemBatchDataDto item3 =new CommonInterfaceDto.ItemBatchDataDto(); item3.setSchoolName("雙高校"); item3.setData(30); item3.setBatchName("21年"); list.add(item3); CommonInterfaceDto.ItemBatchDataDto item4 =new CommonInterfaceDto.ItemBatchDataDto(); item4.setSchoolName("雙高校"); item4.setData(40); item4.setBatchName("22年"); list.add(item4); return list; } public static List<CommonInterfaceDto.ItemBatchDataDto> list7(){ List<CommonInterfaceDto.ItemBatchDataDto> list =new ArrayList<>(); CommonInterfaceDto.ItemBatchDataDto item1 =new CommonInterfaceDto.ItemBatchDataDto(); item1.setSchoolName("鄭州經(jīng)貿(mào)學(xué)院"); item1.setData(60); item1.setBatchName("19年"); list.add(item1); CommonInterfaceDto.ItemBatchDataDto item2 =new CommonInterfaceDto.ItemBatchDataDto(); item2.setSchoolName("鄭州經(jīng)貿(mào)學(xué)院"); item2.setData(10); item2.setBatchName("22年"); list.add(item2); return list; } public static List<Item> list8(){ List<Item> list =new ArrayList<>(); Item item1 =new Item(); item1.setName("計(jì)算機(jī)"); item1.setValue(10); list.add(item1); Item item2 =new Item(); item2.setName("會(huì)計(jì)"); item2.setValue(20); list.add(item2); Item item3 =new Item(); item3.setName("銷(xiāo)售"); item3.setValue(30); list.add(item3); Item item4 =new Item(); item4.setName("老師"); item4.setValue(40); list.add(item4); Item item5 =new Item(); item5.setName("醫(yī)學(xué)"); item5.setValue(40); list.add(item5); Item item6 =new Item(); item6.setName("農(nóng)業(yè)"); item6.setValue(94); list.add(item6); Item item7 =new Item(); item7.setName("工程"); item7.setValue(100); list.add(item7); return list; } public static Item list9(){ Item item7 =new Item(); item7.setName("工程"); item7.setValue(100); return item7; } public static List<Item> list10(){ List<Item> list =new ArrayList<>(); Item item1 =new Item(); item1.setName("河南省"); item1.setValue("鄭州市"); list.add(item1); Item item2 =new Item(); item2.setName("河南省"); item2.setValue("洛陽(yáng)市"); list.add(item2); Item item3 =new Item(); item3.setName("河南省"); item3.setValue("開(kāi)封市"); list.add(item3); Item item4 =new Item(); item4.setName("湖北省"); item4.setValue("武漢市"); list.add(item4); Item item5 =new Item(); item5.setName("湖北省"); item5.setValue("襄陽(yáng)市"); list.add(item5); Item item6 =new Item(); item6.setName("湖北省"); item6.setValue("潛江市"); list.add(item6); Item item7 =new Item(); item7.setName("湖北省"); item7.setValue("荊州市"); list.add(item7); Item item8 =new Item(); item8.setName("北京"); item8.setValue("北京市"); list.add(item8); return list; } }
調(diào)用結(jié)果:
相關(guān)文章
啟動(dòng)springboot應(yīng)用因未配置數(shù)據(jù)庫(kù)報(bào)錯(cuò)的解決方案
這篇文章主要介紹了啟動(dòng)springboot應(yīng)用因未配置數(shù)據(jù)庫(kù)報(bào)錯(cuò)的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11Spring?Boot集成Milvus快速入門(mén)demo示例詳解
Milvus是一種高性能向量數(shù)據(jù)庫(kù),支持從筆記本到大型分布式系統(tǒng)的多環(huán)境運(yùn)行,它以開(kāi)源和云服務(wù)形式提供,是LFAI & Data Foundation的項(xiàng)目,采用Apache 2.0許可,Milvus特別支持高并行化和解耦的系統(tǒng)架構(gòu),使其能夠隨數(shù)據(jù)增長(zhǎng)而擴(kuò)展,支持各種復(fù)雜搜索功能,滿(mǎn)足企業(yè)級(jí)AI應(yīng)用需求2024-09-09Spring、SpringMvc和SpringBoot的區(qū)別及說(shuō)明
Spring框架提供了全面的Java開(kāi)發(fā)解決方案,核心包括IOC和AOP,SpringMvc作為其中的WEB層開(kāi)發(fā)框架,通過(guò)復(fù)雜的XML配置管理前端視圖和后臺(tái)邏輯,SpringBoot則簡(jiǎn)化了配置,專(zhuān)注于微服務(wù)接口開(kāi)發(fā),支持嵌入式服務(wù)器,提高了開(kāi)發(fā)效率2024-10-10Spring Boot + Kotlin整合MyBatis的方法教程
前幾天由于工作需要,便開(kāi)始學(xué)習(xí)了kotlin,java基礎(chǔ)扎實(shí)學(xué)起來(lái)也還算比較快,對(duì)于kotlin這個(gè)編程語(yǔ)言自然是比java有趣一些,下面這篇文章主要給大家介紹了關(guān)于Spring Boot + Kotlin整合MyBatis的方法教程,需要的朋友可以參考下。2018-01-01RocketMQ NameServer保障數(shù)據(jù)一致性實(shí)現(xiàn)方法講解
這篇文章主要介紹了RocketMQ NameServer保障數(shù)據(jù)一致性實(shí)現(xiàn)方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12springboot3+r2dbc響應(yīng)式編程實(shí)踐
本文主要介紹了springboot3+r2dbc響應(yīng)式編程實(shí)踐,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02