【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache功能的开发实战指南(一)

简介: 【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache功能的开发实战指南

CacheManager管理器的扩展支持

Spring的抽象控制机制,即允许绑定不同的缓存解决方案(如Caffeine、Ehcache等),但本身不直接提供缓存功能的实现。它支持注解方式使用缓存,非常方便。

SpringBoot在Annotation的层面实现了数据缓存的功能,基于Spring的AOP技术。所有的缓存配置只是在Annotation层面配置,像声明式事务一样。

Spring定义了CacheManager和Cache接口统一不同的缓存技术。其中CacheManager是Spring提供的各种缓存技术的抽象接口。而Cache接口包含缓存的各种操作。

Cache接口下Spring提供了各种xxxCache的实现,如RedisCache,EhCacheCache ,ConcurrentMapCache等;

缓存技术类型与CacheManger

针对不同的缓存技术,需要实现不同的cacheManager,Spring定义了如下的cacheManger实现。

CacheManger 描述
SimpleCacheManager 使用简单Collection来存储缓存,主要用于测试
ConcurrentMapCacheManager 使用ConcurrentMap作为缓存技术(默认),需要显式的删除缓存,无过期机制
NoOpCacheManager 仅测试用途,不会实际存储缓存
EhCacheCacheManager 使用EhCache作为缓存技术,以前在hibernate的时候经常用
GuavaCacheManager 使用google guava的GuavaCache作为缓存技术(1.5版本已不建议使用)
CaffeineCacheManager 是使用Java8对Guava缓存的重写,spring5(springboot2)开始用Caffeine取代guava
HazelcastCacheManager 使用Hazelcast作为缓存技术
JCacheCacheManager 使用JCache标准的实现作为缓存技术,如Apache Commons JCS
RedisCacheManager 使用Redis作为缓存技术

常规的SpringBoot已经为我们自动配置了EhCache、Collection、Guava、ConcurrentMap等缓存,默认使用ConcurrentMapCacheManager。

SpringBoot的application.properties配置文件,使用spring.cache前缀的属性进行配置。


缓存依赖

开始使用前需要导入依赖spring-boot-starter-cache为基础依赖,其他依赖根据使用不同的缓存技术选择加入,默认情况下使用ConcurrentMapCache不需要引用任何依赖。

xml

复制代码

<!-- 基础依赖 -->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- 使用 ehcache -->
<dependency>
   <groupId>net.sf.ehcache</groupId>
   <artifactId>ehcache</artifactId>
</dependency>
<!-- 使用  caffeine https://mvnrepository.com/artifact/com.github.ben-manes.caffeine/caffeine -->
<dependency>
   <groupId>com.github.ben-manes.caffeine</groupId>
   <artifactId>caffeine</artifactId>
   <version>2.6.0</version>
</dependency>
<!-- 使用  redis  -->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

application配置

yaml

复制代码

spring.cache.type= #缓存的技术类型,可选 generic,ehcache,hazelcast,infinispan,jcache,redis,guava,simple,none
spring.cache.cache-names= #应用程序启动创建缓存的名称,必须将所有注释为@Cacheable缓存name(或value)罗列在这里,否者:Cannot find cache named 'xxx' for Builder[xx] caches=[sysItem] | key='' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='' | unless='' | sync='false'
#以下根据不同缓存技术选择配置
spring.cache.ehcache.config= #EHCache的配置文件位置
spring.caffeine.spec= #caffeine类型创建缓存的规范。查看CaffeineSpec了解更多关于规格格式的细节
spring.cache.infinispan.config= #infinispan的配置文件位置
spring.cache.jcache.config= #jcache配置文件位置
spring.cache.jcache.provider= #当多个jcache实现类时,指定选择jcache的实现类

缓存注解

下面是缓存公用接口注释,适用于任何缓存类型。

@EnableCaching

在启动类注解@EnableCaching开启缓存。

java

复制代码

@SpringBootApplication
@EnableCaching  //开启缓存
public class DemoApplication{     
   public static void main(String[] args) {
      SpringApplication.run(DemoApplication.class, args);
   }
}

@Cacheable

配置findByName函数的返回值将被加入缓存。同时在查询时,会先从缓存中获取,若不存在才再发起对数据库的访问。

该注解主要有下面几个参数:

  • value、cacheNames:两个等同的参数(cacheNames为Spring4新增,作为value的别名),用于指定缓存存储的集合名。
  • 由于Spring4中新增了@CacheConfig,因此在Spring3中原本必须有的value属性,也成为非必需项了。
  • key:缓存对象存储在Map集合中的key值,非必需,缺省按照函数的所有参数组合作为key值,若自己配置需使用SpEL表达式,比如:@Cacheable(key = “#p0”):使用函数第一个参数作为缓存的key值。
  • condition:缓存对象的条件,非必需,也需使用SpEL表达式,只有满足表达式条件的内容才会被缓存,比如:@Cacheable(key = “#p0”, condition = “#p0.length() < 3”),表示只有当第一个参数的长度小于3的时候才会被缓存
  • unless:另外一个缓存条件参数,非必需,需使用SpEL表达式。它不同于condition参数的地方在于它的判断时机,该条件是在函数被调用之后才做判断的,所以它可以通过对result进行判断。
  • keyGenerator:用于指定key生成器,非必需。若需要指定一个自定义的key生成器,我们需要去实现org.springframework.cache.interceptor.KeyGenerator接口,并使用该参数来指定。
  • 需要注意的是:该参数与key是互斥的。
  • cacheManager:用于指定使用哪个缓存管理器,非必需。只有当有多个时才需要使用
  • cacheResolver:用于指定使用那个缓存解析器,非必需。需通过org.springframework.cache.interceptor.CacheResolver接口来实现自己的缓存解析器,并用该参数指定。

java

复制代码

public class SampleServiceImpl implements SampleService {
     @Override
     @Cacheable(value = {"newJob"},key = "#p0")
     public List<NewJob> findAllLimit(int num) {
         return botRelationRepository.findAllLimit(num);
     }
        .....
}

@CachePut

针对方法配置,能够根据方法的请求参数对其结果进行缓存,和 @Cacheable不同的是,它每次都会触发真实方法的调用 。

简单来说就是用户更新缓存数据。但需要注意的是该注解的value 和key必须与要更新的缓存相同,也就是与@Cacheable 相同。

示例:

java

复制代码

//按条件更新缓存
@CachePut(value = "newJob", key = "#p0") 
public NewJob updata(NewJob job) {
     NewJob newJob = newJobDao.findAllById(job.getId());
     newJob.updata(job);
     return job;
}

@CacheEvict

配置于函数上,通常用在删除方法上,用来从缓存中移除相应数据。除了同@Cacheable一样的参数之外,它还有下面两个参数:

  • allEntries:非必需,默认为false。当为true时,会移除所有数据。如:@CachEvict(value=”testcache”,allEntries=true)
  • beforeInvocation:非必需,默认为false,会在调用方法之后移除数据。当为true时,会在调用方法之前移除数据。 如:

java

复制代码

@CachEvict(value=”testcache”,beforeInvocation=true)
        @Cacheable(value = "emp",key = "#p0.id")
        public NewJob save(NewJob job) {
            newJobDao.save(job);
            return job;
        }
        //清除一条缓存,key为要清空的数据
        @CacheEvict(value="emp",key="#id")
        public void delect(int id) {
            newJobDao.deleteAllById(id);
        }
        //方法调用后清空所有缓存
        @CacheEvict(value="accountCache",allEntries=true)
        public void delectAll() {
            newJobDao.deleteAll();
        }
        //方法调用前清空所有缓存
        @CacheEvict(value="accountCache",beforeInvocation=true)
        public void delectAll() {
            newJobDao.deleteAll();
        }

@CacheConfig

统一配置本类的缓存注解的属性,在类上面统一定义缓存的名字,方法上面就不用标注了,当标记在一个类上时则表示该类所有的方法都是支持缓存的

java

复制代码

@CacheConfig(cacheNames = {"myCache"})
    public class SampleServiceImpl implements SampleService {
        @Override
        @Cacheable(key = "targetClass + methodName +#p0")
        //此处没写value
        public List<BotRelation> findAllLimit(int num) {
            return botRelationRepository.findAllLimit(num);
        }
        .....
    }

SpEL上下文数据

Spring Cache提供了一些供我们使用的SpEL上下文数据,直接摘自Spring官方文档:

名称 位置 描述 示例
methodName root对象 当前被调用的方法名 #root.methodname
method root对象 当前被调用的方法 #root.method.name
target root对象 当前被调用的目标对象实例 #root.target
targetClass root对象 当前被调用的目标对象的类 #root.targetClass
args root对象 当前被调用的方法的参数列表 #root.args[0]
caches root对象 当前方法调用使用的缓存列表 #root.caches[0].name
Argument Name 执行上下文 当前被调用的方法的参数,如findArtisan(Artisan artisan),可以通过#artsian.id获得参数 #artsian.id
result 执行上下文 方法执行后的返回值(仅当方法执行后的判断有效,如 unless cacheEvict的beforeInvocation=false) #result

注意

当我们要使用root对象的属性作为key时,我们也可以将“#root”省略,因为Spring默认使用的就是root对象的属性。 如

ini

复制代码

@Cacheable(key = "targetClass + methodName +#p0")

使用方法参数时,可以直接使用“#参数名”或者“#p参数index”。 如:

java

复制代码

@Cacheable(value="users", key="#id")
@Cacheable(value="users", key="#p0")

SpEL提供了多种运算符

类型 运算符
关系 <,>,<=,>=,==,!=,lt,gt,le,ge,eq,ne
算术 +,- ,* ,/,%,^
逻辑 &&, ,!,and,or,not,between,instanceof
条件 ?: (ternary),?: (elvis)
正则表达式 matches
其他类型 ?.,?[…],![…],^[…],$[…]

不同Cache的实现机制



ConcurrentMap Cache的实现方案

SpringBoot默认使用的是SimpleCacheConfiguration,使用ConcurrentMapCacheManager来实现缓存,ConcurrentMapCache实质是一个ConcurrentHashMap集合对象java内置,所以无需引入其他依赖,也没有额外的配置

ConcurrentMapCache的自动装配声明在SimpleCacheConfiguration中,如果需要也可对它进行额外的装配

java

复制代码

//注册id为cacheManager,类型为ConcurrentMapCacheManager的bean
@Bean
public ConcurrentMapCacheManager cacheManager() {
    ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager(); //实例化ConcurrentMapCacheManager
    List<String> cacheNames = this.cacheProperties.getCacheNames(); //读取配置文件,如果配置有spring.cache.cache-names=xx,xx,则进行配置cacheNames,默认是没有配置的
    if (!cacheNames.isEmpty()) {
       cacheManager.setCacheNames(cacheNames);
    }
    return this.customizerInvoker.customize(cacheManager); 
    }

调用CacheManagerCustomizers#customize 进行个性化设置,在该方法中是遍历其持有的List。

Caffeine Cache

Caffeine是使用Java8对Guava缓存的重写版本,在Spring Boot 2.0中将取代,基于LRU算法实现,支持多种缓存过期策略。具体查看这里。

Caffeine参数说明

ini

复制代码

initialCapacity=[integer]: 初始的缓存空间大小
maximumSize=[long]: 缓存的最大条数
maximumWeight=[long]: 缓存的最大权重
expireAfterAccess=[duration]: 最后一次写入或访问后经过固定时间过期
expireAfterWrite=[duration]: 最后一次写入后经过固定时间过期
refreshAfterWrite=[duration]: 创建缓存或者最近一次更新缓存后经过固定的时间间隔,刷新缓存 refreshAfterWrite requires a LoadingCache
weakKeys: 打开key的弱引用
weakValues:打开value的弱引用
softValues:打开value的软引用
recordStats:开发统计功能

注意:

refreshAfterWrite必须实现LoadingCache,跟expire的区别是,指定时间过后,expire是remove该key,下次访问是同步去获取返回新值,而refresh则是指定时间后,不会remove该key,下次访问会触发刷新,新值没有回来时返回旧值

  • expireAfterWrite和expireAfterAccess同时存在时,以expireAfterWrite为准。
  • maximumSize和maximumWeight不可以同时使用
  • weakValues和softValues不可以同时使用

导入依赖

xml

复制代码

<!-- 使用  caffeine https://mvnrepository.com/artifact/com.github.ben-manes.caffeine/caffeine -->
<dependency>
   <groupId>com.github.ben-manes.caffeine</groupId>
   <artifactId>caffeine</artifactId>
   <version>2.6.0</version>
</dependency>

通过yaml配置

通过配置文件来设置Caffeine

yaml

复制代码

spring:
  cache:
    cache-names: outLimit,notOutLimit
    caffeine:
      spec: initialCapacity=50,maximumSize=500,expireAfterWrite=5s,refreshAfterWrite=7s #
      type: caffeine

通过bean装配

java

复制代码

@Bean
@Primary
public CacheManager cacheManagerWithCaffeine() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();
        Caffeine caffeine = Caffeine.newBuilder()
                .initialCapacity() //cache的初始容量值
                .maximumSize() //maximumSize用来控制cache的最大缓存数量,maximumSize和maximumWeight不可以同时使用,
                .maximumWeight() //控制最大权重
                .expireAfter(customExpireAfter) //自定义过期
                .refreshAfterWrite(, TimeUnit.SECONDS);  //使用refreshAfterWrite必须要设置cacheLoader
        cacheManager.setCaffeine(caffeine);
        cacheManager.setCacheLoader(cacheLoader); //缓存加载方案
        cacheManager.setCacheNames(getNames());   //缓存名称列表
        cacheManager.setAllowNullValues(false);
        return cacheManager;
    }


【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache功能的开发实战指南(二)https://developer.aliyun.com/article/1471012

相关文章
|
6月前
|
负载均衡 监控 Java
Spring Cloud Gateway 全解析:路由配置、断言规则与过滤器实战指南
本文详细介绍了 Spring Cloud Gateway 的核心功能与实践配置。首先讲解了网关模块的创建流程,包括依赖引入(gateway、nacos 服务发现、负载均衡)、端口与服务发现配置,以及路由规则的设置(需注意路径前缀重复与优先级 order)。接着深入解析路由断言,涵盖 After、Before、Path 等 12 种内置断言的参数、作用及配置示例,并说明了自定义断言的实现方法。随后重点阐述过滤器机制,区分路由过滤器(如 AddRequestHeader、RewritePath、RequestRateLimiter 等)与全局过滤器的作用范围与配置方式,提
Spring Cloud Gateway 全解析:路由配置、断言规则与过滤器实战指南
|
7月前
|
监控 Java API
Spring Boot 3.2 结合 Spring Cloud 微服务架构实操指南 现代分布式应用系统构建实战教程
Spring Boot 3.2 + Spring Cloud 2023.0 微服务架构实践摘要 本文基于Spring Boot 3.2.5和Spring Cloud 2023.0.1最新稳定版本,演示现代微服务架构的构建过程。主要内容包括: 技术栈选择:采用Spring Cloud Netflix Eureka 4.1.0作为服务注册中心,Resilience4j 2.1.0替代Hystrix实现熔断机制,配合OpenFeign和Gateway等组件。 核心实操步骤: 搭建Eureka注册中心服务 构建商品
1192 3
|
5月前
|
监控 Cloud Native Java
Spring Boot 3.x 微服务架构实战指南
🌟蒋星熠Jaxonic,技术宇宙中的星际旅人。深耕Spring Boot 3.x与微服务架构,探索云原生、性能优化与高可用系统设计。以代码为笔,在二进制星河中谱写极客诗篇。关注我,共赴技术星辰大海!(238字)
1068 2
Spring Boot 3.x 微服务架构实战指南
|
5月前
|
XML Java 测试技术
《深入理解Spring》:IoC容器核心原理与实战
Spring IoC通过控制反转与依赖注入实现对象间的解耦,由容器统一管理Bean的生命周期与依赖关系。支持XML、注解和Java配置三种方式,结合作用域、条件化配置与循环依赖处理等机制,提升应用的可维护性与可测试性,是现代Java开发的核心基石。
|
7月前
|
人工智能 监控 安全
如何快速上手【Spring AOP】?核心应用实战(上篇)
哈喽大家好吖~欢迎来到Spring AOP系列教程的上篇 - 应用篇。在本篇,我们将专注于Spring AOP的实际应用,通过具体的代码示例和场景分析,帮助大家掌握AOP的使用方法和技巧。而在后续的下篇中,我们将深入探讨Spring AOP的实现原理和底层机制。 AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架中的核心特性之一,它能够帮助我们解决横切关注点(如日志记录、性能统计、安全控制、事务管理等)的问题,提高代码的模块化程度和复用性。
|
8月前
|
存储 缓存 NoSQL
Spring Cache缓存框架
Spring Cache是Spring体系下的标准化缓存框架,支持多种缓存(如Redis、EhCache、Caffeine),可独立或组合使用。其优势包括平滑迁移、注解与编程两种使用方式,以及高度解耦和灵活管理。通过动态代理实现缓存操作,适用于不同业务场景。
665 0
|
10月前
|
缓存 NoSQL 关系型数据库
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
|
5月前
|
缓存 负载均衡 监控
135_负载均衡:Redis缓存 - 提高缓存命中率的配置与最佳实践
在现代大型语言模型(LLM)部署架构中,缓存系统扮演着至关重要的角色。随着LLM应用规模的不断扩大和用户需求的持续增长,如何构建高效、可靠的缓存架构成为系统性能优化的核心挑战。Redis作为业界领先的内存数据库,因其高性能、丰富的数据结构和灵活的配置选项,已成为LLM部署中首选的缓存解决方案。
618 25
|
6月前
|
存储 缓存 NoSQL
Redis专题-实战篇二-商户查询缓存
本文介绍了缓存的基本概念、应用场景及实现方式,涵盖Redis缓存设计、缓存更新策略、缓存穿透问题及其解决方案。重点讲解了缓存空对象与布隆过滤器的使用,并通过代码示例演示了商铺查询的缓存优化实践。
310 1
Redis专题-实战篇二-商户查询缓存