SpringCache概述
缓存的框架太多了,各有各的优势,比如Redis、Memcached、Guava、Caffeine等等。
如果我们的程序想要使用缓存,就要与这些框架耦合。聪明的架构师已经在利用接口来降低耦合了,利用面向对象的抽象和多态的特性,做到业务代码与具体的框架分离。
但我们仍然需要显式地在代码中去调用与缓存有关的接口和方法,在合适的时候插入数据到缓存里,在合适的时候从缓存中读取数据。想一想「AOP」的适用场景,这不就是天生就应该AOP去做的吗?
自Spring 3.1起,提供了类似于 @Transactional 注解事务的注解Cache支持,且提供了Cache抽象,在此之前一般通过AOP实现。
使用Spring Cache的好处:
- 提供基本的Cache抽象,方便切换各种底层Cache;
- 通过注解Cache可以实现类似于事务一样,缓存逻辑透明的应用到我们的业务代码上,且只需要更少的代码就可以完成;
- 提供事务回滚时也自动回滚缓存;
- 支持比较复杂的缓存逻辑;
2.1、SpringCache概述及核心配置
是的,Spring Cache就是一个这个框架。它利用了AOP,实现了基于注解的缓存功能,并且进行了合理的抽象,业务代码不用关心底层是使用了什么缓存框架,只需要简单地加一个注解,就能实现缓存功能了。而且Spring Cache也提供了很多默认的配置,用户可以3秒钟就使用上一个很不错的缓存功能。
那我们怎么使用这个插件呢?来我们看下我们项目中集成的framework-starter-redis的模块:
1 2
| |——restkeeper-framework 核心组件模块,主要框架集成:mybatis-plus、seata、jwt、redis等等 |———— framework-starter-redis关于redis及SpringCache基础集成
|
framework-starter-redis模块中我们需要导入对SpringCache的支持:
1 2 3 4 5
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency>
|
下面我们看下framework-starter-redis模块中的RedisCacheConfig配置类是如何实现上述功能的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| package com.itheima.restkeeper.config;
import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializationContext; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration; import java.util.HashMap; import java.util.Map;
@Configuration
@EnableCaching public class RedisCacheConfig {
@Bean public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) { RedisSerializer<String> redisSerializer = new StringRedisSerializer(); GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofSeconds(60)) .serializeKeysWith(RedisSerializationContext .SerializationPair .fromSerializer(redisSerializer)) .serializeValuesWith(RedisSerializationContext .SerializationPair .fromSerializer(genericJackson2JsonRedisSerializer)) .disableCachingNullValues() .computePrefixWith(cacheName -> cacheName + ":");
Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>(); cacheConfigurations.put("dataDictList",customRedisCacheConfiguration(config,Duration.ofSeconds(6000))); cacheConfigurations.put("affixs",customRedisCacheConfiguration(config,Duration.ofSeconds(3000))); cacheConfigurations.put("brands",customRedisCacheConfiguration(config,Duration.ofSeconds(300)));
return RedisCacheManager.builder(redisConnectionFactory) .transactionAware() .withInitialCacheConfigurations(cacheConfigurations) .cacheDefaults(config) .build(); }
public RedisCacheConfiguration customRedisCacheConfiguration(RedisCacheConfiguration config, Duration ttl) { return config.entryTtl(ttl); } }
|
2.2、SpringCache的项目集成
以model-basic-producer项目为例,单我们使用SpringCache的时候只需要依赖: framework-starter-redis模块

然后在model-basic-producer的application.yml中添加redis配置即可==【如果使用nacos配置中心,需要在配置中心中添加配置】==:
1 2 3 4 5 6 7 8 9 10 11
| spring: main: allow-bean-definition-overriding: true redis: redisson: config: classpath:singleServerConfig.yaml host: 192.168.200.129 port: 6379 password: pass
|
第二章、优雅使用SpringCache
1、缓存层选择

选择producer的Face的理由:
1、producer的face是dubbo服务发生产者,其功能都是单一职能
2、service的功能过于细腻,切关联甚广
3、controller层功能过于粗狂,不易缓存的维护
2、SpringCache注解详解
2.1、@Cacheable注解
==如果缓存中没有:查询数据库,存储缓存,返回结果,==
==如果缓存中有:直接返回结果==
作用:可以用来进行缓存的写入,将结果存储在缓存中,以便于在后续调用的时候可以直接返回缓存中的值,而不必再执行实际的方法。 最简单的使用方式,注解名称=缓存名称,使用例子如下:

2.2、@CacheEvict注解
@CacheEvict:删除缓存的注解,这对删除旧的数据和无用的数据是非常有用的。这里还多了一个参数(allEntries),设置allEntries=true时,可以对整个条目进行批量删除

2.3、@CachePut注解
@CachePut:当需要更新缓存而不干扰方法的运行时 ,可以使用该注解。也就是说,始终执行该方法,并将结果放入缓存
备注:
@Cacheable 的逻辑是:查找缓存 - 有就返回 -没有就执行方法体 - 将结果缓存起来;
@CachePut 的逻辑是:执行方法体 - 将结果缓存起来;

2.4、@Caching注释
在使用缓存的时候,有可能会同时进行更新和删除,会出现同时使用多个注解的情况.而@Caching可以实现
1 2 3
| @Caching(put =@CachePut(value = "user",key ="#userVo.id"), evict = @CacheEvict(value = "userPage",allEntries = true))
|
提供的SpEL上下文数据:
Spring Cache提供了一些供我们使用的SpEL上下文数据,下表直接摘自Spring官方文档:

2.5、小结
对于缓存声明,spring的缓存提供了一组java注解:
- @Cacheable
- 功能:触发缓存写入,如果缓存中没有,查询数据库,存储缓存,返回结果,如果缓存中有,直接返回结果
- 应用:查询数据库方法
- @CacheEvict
- @CachePut
- 功能:更新缓存(不会影响到方法的运行)。
- 应用:新增到数据库方法
- @Caching
- 功能:重新组合要应用于方法的多个缓存操作
- 应用:上面的注解的组合使用