SpringCache
官网地址:https://docs.spring.io/spring-framework/docs/current/reference/html/integration.html#cache
一 理解缓存抽象
1. 官网文档
与 Spring Framework 中的其他服务一样,缓存服务是一种抽象(不是缓存实现),需要使用实际存储来存储缓存数据——也就是说,抽象使您不必编写缓存逻辑,但确实不提供实际的数据存储。这种抽象是由 org.springframework.cache.Cache
和org.springframework.cache.CacheManager
接口实现的。
2. 注意
要使用缓存抽象,您需要注意两个方面:
- 缓存声明:标识需要缓存的方法及其策略。
- 缓存配置:存储数据和从中读取数据的后备缓存。
二 使用SpringCache
1. 加入依赖
<!-- SpringCache依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
2. 在启动类上开启缓存
@EnableCaching
3. 在需要使用注解的方法上添加对应注解
@Cacheable: Triggers cache population. 添加缓存
@CacheEvict: Triggers cache eviction. 移除缓存
@CachePut: Updates the cache without interfering with the method execution. 在不干扰方法执行的情况下更新缓存
@Caching: Regroups multiple cache operations to be applied on a method. 重新组合多个缓存操作以应用于一个方法
@CacheConfig: Shares some common cache-related settings at class-level. 在类级别共享一些常见的缓存相关设置。
3. 自定义缓存配置
找到cache的自动化配置类 CacheAutoConfiguration
该类会自动导入配置对应相关的组件
static class CacheConfigurationImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { CacheType[] types = CacheType.values(); String[] imports = new String[types.length]; for (int i = 0; i < types.length; i++) { imports[i] = CacheConfigurations.getConfigurationClass(types[i]); } return imports; } }
CacheConfigurations类包含的配置
static { Map<CacheType, Class<?>> mappings = new EnumMap<>(CacheType.class); mappings.put(CacheType.GENERIC, GenericCacheConfiguration.class); mappings.put(CacheType.EHCACHE, EhCacheCacheConfiguration.class); mappings.put(CacheType.HAZELCAST, HazelcastCacheConfiguration.class); mappings.put(CacheType.INFINISPAN, InfinispanCacheConfiguration.class); mappings.put(CacheType.JCACHE, JCacheCacheConfiguration.class); mappings.put(CacheType.COUCHBASE, CouchbaseCacheConfiguration.class); mappings.put(CacheType.REDIS, RedisCacheConfiguration.class); mappings.put(CacheType.CAFFEINE, CaffeineCacheConfiguration.class); mappings.put(CacheType.SIMPLE, SimpleCacheConfiguration.class); mappings.put(CacheType.NONE, NoOpCacheConfiguration.class); MAPPINGS = Collections.unmodifiableMap(mappings); }
我们在配置文件中使用的是redis,所以此处会自动导入redis的自动配置类RedisCacheConfiguration
RedisCacheConfiguration配置类中自动配置了redis的缓存管理器
@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)); } redisCacheManagerBuilderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); return cacheManagerCustomizers.customize(builder.build()); }
redisCache配置都在对应的org.springframework.data.redis.cache.RedisCacheConfiguration配置类中
//RedisCacheConfiguration 类 private final Duration ttl; private final boolean cacheNullValues; private final CacheKeyPrefix keyPrefix; private final boolean usePrefix; private final SerializationPair<String> keySerializationPair; private final SerializationPair<Object> valueSerializationPair; private final ConversionService conversionService; @SuppressWarnings("unchecked") private RedisCacheConfiguration(Duration ttl, Boolean cacheNullValues, Boolean usePrefix, CacheKeyPrefix keyPrefix, SerializationPair<String> keySerializationPair, SerializationPair<?> valueSerializationPair, ConversionService conversionService) { this.ttl = ttl; this.cacheNullValues = cacheNullValues; this.usePrefix = usePrefix; this.keyPrefix = keyPrefix; this.keySerializationPair = keySerializationPair; this.valueSerializationPair = (SerializationPair<Object>) valueSerializationPair; this.conversionService = conversionService; }
配置源码分析
/* * Copyright 2012-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.boot.autoconfigure.cache; import java.util.LinkedHashSet; import java.util.List; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.cache.CacheProperties.Redis; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.cache.CacheManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ResourceLoader; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.cache.RedisCacheManager.RedisCacheManagerBuilder; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializationContext.SerializationPair; /** * Redis cache configuration. * * @author Stephane Nicoll * @author Mark Paluch * @author Ryon Day */ @Configuration(proxyBeanMethods = false) @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)); } redisCacheManagerBuilderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder)); return cacheManagerCustomizers.customize(builder.build()); } //获取对应的配置 private org.springframework.data.redis.cache.RedisCacheConfiguration determineConfiguration( CacheProperties cacheProperties, ObjectProvider<org.springframework.data.redis.cache.RedisCacheConfiguration> redisCacheConfiguration, ClassLoader classLoader) { // 1.调用createConfiguration方法,返回默认的缓存管理器 // 2. redisCacheConfiguration.getIfAvailable... 见下 return redisCacheConfiguration.getIfAvailable(() -> createConfiguration(cacheProperties, classLoader)); } private org.springframework.data.redis.cache.RedisCacheConfiguration createConfiguration( CacheProperties cacheProperties, ClassLoader classLoader) { Redis redisProperties = cacheProperties.getRedis(); org.springframework.data.redis.cache.RedisCacheConfiguration config = org.springframework.data.redis.cache.RedisCacheConfiguration .defaultCacheConfig(); config = config.serializeValuesWith( SerializationPair.fromSerializer(new JdkSerializationRedisSerializer(classLoader))); if (redisProperties.getTimeToLive() != null) { config = config.entryTtl(redisProperties.getTimeToLive()); } if (redisProperties.getKeyPrefix() != null) { config = config.prefixKeysWith(redisProperties.getKeyPrefix()); } if (!redisProperties.isCacheNullValues()) { config = config.disableCachingNullValues(); } if (!redisProperties.isUseKeyPrefix()) { config = config.disableKeyPrefix(); } return config; } }
5.1 redisCacheConfiguration.getIfAvailable方法实现类ClientFactoryObjectProvider
@Override public T getIfAvailable(Supplier<T> defaultSupplier) throws BeansException { return delegate().getIfAvailable(defaultSupplier); } private ObjectProvider<T> delegate() { if (this.provider == null) { this.provider = this.clientFactory.getProvider(this.name, this.type); } return this.provider; }
delegate().getIfAvailable(defaultSupplier) 在容器中通过对象查询bean。如果有,则返回容器中的对象,如果没有就返回当前对象。(通过生成的默认缓存配置去容器中查找是否有相同的Bean(是否已经存在),如果有则返回对应的Bean,如果没有则返回当前配置类)
对此,我们只需要在配置文件中新增对应的Bean RedisCacheConfiguration即可
```java
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
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.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
@EnableCaching
@EnableConfigurationProperties(CacheProperties.class)//使CacheProperties配置文件生效,可以在容器中直接使用
public class MyRedisConfigure {
//1. CacheProperties可以使用自动注解使用
// @Autowired
// CacheProperties cacheProperties;
/**
* remark:@ConfigurationProperties(prefix = "spring.cache")
* public class CacheProperties
* 配置文件中的cache配置在这个类生效,使用自定义配置后会失效
2.CacheProperties直接在方法上使用
*/
@Bean
RedisCacheConfiguration redisCacheConfiguration(CacheProperties cacheProperties){
//配置key和val的序列化方式
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new Jackson2JsonRedisSerializer<Object>(Object.class)));
config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
//获取cache的配置文件配置,使其生效
CacheProperties.Redis redisProperties = cacheProperties.getRedis();
if (redisProperties.getTimeToLive() != null) {
config = config.entryTtl(redisProperties.getTimeToLive());
}
if (redisProperties.getKeyPrefix() != null) {
config = config.prefixKeysWith(redisProperties.getKeyPrefix());
}
if (!redisProperties.isCacheNullValues()) {
config = config.disableCachingNullValues();
}
if (!redisProperties.isUseKeyPrefix()) {
config = config.disableKeyPrefix();
}
return config;
}
}
```
4. 缓存注解使用方式
执行时清除category组下 key为getOneLevelCategory的缓存
@CacheEvict(value = {"category"},key = "'getOneLevelCategory'")
执行时会清除所有category组下的缓存
@CacheEvict(value = {"category"},allEntries = true)
先清除category组下 key为getOneLevelCategory的缓存 再向category组下缓存key为temp的值
@Caching(cacheable = {@Cacheable(value = {"category"},key = "'temp'")},
evict = { @CacheEvict(value = {"category"},key = "'getOneLevelCategory'")}
)
先清除category组下 key为getOneLevelCategory的缓存。再缓存
@CachePut(value = {"category"},key = "'getOneLevelCategory'")
- 本文标签: Java Spring
- 本文链接: https://www.tianyajuanke.top/article/21
- 版权声明: 本文由吴沛芙原创发布,转载请遵循《署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)》许可协议授权