springboot的缓存
Spring缓存注解
最常用的缓存注解有如下:
- @Cacheable主要针对方法配置,能够根据方法的请求参数对其结果进行缓存。
- @CacheEvict清空缓存。用于标注在一些删除方法上。
- @CachePut保证方法被调用,又希望结果被缓存。用于标注在一些更新方法上,更新缓存。
- @EnableCaching开启基于注解的缓存。
@Cacheable("CACHE-DistrictProvince")
public List<DistrictProvinceVO> listProvinceByCache() {
省略...
}
// 把缓存名称为【CACHE-DistrictProvince】下面的对象都清除
@CacheEvict(value = "CACHE-DistrictProvince", allEntries = true)
public void clean() {
}
//
@Cacheable(value = "CACHE-DistrictCity", key = "#param['provinceId']")
public List<DistrictCityVO> listCityByCache(Map<String, Object> param) {
省略...
}
// 把缓存名称为【CACHE-DistrictCity】下面的对象key为provinceId的对象清除
@CacheEvict(value = "CACHE-DistrictCity", key = "#provinceId")
public void clean(String provinceId) {
}
spring支持的缓存
org.springframework.boot.autoconfigure.cache.CacheType 定义了几种spring缓存类型
public enum CacheType {
GENERIC,
JCACHE,
EHCACHE,
HAZELCAST,
INFINISPAN,
COUCHBASE,
REDIS,
CAFFEINE,
SIMPLE,
NONE
}
spring配置文件通过 spring.cache.type=redis
来确定使用哪个缓存
这个类也可以对具体的缓存类型指定更多的缓存配置
@ConfigurationProperties(prefix = "spring.cache")
public class CacheProperties {
private CacheType type;
private List<String> cacheNames = new ArrayList<>();
private final Caffeine caffeine = new Caffeine();
private final Couchbase couchbase = new Couchbase();
private final EhCache ehcache = new EhCache();
private final Infinispan infinispan = new Infinispan();
private final JCache jcache = new JCache();
private final Redis redis = new Redis();
}
spring的默认缓存
只需依赖spring-boot-starter-cache就可以使用spring默认的cache
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
默认缓存示例
@EnableCaching启用spring缓存
@SpringBootApplication
@EnableCaching
public class SpringbootCacheApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootCacheApplication.class, args);
}
}
@Slf4j
@SpringBootTest(classes=SpringbootCacheApplication.class)
@RunWith(SpringRunner.class)
class CacheDemoServiceTest {
@Autowired CacheManager cacheManager;
@Autowired CacheDemoService cacheDemoService;
@Test
void test() {
// 打印CacheManager的真实对象
log.info("CacheManager={}", cacheManager.getClass().getSimpleName());
}
@Test
void test1() {
String code = "99";
log.info("cacheNames={}", cacheManager.getCacheNames());
log.info("testCache={}", cacheDemoService.testCache(code));
log.info("cacheNames={}", cacheManager.getCacheNames());
log.info("testCache={}", cacheDemoService.testCache(code));
}
}
public interface CacheDemoService {
List<String> testCache(String code);
}
@Service
@Slf4j
public class CacheDemoServiceImpl implements CacheDemoService {
@Override
@Cacheable(cacheNames = "demo", key="#code")
public List<String> testCache(String code) {
log.info("执行方法 CacheDemoServiceImpl.testCache ... ");
return Arrays.asList("1","2");
}
}
打印测试信息
第一次执行,因为没有缓存,正常调用service方法
spring的默认cache是 org.springframework.cache.concurrent.ConcurrentMapCacheManager
2022-05-15 21:17:59.438 INFO 13984 --- [ main] z.l.s.service.CacheDemoServiceTest : CacheManager=ConcurrentMapCacheManager
2022-05-15 21:17:59.452 INFO 13984 --- [ main] z.l.s.service.CacheDemoServiceTest : cacheNames=[]
2022-05-15 21:17:59.486 INFO 13984 --- [ main] z.l.s.service.impl.CacheDemoServiceImpl : 执行方法 CacheDemoServiceImpl.testCache ...
2022-05-15 21:17:59.486 INFO 13984 --- [ main] z.l.s.service.CacheDemoServiceTest : testCache=[1, 2]
2022-05-15 21:17:59.486 INFO 13984 --- [ main] z.l.s.service.CacheDemoServiceTest : cacheNames=[demo]
2022-05-15 21:17:59.487 INFO 13984 --- [ main] z.l.s.service.CacheDemoServiceTest : testCache=[1, 2]
第二次执行,这时候因为有了对应的缓存,不在执行service中的方法,直接返回缓存中的值
往后再执行,打印信息与第二次的一样。
spring默认缓存配置原理
spring-boot-autoconfigure.jar
中有一个配置类SimpleCacheConfiguration
如果应用缺少CacheManager
对象,那么实例化一个ConcurrentMapCacheManager
对象
ConcurrentMapCacheManager就是CacheManager的一个实现
ConcurrentMapCacheManager和CacheManager都在 spring-context.jar中
@Configuration
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
class SimpleCacheConfiguration {
@Bean
public ConcurrentMapCacheManager cacheManager() {
ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
List<String> cacheNames = this.cacheProperties.getCacheNames();
if (!cacheNames.isEmpty()) {
cacheManager.setCacheNames(cacheNames);
}
return this.customizerInvoker.customize(cacheManager);
}
}
spring整合EhCache
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
application.properties
spring.cache.ehcache.config=classpath:ehcache.xml
ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
<!-- 磁盘缓存位置 -->
<diskStore path="java.io.tmpdir" />
<!-- 默认缓存 -->
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxElementsOnDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap" />
</defaultCache>
<!-- 测试 -->
<cache name="demo"
eternal="false"
timeToIdleSeconds="2400"
timeToLiveSeconds="2400"
maxEntriesLocalHeap="10000"
maxEntriesLocalDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
overflowToDisk="false"
memoryStoreEvictionPolicy="LRU">
</cache>
</ehcache>
缓存配置:
timeToIdleSeconds
当缓存闲置n秒后销毁
timeToLiveSeconds
当缓存存活n秒后销毁
-
name
:缓存名称。 -
maxElementsInMemory
:缓存最大个数。 -
eternal
:对象是否永久有效,一但设置了,timeout将不起作用。 -
timeToIdleSeconds
:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。 -
timeToLiveSeconds
:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。 -
overflowToDisk
:当内存中对象数量达到maxElementsInMemory
时,Ehcache将会对象写到磁盘中。 -
diskSpoolBufferSizeMB
:这个参数设置DiskStore
(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。 -
maxElementsOnDisk
:硬盘最大缓存个数。 -
diskPersistent
:是否缓存虚拟机重启期数据
Whether the disk store persists between restarts of the Virtual Machine. The default value is false. -
diskExpiryThreadIntervalSeconds
:磁盘失效线程运行时间间隔,默认是120秒。 memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是 LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。 -
clearOnFlush
:内存数量最大时是否清除
EhCache示例
EhCache示例直接使用原先的示例,不需修改
打印测试信息
第一次执行,因为没有缓存,正常调用service方法
因为在ehcache.xml配置了cache示例(name=demo),所以初次调用可以获取cacheName
执行方法 CacheController.testCache ...
CacheManager=org.springframework.cache.ehcache.EhCacheCacheManager
cacheManager.getCacheNames()=[demo]
执行方法 CacheDemoServiceImpl.testCache ...
第二次执行,这时候因为有了对应的缓存,不在执行service中的方法,直接返回缓存中的值
往后再执行,打印信息与第二次的一样。
执行方法 CacheController.testCache ...
CacheManager=org.springframework.cache.ehcache.EhCacheCacheManager
cacheManager.getCacheNames()=[demo]
spring中Ehcache缓存配置原理
spring-boot-autoconfigure.jar
中有一个配置类EhCacheCacheConfiguration
如果应用包含Cache.class
和EhCacheCacheManager.class
缺少CacheManager
对象,
那么配置文件EhCacheCacheConfiguration 生效,然后会实例化一个EhCacheCacheManager
对象
@Configuration
@ConditionalOnClass({ Cache.class, EhCacheCacheManager.class })
@ConditionalOnMissingBean(org.springframework.cache.CacheManager.class)
@Conditional({ CacheCondition.class,
EhCacheCacheConfiguration.ConfigAvailableCondition.class })
class EhCacheCacheConfiguration {
@Bean
public EhCacheCacheManager cacheManager(CacheManager ehCacheCacheManager) {
return this.customizers.customize(new EhCacheCacheManager(ehCacheCacheManager));
}
@Bean
@ConditionalOnMissingBean
public CacheManager ehCacheCacheManager() {
Resource location = this.cacheProperties.resolveConfigLocation(this.cacheProperties.getEhcache().getConfig());
if (location != null) {
return EhCacheManagerUtils.buildCacheManager(location);
}
return EhCacheManagerUtils.buildCacheManager();
}
}
EhCacheCacheManager
在spring-context-support.jar
中
spring整合redis
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
application.properties
对CacheProperties
下面的Redis
配置缓存信息
org.springframework.boot.autoconfigure.cache.CacheProperties
public static class Redis {
//redis过期时间,默认不过期
private Duration timeToLive;
// 允许 null 值
private boolean cacheNullValues = true;
// 给Key加前缀
private String keyPrefix;
// 是否使用前缀
private boolean useKeyPrefix = true;
// 允许统计
private boolean enableStatistics;
}
配置RedisProperties
org.springframework.boot.autoconfigure.data.redis.RedisProperties
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
private int database = 0;
private String url;
private String host = "localhost";
private String username;
private String password;
private int port = 6379;
private boolean ssl;
private Duration timeout;
private Duration connectTimeout;
private String clientName;
private ClientType clientType;
private Sentinel sentinel;
private Cluster cluster;
private final Jedis jedis = new Jedis();
private final Lettuce lettuce = new Lettuce();
public static class Jedis {
private Pool pool;
}
public static class Lettuce {
private Duration shutdownTimeout = Duration.ofMillis(100);
private Pool pool;
}
public static class Pool {
private int maxIdle = 8;
private int minIdle = 0;
private int maxActive = 8;
private Duration maxWait = Duration.ofMillis(-1);
private Duration timeBetweenEvictionRuns;
}
}
spring.cache.type=redis
spring.cache.redis.timeToLive=60000
spring.cache.redis.useKeyPrefix=true
spring.cache.redis.keyPrefix=PRE-
# 缓存时间:毫秒
spring.redis.host=xxx
spring.redis.port=6379
spring.redis.password=xx
spring.redis.timeout=5000
spring.redis.pool.max-idle=8
spring.redis.pool.min-idle=0
spring.redis.pool.max-active=8
spring.redis.pool.max-wait=-1
spring-boot-autoconfigure.jar
里面的
org.springframework.boot.autoconfigure.cache.CacheType
定义了几种spring缓存类型
GENERIC
、JCACHE
、EHCACHE
、REDIS
、SIMPLE
、NONE
等
Redis示例
Redis示例直接使用原先的示例,不需修改
打印测试信息
spring中Redis缓存配置原理
spring-boot-autoconfigure.jar
中有一个配置类RedisCacheConfiguration
如果应用包含RedisConnectionFactory.class
和RedisConnectionFactory
对象,
并且不能有其他CacheManager
对象,
那么配置文件RedisCacheConfiguration
会在RedisAutoConfiguration
之后生效,然后会实例化一个RedisCacheManager
对象
其中
spring-boot-autoconfigure.jar
中的类有:RedisCacheConfiguration
RedisAutoConfiguration
RedisProperties
LettuceConnectionConfiguration
JedisConnectionConfiguration
spring-data-redis.jar
中的类有:RedisConnectionFactory
RedisOperations
@Configuration
@ConditionalOnClass(RedisConnectionFactory.class)
@AutoConfigureAfter(RedisAutoConfiguration.class)
@ConditionalOnBean(RedisConnectionFactory.class)
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
class RedisCacheConfiguration {
@Bean
RedisCacheManager cacheManager(CacheProperties cacheProperties, CacheManagerCustomizers cacheManagerCustomizers,
ObjectProvider<org.springframework.data.redis.cache.RedisCacheConfiguration> redisCacheConfiguration,
ObjectProvider<RedisCacheManagerBuilderCustomizer> redisCacheManagerBuilderCustomizers,
RedisConnectionFactory redisConnectionFactory, ResourceLoader resourceLoader) {
RedisCacheManagerBuilder builder = RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(
determineConfiguration(cacheProperties, redisCacheConfiguration, resourceLoader.getClassLoader()));
List<String> cacheNames = cacheProperties.getCacheNames();
if (!cacheNames.isEmpty()) {
builder.initialCacheNames(new LinkedHashSet<>(cacheNames));
}
if (cacheProperties.getRedis().isEnableStatistics()) {
builder.enableStatistics();
}
redisCacheManagerBuilderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
return cacheManagerCustomizers.customize(builder.build());
}
}
@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(
RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(
RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
Jedis配置类
在classpath下能找到org.apache.commons.pool2.impl.GenericObjectPool.class
, JedisConnection.class
, redis.clients.jedis.Jedis.class
则Jedis配置类生效
redis.clients.jedis.Jedis.class
在jedis.jar
中
org.apache.commons.pool2.impl.GenericObjectPool.class
在commons-pools.jar
中
@Configuration
@ConditionalOnClass({ GenericObjectPool.class, JedisConnection.class, Jedis.class })
class JedisConnectionConfiguration extends RedisConnectionConfiguration {
private final RedisProperties properties;
@Bean
@ConditionalOnMissingBean(RedisConnectionFactory.class)
public JedisConnectionFactory redisConnectionFactory() throws UnknownHostException {
return createJedisConnectionFactory();
}
}
Lettuce配置类
在classpath下能找到io.lettuce.core.RedisClient.class
, `则Lettuce配置类生效
@Configuration
@ConditionalOnClass(RedisClient.class)
class LettuceConnectionConfiguration extends RedisConnectionConfiguration {
@Bean
@ConditionalOnMissingBean(RedisConnectionFactory.class)
public LettuceConnectionFactory redisConnectionFactory(
ClientResources clientResources) throws UnknownHostException {
LettuceClientConfiguration clientConfig = getLettuceClientConfiguration(
clientResources, this.properties.getLettuce().getPool());
return createLettuceConnectionFactory(clientConfig);
}
}
redis配置类
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
private int database = 0;
private String url;
private String host = "localhost";
private String password;
private int port = 6379;
private boolean ssl;
private Duration timeout;
private Sentinel sentinel;
private Cluster cluster;
private final Jedis jedis = new Jedis();
private final Lettuce lettuce = new Lettuce();
}