Spring Cache简化缓存开发
Posted 赵广陆
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Cache简化缓存开发相关的知识,希望对你有一定的参考价值。
目录
1 背景介绍
官方入门文档:https://spring.io/guides/gs/caching/
Spring 从3.1 开始定义了org.springframework.cache.Cache和org.springframework.cache.CacheManager 接口来统一不同的缓存技术; 并支持使用JCache(JSR-107)注解简化我们开发;Cache 接口为缓存的组件规范定义,包含缓存的各种操作集合; Cache 接口下Spring 提供了各种xxxCache 的实现;如RedisCache , EhCacheCache , ConcurrentMapCache 等;
每次调用需要缓存功能的方法时,Spring 会检查检查指定参数的指定的目标方法是否已 经被调用过;如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓存结果后返回给用户。下次调用直接从缓存中获取。
如果我们的程序想要使用缓存,就要与这些框架耦合。聪明的架构师已经在利用接口来降低耦合了,利用面向对象的抽象和多态的特性,做到业务代码与具体的框架分离。
但我们仍然需要显式地在代码中去调用与缓存有关的接口和方法,在合适的时候插入数据到缓存里,在合适的时候从缓存中读取数据。
想一想AOP的适用场景,这不就是天生就应该AOP去做的吗?
是的,Spring Cache就是一个这个框架。它利用了AOP,实现了基于注解的缓存功能,并且进行了合理的抽象,业务代码不用关心底层是使用了什么缓存框架,只需要简单地加一个注解,就能实现缓存功能了。而且Spring Cache也提供了很多默认的配置,用户可以3秒钟就使用上一个很不错的缓存功能。
既然有这么好的轮子,干嘛不用呢?
2 使用方式
Spring cache提供了开箱即用的接入方式,只需要若干注解和缓存管理类即可接入.
1.开启缓存能力
引入缓存依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
在应用启动类添加@EnableCaching注解:
@SpringBootApplication
@EnableCaching
public class Application
public static void main(String[] args)
SpringApplication.run(Application.class, args);
2.使用缓存
在业务方法添加@Cacheable注解:
@Cacheable(cacheNames = "task")
public TaskInfoDTO getTask(String taskId)
log.info("TestBuzz.getTask mock query from DB......");
TaskInfoDTO taskInfoDTO = new TaskInfoDTO();
taskInfoDTO.setTaskId(taskId);
taskInfoDTO.setApplicantId("system");
taskInfoDTO.setDescription("test");
return taskInfoDTO;
模拟请求:
@GetMapping("/test_cache")
public IResp<TaskInfoDTO> testCache()
TaskInfoDTO taskInfoDTO = this.testBuzz.getTask("task123");
return IResp.getSuccessResult(taskInfoDTO);
连续发送两次查询请求:
curl http://localhost:port/test_cache
从日志中看到只打印了一次DB调用,也就是说明第二次走了缓存。就这么简单我们就开启并使用了spring的缓存能力。
3 常用注解
原理:基于Proxy/AspectJ动态代理技术的AOP思想(面向切面编程)
使用:
- SpringCache包含两个顶级接口,Cache(缓存)和CacheManager(缓存管理器),顾名思义,用CacheManager去管理一堆Cache。
- spring cache实现有基于XML/注解实现AOP;
- CacheManager负责对缓存的增删改查, CacheManager的缓存的介质可配置, 如:ConcurrentMap/EhCache/Redis等。当没有加入EhCache或者Redis依赖时默认采用concurrentMap实现的缓存,是存在内存中,重启服务器则清空缓存
pring Cache中的注解主要有如下五个:
- @Cacheable:缓存数据或者获取缓存数据
- @CachePut:修改缓存数据
- @CacheEvict: 清空缓存
- @CacheConfig:统一配置@Cacheable中的value值
- @Caching:组合多个Cache注解
3.1 @Cacheable
先从value中获取为key的缓存,如果存在直接返回;如果不存在则执行方法并返回,且把返回输出存入缓存。(注意:保存的数据是return返回的数据)
主要有三个参数:
- value:缓存的名称,可以多个 (必填,也可以用@CacheConfig替代)
@Cacheable(value="testcache")
@Cacheable(value="testcache1","testcache2"
- key:缓存的 key,按照 SpEL 表达式编写,为空时默认为方法的入参(value相当于缓存空间的名称,而key相当于是一个缓存值的名字)
@Cacheable(value="testcache",key="#id")
- condition:缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false
@Cacheable(value="testcache",condition="#id.length()>2")
3.2 @CachePut
根据value中获取为key的缓存,如果存在则修改;不存在则新增
主要是三个参数
value,key,condition如上所示。
注意:保存的数据是return返回的数据,如下返回的有user对象和null,第一个缓存的数据是实体类,第二个缓存的数据是空
3.3 @CacheEvict
根据对应的value和key删除缓存,没有key值则删除value中所有的缓存
主要有五个参数value,key,condition,allEntries,beforeInvocation
- allEntries:是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存
@CachEvict(value="testcache",allEntries=true)
图一、会清空getData下面所有缓存(allEntries=true则删除所有)
图二、只会清空getData下面key值为id的缓存(没有key默认取入参)
图三、不会清空任何缓存
- beforeInvocation: 是否在方法执行前就清空,缺省为 false,如果指定为
true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存(注:作用只有一个,就是先清空缓存再执行方法
)
@CachEvict(value="testcache",beforeInvocation=true)
3.4 @CacheConfig
相当于把类下面所有方法@Cacheable中的value值放到@CacheConfig注解中,
如果@Cacheable中没有value值则用@Cacheable中的值;如果@Cacheable中有value值则以value值为准。
@CacheConfig("testcache")
public class UserServiceImpl implements UserService
@Cacheable
public Result findById(Long id)
@Cacheable
public Result findByIdAndName(Long id,String name)
3.5 @Caching
组合注解,可以组合多个注解
@Caching(put =
@CachePut(value = "user", key = "#user.id"),
@CachePut(value = "user", key = "#user.username"),
@CachePut(value = "user", key = "#user.email")
)
public User save(User user)
4 实战:Spring Cache整合redis缓存
4.1 CacheManager 缓存管理器
public interface CacheManager
//根据缓存名字获取缓存
@Nullable
Cache getCache(String name);
//获取管理的所有缓存的名字
Collection<String> getCacheNames();
支持多种类型的缓存
这里我们使用redis作为缓存
<!-- 引入redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- jedis不写版本springboot控制 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
4.2 配置spring-boot-starter-cache
1.缓存的自动配置
CacheProperties封装了配置文件中可以配置的属性
@ConfigurationProperties(prefix = "spring.cache")
org.springframework.boot.autoconfigure.cache.CacheProperties
CacheConfiguration`会根据缓存类型导入`RedisCacheConfiguration
RedisCacheConfiguration
自动配好了缓存管理器
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory,
ResourceLoader resourceLoader)
RedisCacheManagerBuilder builder = RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(determineConfiguration(resourceLoader.getClassLoader()));
List<String> cacheNames = this.cacheProperties.getCacheNames();
if (!cacheNames.isEmpty())
builder.initialCacheNames(new LinkedHashSet<>(cacheNames));
return this.customizerInvoker.customize(builder.build());
4.3 手动配置
配置使用Redis作为缓存
spring:
cache:
#cache-names: 可以自动配置
type: redis
4.4 开启缓存功能 @Cacheable(“…”)
在启动类添加@EnableCaching 注解,不需要重复配Redis
@EnableCaching 注解
2.使用注解 就能完成缓存操作
//每一个需要缓存的数据都需要指定要放到哪个名字的缓存,缓存的分区,按照业务类型分
@Cacheable("categroy") //代表方法的结果需要缓存 ,如果缓存中有 方法都不用调用,如果缓存中没有,调用方法,将结果放入缓存
@Override
public List<CategoryEntity> getLevel1Categorys()
long l = System.currentTimeMillis();
List<CategoryEntity> categoryEntityList = baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));
System.out.println("消耗时间:" + (System.currentTimeMillis() - l));
return categoryEntityList;
使用@Cacheable 默认行为
如果缓存中有缓存 方法都不用调用
key值 自动生成
缓存的Value值默认使用JDK序列化机制,将序列化后的数据存到Redis
默认过期时间TTL -1 永不过去
应该要指定缓存存活时间 在配置文件中修改
cache:
#cache-names: 可以自动配置
type: redis
redis:
time-to-live: 3600000 #设置存活时间毫秒
key-prefix: CACHE_ #key前缀 如果制定了前缀就用指定的前缀,如果没有就默认使用缓存的名字作为前缀
use-key-prefix: true # 是否使用前缀
cache-null-values: true # 是否缓存控制 解决缓存穿透
以上是关于Spring Cache简化缓存开发的主要内容,如果未能解决你的问题,请参考以下文章
SpringBoot整合Spring Cache,简化分布式缓存开发
SpringBoot整合Spring Cache,简化分布式缓存开发
SpringBoot整合Spring Cache,简化分布式缓存开发