java中常用缓存

在日常开发中,有时通常使用缓存来提高系统的响应速度。下面介绍几种常用的缓存

无缓存依赖引入

当在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依赖。你可以直接使用RedisTemplateStringRedisTemplate来执行缓存操作。 但是,如果你希望利用Spring的缓存抽象来简化你的代码,并使你的应用更易于维护和测试,那么引入spring-boot-starter-cache依赖是很有用的。通过这个依赖,你可以使用注解来声明哪些方法的结果应该被缓存,以及缓存的键应该如何生成等。

说人话就是:

  • 如果你只想使用Redis作为数据存储而不使用Spring的缓存抽象,那么只需引入spring-boot-starter-data-redis
  • 如果你希望使用Spring的缓存抽象来简化你的缓存操作,那么你应该同时引入spring-boot-starter-data-redisspring-boot-starter-cache

那又有人要说了,那为什么我们什么缓存组件都没有引入,也能使用@Cacheable等注解呢? 再来看看文心一言的回答:

在Spring Boot项目中,即使没有显式地引入其他缓存中间件(如Redis、EhCache等)和spring-boot-starter-cache依赖,你仍然可能能够使用@Cacheable这样的缓存注解。这背后有几个原因:

  1. 默认缓存管理器 :Spring Boot在类路径(classpath)上没有检测到其他缓存提供者时,会回退到使用SimpleCacheManagerConcurrentMapCacheManager作为默认的缓存管理器。这些默认的缓存管理器提供了基于内存的缓存实现,非常适合于开发环境或简单的应用场景。
  2. 自动配置 :Spring Boot的自动配置特性会根据项目中引入的依赖和配置自动创建和配置bean。即使没有显式地引入spring-boot-starter-cache,只要Spring Boot检测到缓存相关的注解(如@Cacheable),它可能会尝试为你配置一个默认的缓存管理器。
  3. 条件化配置: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;
}

具体使用方法跟上述相同,后续会继续补充啦 今日摸鱼结束,下班,冲鸭。

相关推荐
摇滚侠1 小时前
Spring Boot 3零基础教程,IOC容器中组件的注册,笔记08
spring boot·笔记·后端
程序员小凯3 小时前
Spring Boot测试框架详解
java·spring boot·后端
你的人类朋友4 小时前
什么是断言?
前端·后端·安全
程序员小凯5 小时前
Spring Boot缓存机制详解
spring boot·后端·缓存
i学长的猫6 小时前
Ruby on Rails 从0 开始入门到进阶到高级 - 10分钟速通版
后端·ruby on rails·ruby
用户21411832636026 小时前
别再为 Claude 付费!Codex + 免费模型 + cc-switch,多场景 AI 编程全搞定
后端
茯苓gao6 小时前
Django网站开发记录(一)配置Mniconda,Python虚拟环境,配置Django
后端·python·django
Cherry Zack6 小时前
Django视图进阶:快捷函数、装饰器与请求响应
后端·python·django
爱读源码的大都督7 小时前
为什么有了HTTP,还需要gPRC?
java·后端·架构
码事漫谈7 小时前
致软件新手的第一个项目指南:阶段、文档与破局之道
后端