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;
}

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

相关推荐
杨哥带你写代码2 小时前
网上商城系统:Spring Boot框架的实现
java·spring boot·后端
camellias_2 小时前
SpringBoot(二十一)SpringBoot自定义CURL请求类
java·spring boot·后端
背水2 小时前
初识Spring
java·后端·spring
晴天飛 雪2 小时前
Spring Boot MySQL 分库分表
spring boot·后端·mysql
weixin_537590452 小时前
《Spring boot从入门到实战》第七章习题答案
数据库·spring boot·后端
AskHarries3 小时前
Spring Cloud Gateway快速入门Demo
java·后端·spring cloud
Qi妙代码3 小时前
MyBatisPlus(Spring Boot版)的基本使用
java·spring boot·后端
宇宙超级勇猛无敌暴龙战神3 小时前
Springboot整合xxl-job
java·spring boot·后端·xxl-job·定时任务
晚睡早起₍˄·͈༝·͈˄*₎◞ ̑̑3 小时前
SpringBoot(五)
java·spring boot·后端