在日常开发中,有时通常使用缓存来提高系统的响应速度。下面介绍几种常用的缓存
无缓存依赖引入
当在springboot项目中,没有引入任何缓存的依赖时,仍然可以使用本地缓存,需要在启动类添加@EnableCaching
代码如下
java
@RestController
@RequestMapping("/cache")
@RequiredArgsConstructor
public class CacheController {
private final TestCache testCache;
// 小伙伴们可以自行debug,看看此时是使用了哪一个manager(当没有配置manager时)
private final CacheManager cacheManager;
@GetMapping("/{id}")
public String testCacheById(@PathVariable("id") String id){
return testCache.getCacheById(id);
}
@GetMapping("testFind/{id}")
public String testFindCacheById(@PathVariable("id") String id){
return testCache.findCacheById(id);
}
@DeleteMapping("/{id}")
public String testDeleteCacheById(@PathVariable("id") String id){
testCache.deleteCacheById(id);
return "okk";
}
@PutMapping("/{id}")
public String testUpdateCacheById(@PathVariable("id") String id){
return testCache.upDateCacheById(id);
}
}
java
@Service
public class TestCacheImpl implements TestCache {
@Override
// "#p0"或者是使用#id都可以拿到getCacheById里面的参数
@Cacheable(value = "cache",key = "#p0") // 可以保存该方法的返回值
public String getCacheById(String id) {
return id;
}
@Override
@CacheEvict(value = "cache",key = "#id")
public void deleteCacheById(String id) {
}
@Override
@CachePut(value = "cache",key = "#id") // 可以保存(修改)该方法的返回值
public String upDateCacheById(String id) {
return id + "###";
}
@Override
@Cacheable(value = "cache_cache",key = "#id") // 可以保存(修改)该方法的返回值
public String findCacheById(String id) {
return "我的命名空间是cache_cache" + id;
}
}
简单解释上述注解里面的含义:
value:
表示命名空间,不同的命名空间是隔离的,根据
key:
表示存储变量key,可以理解为Map的key,每个key可以对应一个值
简单解释上述中用到的几个注解的含义:
@Cacheable:
适用于更新(增加)数据的方法,并将返回的值放入到缓存中
@CacheEvict:
该注解的作用是删除缓存数据
@Cacheable:
使用于新增数据的方法,并将返回值放入到缓存中
总结: 当增删改查时,会首先根据命名空间来找到指定的空间,然后再根据key来定位到值
举个例子: 例如上述方法中getCacheById(1)
和findCacheById(1)
当分别调用了该方法时,第一次时,由于没有此空间(缓存名字),会创建一个,当都第二次调用时,一个会在cache
空间中找寻key
为1的值,一个会在cache_cache
中找key
为1的值,两者并不干扰。
当使用postman测试时,第一次调用testCacheById
方法时,此时缓存里是没有数据的,也没有我们所需要的空间cache
也就是没有命中缓存,此时会走后续代码,并把返回值放入缓存中。需要走具体逻辑如图所示:
但当我们第二次以相同的参数调用该方法时可以看到:给我们创建了命名空间,并且在该空间下可以找到我们的key,此时也就不会执行后续的逻辑,直接返回缓存中的值。
当我们换一个参数请求时,比如id = 100,此时cache
中没有key为100的,就会在缓存中添加,当我们第二次调用时就可以命中缓存
此时小伙伴就想了,那我们要是调用了findCacheById
命名空间是cache_cache
跟上述cache
不同会是什么样子的呢?
当我们调用删除testDeleteCacheById
方法时,会删除指定value
命名空间里面对应的key
,例如:当我们删除在cache
命名空间中key
为100的,结果会是如何呢?
此时大家可以清晰的看见,当调用了该方法时,相对应的命名空间
中的对应的key
就会被删除了。
这时候如果大家调用了testUpdateCacheById(100)
方法时,就会发现此时相对应的key又出现了
,此时@CachePut
就起到了新增的作用。如图:
通过上述图文介绍,大家应该学会怎么该如何简单的使用了吧。有眼尖的小伙伴可能已经发现了,当我们没有配置默认的cacheManager
时,spring
给我们默认的使用了ConcurrentMapCacheManager
来实现了CacheManager
接口,当然我们也可以手动的(显示)的配置其缓存管理器。
java
@Configuration
public class CacheConfig {
@Bean
// 显示的配置了采用什么缓存管理器
public CacheManager cacheManager(){
ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
return cacheManager;
}
}
引入redis缓存
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>
疑问 引入redis
是用到了redis,那么spring-boot-starter-cache
是干什么的呢? 来看看文心一言
的回答
当你在Spring Boot项目中引入Redis作为缓存时,你确实需要引入
spring-boot-starter-data-redis
依赖,这个依赖会包含与Redis交互所需的所有核心类和配置。这个依赖会自动配置RedisTemplate,使得你可以轻松地与Redis数据库进行交互。 然而,关于spring-boot-starter-cache
依赖,这取决于你是否想使用Spring的抽象缓存支持。spring-boot-starter-cache
提供了Spring的缓存抽象,允许你使用注解(如@Cacheable
、@CacheEvict
等)来声明缓存行为,而不需要直接编写与缓存提供者的交互代码。Spring Boot会自动检测你选择的缓存提供者(在这个例子中是Redis),并相应地配置缓存管理器。 如果你只想使用Redis作为存储机制,并不打算使用Spring的缓存抽象,那么你可能不需要spring-boot-starter-cache
依赖。你可以直接使用RedisTemplate
或StringRedisTemplate
来执行缓存操作。 但是,如果你希望利用Spring的缓存抽象来简化你的代码,并使你的应用更易于维护和测试,那么引入spring-boot-starter-cache
依赖是很有用的。通过这个依赖,你可以使用注解来声明哪些方法的结果应该被缓存,以及缓存的键应该如何生成等。
说人话就是:
- 如果你只想使用Redis作为数据存储而不使用Spring的缓存抽象,那么只需引入
spring-boot-starter-data-redis
。 - 如果你希望使用Spring的缓存抽象来简化你的缓存操作,那么你应该同时引入
spring-boot-starter-data-redis
和spring-boot-starter-cache
。
那又有人要说了,那为什么我们什么缓存组件都没有引入,也能使用@Cacheable
等注解呢? 再来看看文心一言
的回答:
在Spring Boot项目中,即使没有显式地引入其他缓存中间件(如Redis、EhCache等)和
spring-boot-starter-cache
依赖,你仍然可能能够使用@Cacheable
这样的缓存注解。这背后有几个原因:
- 默认缓存管理器 :Spring Boot在类路径(classpath)上没有检测到其他缓存提供者时,会回退到使用
SimpleCacheManager
或ConcurrentMapCacheManager
作为默认的缓存管理器。这些默认的缓存管理器提供了基于内存的缓存实现,非常适合于开发环境或简单的应用场景。 - 自动配置 :Spring Boot的自动配置特性会根据项目中引入的依赖和配置自动创建和配置bean。即使没有显式地引入
spring-boot-starter-cache
,只要Spring Boot检测到缓存相关的注解(如@Cacheable
),它可能会尝试为你配置一个默认的缓存管理器。 - 条件化配置:Spring Boot的自动配置是条件化的,这意味着只有在满足某些条件时才会创建和配置bean。在这种情况下,如果没有其他缓存提供者可用,Spring Boot可能会自动配置一个基于内存的默认缓存管理器。
尽管你可以在没有显式引入缓存依赖的情况下使用@Cacheable
等注解,但这样做有一些限制:
- 性能:默认的缓存管理器通常基于内存,可能不适合生产环境,特别是在需要持久化或分布式缓存的场景中。
- 功能限制:默认的缓存管理器可能不提供高级功能,如缓存过期、缓存驱逐策略、缓存分区等。
因此,尽管在没有其他缓存依赖的情况下可以使用Spring Boot的默认缓存管理器,但通常建议根据你的应用需求显式地引入一个合适的缓存提供者,并配置相应的缓存管理器。这可以通过添加相应的Spring Boot Starter依赖(如spring-boot-starter-data-redis
)来完成,并可能需要一些额外的配置来指定缓存的详细信息。
如果有哪位大佬能更好的讲清楚这个问题,欢迎来评论哦
此时就需要对我们的CacheManager
进行配置,配置上我们的redis
java
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofHours(1)) // 设置缓存有效期1小时
.disableCachingNullValues()
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(cacheConfiguration)
/**使得RedisCacheManager创建的缓存操作能够参与到Spring管理的事务中,从而确保数据的一致性。
如果你在一个标记为`@Transactional`的方法中执行了缓存操作(如`put`、`get`、`evict`等),
这些操作将作为事务的一部分执行。如果事务最终提交,那么缓存操作也会生效;如果事务回
滚,那么缓存操作也会被撤销,确保缓存与数据库或其他资源保持同步*/
.transactionAware()
.build();
return cacheManager;
}
yml
spring.redis.database=0
spring.redis.host=localhost
spring.redis.password=root
spring.redis.port=6379
这时就可以快乐的使用redis
来进行缓存操作了,具体使用方法还是跟最上面的使用方式相同。
caffeine
引入依赖
xml
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>3.1.8</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
相关代码 配置 CacheManager
java
@Bean
public CacheManager cacheManager() {
Caffeine<Object, Object> caffeine = Caffeine.newBuilder()
.initialCapacity(100)
.maximumSize(500)
.expireAfterWrite(10, TimeUnit.MINUTES)
.recordStats(); // 启用统计
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(caffeine);
return cacheManager;
}
具体使用方法跟上述相同,后续会继续补充啦 今日摸鱼结束,下班,冲鸭。