SpringBoot整合SpringCache緩存的實(shí)現(xiàn)示例
1.介紹
Spring Cache 提供了 Cache 和 CacheManager 接口來統(tǒng)一管理不同的緩存技術(shù)。Cache 是緩存的抽象,CacheManager 負(fù)責(zé)管理多個(gè) Cache 實(shí)例。Spring Cache 支持多種緩存實(shí)現(xiàn),包括:
- ConcurrentHashMap:默認(rèn)的緩存實(shí)現(xiàn),適用于簡單的本地緩存。
- Redis:基于 Redis 的分布式緩存,適用于高并發(fā)場(chǎng)景。
- Ehcache:符合 JSR-107 標(biāo)準(zhǔn)的緩存實(shí)現(xiàn),支持二級(jí)緩存。
- Caffeine:基于 Java 8 的高性能緩存庫,適用于需要高性能的場(chǎng)景。
- JSR-107:支持 JSR-107 標(biāo)準(zhǔn)的緩存實(shí)現(xiàn)。
2.SpringBoot整合
本文基于springboot2.7版本測(cè)試
1.導(dǎo)入xml依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.配置yml
spring:
cache:
cache-names: user
type: redis
redis:
#緩存前綴
key-prefix: moshangshang_
#是否啟用緩存統(tǒng)計(jì)信息。
enable-statistics: false
#是否允許緩存 null 值。
cache-null-values: true
#寫入 Redis 時(shí)是否使用 key prefix。
use-key-prefix: true
redis:
port: 6379
host: 127.0.0.1
password: root
lettuce:
pool:
max-active: 20 #連接池最大連接數(shù)(使用負(fù)值表示沒有限制)
max-idle: 8 #連接池中的最大空閑連接
min-idle: 5 # 連接池中的最小空閑連接
timeout: 6000 #連接超時(shí)時(shí)長(毫秒)
如果cache-null-values:屬性啟用不能緩存null值,則緩存null時(shí)會(huì)拋出下方異常
java.lang.IllegalArgumentException: Cache 'user' does not allow 'null' values. Avoid storing null via '@Cacheable(unless="#result == null")' or configure RedisCache to allow 'null' via RedisCacheConfiguration.
cache-names屬性說明:
用于管理全的緩存key的全局配置,多個(gè)用逗號(hào)分割,比如 cache-names: user; use-key-prefix: false,則表示 @Cacheable(cacheNames = "user“)之類的注解不會(huì)使用key-prefix指定的緩存前綴,未配置的緩存名稱則采用默認(rèn)全局配置
3.使用@EnableCaching啟用SpringCache
@SpringBootApplication
@EnableCaching
public class CacheApplication {
public static void main(String[] args) {
SpringApplication.run(CacheApplication.class, args);
}
}
4.@Cacheable
@Cacheable 用于標(biāo)記方法或類,表示該方法的返回值可以被緩存。
當(dāng)方法執(zhí)行前,Spring 會(huì)檢查緩存中是否存在相同 key 的緩存元素,如果存在則直接返回,否則執(zhí)行方法并將結(jié)果存入緩存。
@Cacheable 的方法必須為 public:如果方法不是 public 的,Spring 無法通過代理來訪問緩存。
- value 或 cacheNames:指定緩存的名稱,可以是單個(gè)字符串或字符串?dāng)?shù)組。
- key:指定緩存的鍵,可以使用 SpEL 表達(dá)式來定義。
- condition:指定緩存的條件,只有當(dāng)條件為 true 時(shí),才會(huì)從緩存中獲取結(jié)果。
- unless:指定不將結(jié)果放入緩存的條件,即使結(jié)果存在,也不會(huì)被緩存。
- sync:是否使用同步方式獲取緩存,避免多個(gè)線程同時(shí)執(zhí)行方法。
- 在某些場(chǎng)景下,需要確保緩存和數(shù)據(jù)庫的一致性,可以使用 @Cacheable 的 sync 屬性來啟用同步更新。且在多線程環(huán)境下確保只有一個(gè)線程執(zhí)行查詢。
/**
* 根據(jù)id查詢用戶信息
* 生成的key為moshangshang_user::38
*/
@GetMapping("/query/{id}")
@Cacheable(cacheNames = "user",key = "#id",unless = "#result == null")
public User getById(@PathVariable Long id){
return userService.getById(id);
}
5.@CachePut
@CachePut 用于標(biāo)記方法,表示每次調(diào)用該方法時(shí)都會(huì)執(zhí)行并存入緩存。
它總是會(huì)執(zhí)行方法,并將結(jié)果添加到緩存中,不會(huì)檢查緩存中是否存在相同 key 的緩存元素。
- value 或 cacheNames:指定緩存的名稱,可以是單個(gè)字符串或字符串?dāng)?shù)組。
- key:指定緩存的鍵,可以使用 SpEL 表達(dá)式來定義。
- condition:指定緩存的條件,只有當(dāng)條件為 true 時(shí),才會(huì)從緩存中獲取結(jié)果。
- unless:指定不將結(jié)果放入緩存的條件,即使結(jié)果存在,也不會(huì)被緩存。
@PostMapping("/save")
@CachePut( key = "#user.id")
public User updateUser(User user) {
userService.saveOrUpdate(user);
return user;
}
6.@CacheEvict
@CacheEvict 用于標(biāo)記方法,表示該方法執(zhí)行時(shí)會(huì)清除緩存中的數(shù)據(jù)。
@CacheEvict 在方法執(zhí)行期間拋出異常不會(huì)清空緩存:如果方法執(zhí)行過程中拋出異常,@CacheEvict 的 allEntries 屬性不會(huì)生效。
它可以用于刪除緩存中的所有鍵值對(duì),也可以用于清除特定的 key。
- value 或 cacheNames:指定緩存的名稱,可以是單個(gè)字符串或字符串?dāng)?shù)組。
- key:指定緩存的鍵,可以使用 SpEL 表達(dá)式來定義。
- condition:指定緩存的條件,只有當(dāng)條件為 true 時(shí),才會(huì)從緩存中獲取結(jié)果。
- beforeInvocation:是否在方法執(zhí)行前清除緩存,為 true 時(shí)在方法執(zhí)行前清除緩存。
- allEntries:是否清除所有緩存條目,為 true 時(shí)清除所有緩存。
/**
* @CacheEvict 在方法執(zhí)行期間拋出異常不會(huì)清空緩存:如果方法執(zhí)行過程中拋出異常,@CacheEvict 的 allEntries 屬性不會(huì)生效。
* @CacheEvict 用于標(biāo)記方法,表示該方法執(zhí)行時(shí)會(huì)清除緩存中的數(shù)據(jù)。
* 它可以用于刪除緩存中的所有鍵值對(duì),也可以用于清除特定的 key。
* value 或 cacheNames:指定要清除的緩存名稱。
* key:指定要清除的緩存鍵,可以使用 SpEL 表達(dá)式來定義。
* allEntries:是否清除所有緩存條目,為 true 時(shí)清除所有緩存。
* beforeInvocation:是否在方法執(zhí)行前清除緩存,為 true 時(shí)在方法執(zhí)行前清除緩存。
* condition:指定清除緩存的條件,只有當(dāng)條件為 true 時(shí),才會(huì)清除緩存。
*/
@GetMapping("/delete/{id}")
@CacheEvict(key = "#id", allEntries = false)
public void deleteUser(@PathVariable Long id) {
userService.removeById(id);
}
7. @Caching
@Caching 是一個(gè)組合注解,可以同時(shí)應(yīng)用多個(gè)其他注解,表示該方法會(huì)同時(shí)執(zhí)行 @Cacheable、@CachePut 和 @CacheEvict 的操作。
@GetMapping("/save/caching")
@Caching(
cacheable = @Cacheable( key = "#user.id"),
put = @CachePut( key = "#user.id"),
evict = @CacheEvict( key = "#user.id")
)
public User saveUser(User user) {
userService.save(user);
return user;
}
8.@CacheConfig
@CacheConfig 用于在類上設(shè)置公共的緩存配置,避免在每個(gè)方法上重復(fù)配置。
/**
* @CacheConfig 用于在類上設(shè)置公共的緩存配置,避免在每個(gè)方法上重復(fù)配置。
*/
@RestController
@AllArgsConstructor
@RequestMapping("cache")
@CacheConfig(cacheNames = "user")
public class CacheController {
private final IUserService userService;
@GetMapping("/query/{id}")
@Cacheable(key = "#id",unless = "#result == null")
public User getById(@PathVariable Long id){
return userService.getById(id);
}
}
3.其他屬性配置
1.keyGenerator屬性
keyGenerator 屬性用于指定默認(rèn)的鍵生成器(Key Generator)。如果在方法上未顯式指定 key 屬性,則使用該屬性值作為默認(rèn)的鍵生成器。
1.配置生成器
@Configuration
public class CacheConfig {
@Bean(name = "customKeyGenerator")
public KeyGenerator keyGenerator() {
return (target, method, params) -> method.getName() + "[" + Arrays.asList(params) + "]";
}
}
等同于
@Bean(name = "customKeyGenerator")
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
return method.getName() + "[" + Arrays.asList(params) + "]";
}
};
}
2.使用
生成的key為moshangshang_user::getById[[39]]
@Cacheable(unless = "#result == null",keyGenerator = "customKeyGenerator")
public User getById(@PathVariable Long id){
return userService.getById(id);
}
2.cacheManager屬性
cacheManager 屬性用于指定當(dāng)前使用的 CacheManager 實(shí)現(xiàn)類的名稱。CacheManager 是 Spring 定義的一個(gè)接口,用于管理緩存(Cache)的創(chuàng)建和配置。Spring 提供了多種 CacheManager 的實(shí)現(xiàn),例如 ConcurrentMapCacheManager、EhCacheCacheManager、CaffeineCacheManager 等。通過設(shè)置 cacheManager 屬性,可以指定使用哪種緩存管理器來管理緩存。
創(chuàng)建自定義的緩存管理器
@Configuration
public class CacheConfig {
@Bean
public ConcurrentMapCacheManager mapCacheManager() {
return new ConcurrentMapCacheManager("user-map","user");
}
}
cacheManager() 方法返回了一個(gè) ConcurrentMapCacheManager 實(shí)例,并且指定了緩存名稱。這個(gè) CacheManager 將被用于管理名為 指定的緩存。
/**
* 執(zhí)行的是mapCacheManager的緩存管理器
*/
@GetMapping("/manger/map/query/{id}")
@Cacheable(cacheManager = "mapCacheManager")
public User getRedisMangerById(@PathVariable Long id){
return userService.getById(id);
}
3.cacheResolver屬性
cacheResolver 屬性用于指定一個(gè)自定義的 CacheResolver 實(shí)現(xiàn)。默認(rèn)情況下,Spring 使用 SimpleCacheResolver 來解析緩存操作。通過自定義 CacheResolver,可以實(shí)現(xiàn)更復(fù)雜的緩存邏輯,例如根據(jù)方法名動(dòng)態(tài)選擇緩存名稱或緩存管理器。
- cacheManager 和 cacheResolver 是互斥的:如果同時(shí)指定了 cacheManager 和 cacheResolver,Spring 會(huì)拋出異常,因?yàn)?CacheResolver 的實(shí)現(xiàn)會(huì)忽略自定義的 CacheManager 。
- 自定義 CacheResolver 的靈活性:通過自定義 CacheResolver,可以實(shí)現(xiàn)更靈活的緩存管理,例如根據(jù)方法名、參數(shù)或上下文動(dòng)態(tài)選擇緩存名稱或緩存管理器 。
- Spring 4.1 及以上版本:從 Spring 4.1 開始,@Cacheable、@CachePut、@CacheEvict 等注解的 value 屬性不再是強(qiáng)制性的,因?yàn)?CacheResolver 可以提供緩存名稱信息
自定義緩存解析器
getById方法取user緩存名稱下數(shù)據(jù),其他取user-map下數(shù)據(jù)
public class CustomCacheResolver implements CacheResolver {
private final ConcurrentMapCacheManager concurrentMapCacheManager;
private final RedisCacheManager redisCacheManager;
public CustomCacheResolver(ConcurrentMapCacheManager concurrentMapCacheManager,
RedisCacheManager redisCacheManager) {
this.concurrentMapCacheManager = concurrentMapCacheManager;
this.redisCacheManager = redisCacheManager;
}
@Override
public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) {
Collection<Cache> caches = new ArrayList<>();
if (context.getTarget().getClass() == CacheController.class) {
if (context.getMethod().getName().equals("getRedisById")) {
caches.add(redisCacheManager.getCache("user"));
}else {
caches.add(concurrentMapCacheManager.getCache("user-map"));
}
}
return caches;
}
}
配置自定義緩存管理器并注冊(cè)緩存解析器
配置了自定義的CacheManager會(huì)導(dǎo)致yml里面的相關(guān)配置失效(任何一個(gè)都會(huì),比如指定map的緩存管理器,yml配redis,則redis的配置也不生效)
@Bean
public ConcurrentMapCacheManager mapCacheManager() {
return new ConcurrentMapCacheManager("user-map","user");
}
@Primary
@Bean
public RedisCacheManager redisCacheManager(RedisTemplate<String, Object> redisTemplate) {
return RedisCacheManager.builder(Objects.requireNonNull(redisTemplate.getConnectionFactory()))
.cacheDefaults(RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(10)) // 設(shè)置默認(rèn)緩存過期時(shí)間為10分鐘
.disableCachingNullValues()) // 禁用緩存空值
.withInitialCacheConfigurations(initialCacheConfigurations()) // 設(shè)置特定緩存的配置
.build();
}
private Map<String, RedisCacheConfiguration> initialCacheConfigurations() {
Map<String, RedisCacheConfiguration> initialConfigurations = new HashMap<>();
// 設(shè)置特定緩存的過期時(shí)間
initialConfigurations.put("user", RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofHours(1)) // 設(shè)置特定緩存的過期時(shí)間為1小時(shí)
.disableCachingNullValues());
return initialConfigurations;
}
@Bean
public CacheResolver customCacheResolver(ConcurrentMapCacheManager concurrentMapCacheManager,
RedisCacheManager redisCacheManager) {
return new CustomCacheResolver(concurrentMapCacheManager,redisCacheManager);
}
測(cè)試
/**
* 執(zhí)行的是RedisCacheManager的緩存管理器
*/
@GetMapping("/resolver/redis/query/{id}")
@Cacheable(unless = "#result == null",cacheResolver = "customCacheResolver")
public User getRedisById(@PathVariable Long id){
return userService.getById(id);
}
/**
* 執(zhí)行的是ConcurrentMapCacheManager的緩存管理器
*/
@GetMapping("/resolver/map/query/{id}")
@Cacheable(cacheNames = "user-map",unless = "#result == null",cacheResolver = "customCacheResolver")
public User getMapCacheById(@PathVariable Long id){
return userService.getById(id);
}
4.CacheManagerCustomizer
CacheManagerCustomizer 是一個(gè)用于在緩存管理器初始化之前對(duì)其進(jìn)行自定義配置的接口。通過實(shí)現(xiàn)該接口的 Bean,可以對(duì)緩存管理器進(jìn)行定制,例如設(shè)置緩存名稱、是否允許緩存空值、設(shè)置緩存過期時(shí)間等。
- 自定義緩存配置:CacheManagerCustomizer 允許在緩存管理器初始化之前對(duì)緩存進(jìn)行配置,例如設(shè)置緩存名稱、過期時(shí)間、序列化方式等。這使得開發(fā)者可以針對(duì)不同的緩存需求進(jìn)行靈活配置。
- 覆蓋默認(rèn)配置:如果使用了 CacheManagerCustomizer,那么 application.yml 或 application.properties 中的緩存配置將不會(huì)生效,因?yàn)?CacheManagerCustomizer 會(huì)覆蓋默認(rèn)的配置 。
- 支持多種緩存類型:CacheManagerCustomizer 不僅適用于 ConcurrentMapCacheManager,還可以用于其他類型的緩存管理器,如 RedisCacheManager、CaffeineCacheManager 等。通過實(shí)現(xiàn) CacheManagerCustomizer 接口,可以對(duì)不同類型的緩存管理器進(jìn)行統(tǒng)一的配置 。
- 提供回調(diào)機(jī)制:CacheManagerCustomizer 提供了一個(gè) customize 方法,該方法會(huì)在緩存管理器初始化之前被調(diào)用,允許開發(fā)者對(duì)緩存管理器進(jìn)行定制。例如,可以設(shè)置緩存名稱、允許或禁止緩存空值等 。
@Bean
public CacheManagerCustomizer<ConcurrentMapCacheManager> cacheManagerCustomizer() {
return new CacheManagerCustomizer<ConcurrentMapCacheManager>() {
@Override
public void customize(ConcurrentMapCacheManager cacheManager) {
cacheManager.setCacheNames(Arrays.asList("user"));
cacheManager.setAllowNullValues(false); // 禁用緩存空值
}
};
}
@Bean
public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer() {
return (builder) -> {
builder.cacheDefaults(
RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(10)) // 默認(rèn)過期時(shí)間為 10 分鐘
.disableCachingNullValues()); // 禁用緩存空值
};
}
如果配置了自定義的緩存管理器(redisCacheManager),則CacheManagerCustomizer將不生效
到此這篇關(guān)于SpringBoot整合SpringCache緩存的實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)SpringBoot SpringCache緩存內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot如何使用Template請(qǐng)求http接口
在Spring?Boot中,如果你想要通過模板(template)的方式連接HTTP服務(wù),并發(fā)送HTTP請(qǐng)求,有幾種不同的方式可以實(shí)現(xiàn),但最直接和常用的方式之一是使用RestTemplate,這篇文章主要介紹了SpringBoot使用Template請(qǐng)求http接口,需要的朋友可以參考下2024-08-08
Struts1教程之ActionMapping_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要介紹了Struts1教程之ActionMapping,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-09-09
Spring Cloud OAuth2中/oauth/token的返回內(nèi)容格式
Spring Cloud OAuth2 生成access token的請(qǐng)求/oauth/token的返回內(nèi)容就需要自定義,本文就詳細(xì)介紹一下,感興趣的可以了解一下2021-07-07
java實(shí)現(xiàn)學(xué)生成績錄入系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)學(xué)生成績錄入系統(tǒng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01
java數(shù)據(jù)結(jié)構(gòu)算法稀疏數(shù)組示例詳解
這篇文章主要為大家介紹了java數(shù)據(jù)結(jié)構(gòu)算法稀疏數(shù)組示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06

