我們在進行Java項目開發時候,經常會用到Redis緩存例如數據庫里的一些信息、手機驗證碼之類的,正常寫法就會像去連mysql一樣,這種硬編碼的方式肯定是非常不合適的。
@Autowireprivate UserMapper userMapper;@Autowireprivate StringCommand stringCommand;//查詢用戶public User getUserById(Long userId) {String cacheKey = "userId_" + userId;User user=stringCommand.get(cacheKey);if(user != null) {return user;}user = userMapper.getUserById(userId);if(user != null) {stringCommand.set(cacheKey,user);return user;}//修改用戶public void updateUser(User user){userMapper.updateUser(user);String cacheKey = "userId_" + userId.getId();stringCommand.set(cacheKey , user);}//刪除用戶public void deleteUserById(Long userId){userMapper.deleteUserById(userId);String cacheKey = "userId_" + userId.getId();stringCommand.del(cacheKey);}}
有沒有啥更抽象、更優雅的方式來實現呢?
有!那就是使用Spring Cache,它是一個對緩存的抽象,即使使用的緩存不是Redis也可以。
我們看到這里最重要的是這個CacheManager,是他給我們提供了一個抽象,讓我們不用關心底層緩存技術(如 Redis、Ehcache、Caffeine)
那如何使用呢?
當然是通過Spring中最常見的方式——注釋了 !!!這也是CacheManager提供給我們的功能
在pom文件下引入對應包(spring-boot-starter-data-redis、spring-boot-starter-cache)后,在啟動類上加@EnableCaching注釋,就可以使用Spring Cache的功能了。
這里再解釋一下注釋里的value和key分別是啥:
- 首先value指定的是緩存的名稱,對應底層 Redis 的 key 前綴或 hash 名,也就是你緩存數據時存在哪個“區域”。比如下面這個示例,緩存內容都是存入名為 userCache 的緩存區。
- key則是指定緩存項在緩存區中的唯一標識。如果不指定,Spring 會默認根據方法所有參數自動生成一個 key。
// 先查緩存,有則返回緩存內容;如果沒有,執行方法,并把返回值放進緩存。
@Cacheable(value = "userCache", key = "#id")
public User getUserById(Long id) {// 方法體只會在緩存未命中時執行return userService.getUserFromDB(id);
}// 還有一種寫法,假設查詢對應id用戶為空時,這樣可以讓查詢結果不緩存
// unless表示條件成立時,不緩存,也就是說:如果方法返回結果是 null,則不緩存
@Cacheable(value = "userCache", key = "#id", unless = "#result == null")
public User getUserById(Long id) {// 方法體只會在緩存未命中時執行return userService.getUserFromDB(id);
}// 不查緩存,始終執行方法,然后將方法返回值更新到緩存中。
@CachePut(value = "userCache", key = "#user.id")
public User updateUser(User user) {// 更新數據庫return userRepository.save(user);
}// 刪除緩存中的數據,可以指定 key,也可以一次清空整個緩存。
@CacheEvict(value = "userCache", key = "#id")
// @CacheEvict(value = "userCache", allEntries = true) 這個是全部清空
public void deleteUser(Long id) {// 刪除數據庫中的數據userRepository.deleteById(id);
}
還需要強調的一點,可能你會知道RedisTemplate,在我另一篇文章也講了,這是一種給你手動操作Redis數據的方式,你可以調用.opsForXXX()來獲取數據,是一種更加靈活、精細操作Redis的方式,例如在實現分布式鎖等場景可以采用。但我們本文介紹的CacheManager是通過注釋的方式自動操作緩存的,是一種方法級的自動緩存,簡潔優雅,推薦用于 Controller/Service 層。
來個更加易懂的方式來解釋,RedisTemplate像是你手動操作數據庫的 JDBC 接口,而CacheManager則像是用 ORM 框架(比如 MyBatis)配置好后,通過注解自動執行數據庫操作。
@Configuration
@EnableCaching // 開啟Spring Cache注解支持,這樣的話就可以直接使用@@Cacheable、@CachePut、@CacheEvict等注釋來用了,當然如果你已經在主程序上標了,這里就不用寫了,@EnableCaching全局寫一次就行
public class RedisConfig {@Beanpublic RedisCacheManager redisCacheManager(RedisConnectionFactory factory, CacheTtlProperties ttlProps) {// 創建默認緩存配置:設置序列化方式、key前綴、是否緩存null值、默認過期時間等RedisCacheConfiguration defaultConfig = RedisCacheConfiguration.defaultCacheConfig().serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) // key 使用字符串序列化.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())); // value 使用 JSON 序列化// 創建一個 map 存放不同緩存區域(cacheName)對應的 TTL(過期時間)配置Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();// 從application.yml配置中讀取每個緩存區域自定義的 TTL,并生成對應的配置項for (Map.Entry<String, Long> entry : ttlProps.getTtl().entrySet()) {cacheConfigurations.put(entry.getKey(),defaultConfig.entryTtl(Duration.ofMillis(entry.getValue()))); // 將該 cacheName 設置為自定義 TTL}// 構建 RedisCacheManager:指定默認配置及每個緩存區域的個性化配置return RedisCacheManager.builder(factory).cacheDefaults(defaultConfig) // 設置默認緩存行為.withInitialCacheConfigurations(cacheConfigurations) // 設置個性化緩存行為.build(); // 構建緩存管理器}
}
然后記得在application.yml配置對應參數,例如過期時間等:
spring:redis:host: localhost # Redis 服務地址port: 6379 # Redis 端口號cache:type: redis # 使用 Redis 作為緩存實現redis:time-to-live: 1800000 # 全局默認緩存過期時間(單位:毫秒,1800000ms = 30分鐘)cache-null-values: false # 是否緩存 null,false 表示不緩存 null 值,假設想在每個地方自行判斷要不要緩存null可以不寫這行use-key-prefix: true # 是否啟用 key 前綴key-prefix: cache:: # 緩存 key 的統一前綴,也就是key都變成了cache::xxx
// 上面配置的是全局緩存過期時間,你如果還想單獨指定redis緩存空間例如userCache、orderCache的過期時間可以寫在下面,其余的空間仍然是30分鐘
custom:cache:ttl:userCache: 600000 # userCache 緩存區域的 TTL(單位:毫秒,600000ms = 10分鐘)orderCache: 3600000 # orderCache 緩存區域的 TTL(單位:毫秒,3600000ms = 1小時)