接着来看缓存类CacheManager
从名字就能看出是管理缓存的类,CacheManager有两种,一种是Spring的,一种是javax的,就是上面所说的扩展类,但实现确实大体一致,
就接口实现入手,先从最简单的看起,从名字看就是SimpleCacheManager,提供最基本的set方法,load方法。
SimpleCacheManager在spring-context包下,5.1.4版本,rediscachemanager在spring-data-redis包下
CachingProvider:创建、配置、获取、管理和控制多个CacheManager
CacheManager:创建、配置、获取、管理和控制多个唯一命名的Cache。(一个CacheManager仅被一个CachingProvider所拥有)
Cache:一个类似Map的数据结构。(一个Cache仅被一个CacheManager所拥有)
Entry:一个存储在Cache中的key-value对
Expiry:每一个存储在Cache中的条目有一个定义的有效期,过期后不可访问、更新、删除。缓存有效期可以通过ExpiryPolicy设置
https://cloud.tencent.com/developer/article/1497762
缓存么,除了快之外,还要满足有过期时间,但是除了在redis中并没有提供响应的方法,为什么呢?我觉得既然你启动或者加载就将bean放入cache管理了
就不可能伴随过期,应该会有响应的destroy方法在实例结束运行时清理,要不不可能实例还没运行完就进行清理吧。
@Override
protected Collection<RedisCache> loadCaches() {
List<RedisCache> caches = new LinkedList<>();
for (Map.Entry<String, RedisCacheConfiguration> entry : initialCacheConfiguration.entrySet()) {
caches.add(createRedisCache(entry.getKey(), entry.getValue()));
}
return caches;
}
读取缓存的配置时间,一级缓存60s,二级缓存30s
在spring-autoconfigure-metadata.properties中的org.springframework.data.redis.cache.RedisCacheConfiguration配置此参数
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration.AutoConfigureAfter=
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,
org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration
启动时
c.y.c.b.redis.config.RedisConfiguration : [BSF][Redis]已启动,addressList:
2022-02-18 15:04:45.674 INFO 2604 --- [ main] c.y.c.b.e.c.EurekaClientConfiguration : [BSF][Eureka-Client]已启动!!! eureka.client.serviceUrl.defaultZone=http://10.:8080/eureka/
2022-02-18 15:04:47.846 WARN 2604 --- [ main] c.n.c.sources.URLConfigurationSource : No URLs will be polled as dynamic configuration sources.
2022-02-18 15:04:47.847 INFO 2604 --- [ main] c.n.c.sources.URLConfigurationSource : To enable URLs as dynamic configuration sources, define System property archaius.configurationSource.additionalUrls or make config.properties available on classpath.
2022-02-18 15:04:47.943 INFO 2604 --- [ main] c.netflix.config.DynamicPropertyFactory : DynamicPropertyFactory is initialized with configuration sources: com.netflix.config.ConcurrentCompositeConfiguration@4bf9f44b
2022-02-18 15:04:54.662 INFO 2604 --- [ main] c.y.c.b.s.ShardingJdbcConfiguration : [BSF][Sharding-jdbc]已启动!!!
2022-02-18 15:04:57.232 INFO 2604 --- [ main] com.alibaba.druid.pool.DruidDataSource : {dataSource-1} inited
2022-02-18 15:04:58.661 INFO 2604 --- [ main] com.alibaba.druid.pool.DruidDataSource : {dataSource-2} inited
2022-02-18 15:05:02.531 ERROR 2604 --- [ main] c.b.mybatisplus.MybatisConfiguration
或者在redisconfig中配置
@Configuration
public class RedisCacheConfig {
@Bean
public KeyGenerator simpleKeyGenerator() {
return (o, method, objects) -> {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(o.getClass().getSimpleName());
stringBuilder.append(".");
stringBuilder.append(method.getName());
stringBuilder.append("[");
for (Object obj : objects) {
stringBuilder.append(obj.toString());
}
stringBuilder.append("]");
return stringBuilder.toString();
};
}
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
return new RedisCacheManager(
RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory),
this.getRedisCacheConfigurationWithTtl(600), // 默认策略,未配置的 key 会使用这个
this.getRedisCacheConfigurationMap() // 指定 key 策略
);
}
private Map<String, RedisCacheConfiguration> getRedisCacheConfigurationMap() {
Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();
redisCacheConfigurationMap.put("UserInfoList", this.getRedisCacheConfigurationWithTtl(3000));
redisCacheConfigurationMap.put("UserInfoListAnother", this.getRedisCacheConfigurationWithTtl(18000));
return redisCacheConfigurationMap;
}
private RedisCacheConfiguration getRedisCacheConfigurationWithTtl(Integer seconds) {
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
redisCacheConfiguration = redisCacheConfiguration.serializeValuesWith(
RedisSerializationContext
.SerializationPair
.fromSerializer(jackson2JsonRedisSerializer)
).entryTtl(Duration.ofSeconds(seconds));
return redisCacheConfiguration;
}
}
过期时间
@Cacheable(value = "UserInfoList", keyGenerator = "simpleKeyGenerator") // 3000秒
@Cacheable(value = "UserInfoListAnother", keyGenerator = "simpleKeyGenerator") // 18000秒
@Cacheable(value = "DefaultKeyTest", keyGenerator = "simpleKeyGenerator") // 600秒,未指定的key,使用默认策略
在spring2.0前后差异
构造器差异
before
RedisCacheManager cacheManager = new RedisCacheManager(RedisTemplate redisTemplate);
after
RedisCacheManager cacheManager = new RedisCacheManager(RedisCacheWriter redisCacheWriter,RedisCacheConfiguration redisCacheConfiguration);
创建RedisCacheWriter分为有锁和无锁
static RedisCacheWriter nonLockingRedisCacheWriter(RedisConnectionFactory connectionFactory) {
Assert.notNull(connectionFactory, "ConnectionFactory must not be null!");
return new DefaultRedisCacheWriter(connectionFactory);
}
JedisConnectionFactory
/**
* Create new {@link RedisCacheWriter} with locking behavior.
*
* @param connectionFactory must not be {@literal null}.
* @return new instance of {@link DefaultRedisCacheWriter}.
*/
static RedisCacheWriter lockingRedisCacheWriter(RedisConnectionFactory connectionFactory) {
Assert.notNull(connectionFactory, "ConnectionFactory must not be null!");
return new DefaultRedisCacheWriter(connectionFactory, Duration.ofMillis(50));
}
即使是同一个缓存CacheManager管理的缓存实例,配置有可能不一样。
指定redis数据序列化
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer()))
JAVA序列化方式
序列化方式一实现:Serializable接口
序列化方式二:Externalizable显式序列化
序列化方式三:实现Serializable接口+添加writeObject()和readObject()方法。(显+隐序列化)
对了,想要使用cache记得开启缓存注解,@EnableCaching
转过头来看下CacheOperation,这里面是缓存相关注解的父类,在SpringCacheAnnotationParser中管理了子类相关
static {
CACHE_OPERATION_ANNOTATIONS.add(Cacheable.class);
CACHE_OPERATION_ANNOTATIONS.add(CacheEvict.class);
CACHE_OPERATION_ANNOTATIONS.add(CachePut.class);
CACHE_OPERATION_ANNOTATIONS.add(Caching.class);
}
解析注解的时候他们的入参,实现一模一样,只有返回值不一样,但是一模一样的代码写了三遍,为什么不判断类型动态返回呢~
@Nullable
private Collection<CacheOperation> parseCacheAnnotations(
DefaultCacheConfig cachingConfig, AnnotatedElement ae, boolean localOnly) {
Collection<? extends Annotation> anns = (localOnly ?
AnnotatedElementUtils.getAllMergedAnnotations(ae, CACHE_OPERATION_ANNOTATIONS) :
AnnotatedElementUtils.findAllMergedAnnotations(ae, CACHE_OPERATION_ANNOTATIONS));
if (anns.isEmpty()) {
return null;
}
final Collection<CacheOperation> ops = new ArrayList<>(1);
anns.stream().filter(ann -> ann instanceof Cacheable).forEach(
ann -> ops.add(parseCacheableAnnotation(ae, cachingConfig, (Cacheable) ann)));
anns.stream().filter(ann -> ann instanceof CacheEvict).forEach(
ann -> ops.add(parseEvictAnnotation(ae, cachingConfig, (CacheEvict) ann)));
anns.stream().filter(ann -> ann instanceof CachePut).forEach(
ann -> ops.add(parsePutAnnotation(ae, cachingConfig, (CachePut) ann)));
anns.stream().filter(ann -> ann instanceof Caching).forEach(
ann -> parseCachingAnnotation(ae, cachingConfig, (Caching) ann, ops));
return ops;
}
private CacheableOperation parseCacheableAnnotation(
AnnotatedElement ae, DefaultCacheConfig defaultConfig, Cacheable cacheable) {
CacheableOperation.Builder builder = new CacheableOperation.Builder();
builder.setName(ae.toString());
builder.setCacheNames(cacheable.cacheNames());
builder.setCondition(cacheable.condition());
builder.setUnless(cacheable.unless());
builder.setKey(cacheable.key());
builder.setKeyGenerator(cacheable.keyGenerator());
builder.setCacheManager(cacheable.cacheManager());
builder.setCacheResolver(cacheable.cacheResolver());
builder.setSync(cacheable.sync());
defaultConfig.applyDefault(builder);
CacheableOperation op = builder.build();
validateCacheOperation(ae, op);
return op;
}
private CacheEvictOperation parseEvictAnnotation(
AnnotatedElement ae, DefaultCacheConfig defaultConfig, CacheEvict cacheEvict) {
CacheEvictOperation.Builder builder = new CacheEvictOperation.Builder();
builder.setName(ae.toString());
builder.setCacheNames(cacheEvict.cacheNames());
builder.setCondition(cacheEvict.condition());
builder.setKey(cacheEvict.key());
builder.setKeyGenerator(cacheEvict.keyGenerator());
builder.setCacheManager(cacheEvict.cacheManager());
builder.setCacheResolver(cacheEvict.cacheResolver());
builder.setCacheWide(cacheEvict.allEntries());
builder.setBeforeInvocation(cacheEvict.beforeInvocation());
defaultConfig.applyDefault(builder);
CacheEvictOperation op = builder.build();
validateCacheOperation(ae, op);
return op;
}
private CacheOperation parsePutAnnotation(
AnnotatedElement ae, DefaultCacheConfig defaultConfig, CachePut cachePut) {
CachePutOperation.Builder builder = new CachePutOperation.Builder();
builder.setName(ae.toString());
builder.setCacheNames(cachePut.cacheNames());
builder.setCondition(cachePut.condition());
builder.setUnless(cachePut.unless());
builder.setKey(cachePut.key());
builder.setKeyGenerator(cachePut.keyGenerator());
builder.setCacheManager(cachePut.cacheManager());
builder.setCacheResolver(cachePut.cacheResolver());
defaultConfig.applyDefault(builder);
CachePutOperation op = builder.build();
validateCacheOperation(ae, op);
return op;
}
private void parseCachingAnnotation(
AnnotatedElement ae, DefaultCacheConfig defaultConfig, Caching caching, Collection<CacheOperation> ops) {
Cacheable[] cacheables = caching.cacheable();
for (Cacheable cacheable : cacheables) {
ops.add(parseCacheableAnnotation(ae, defaultConfig, cacheable));
}
CacheEvict[] cacheEvicts = caching.evict();
for (CacheEvict cacheEvict : cacheEvicts) {
ops.add(parseEvictAnnotation(ae, defaultConfig, cacheEvict));
}
CachePut[] cachePuts = caching.put();
for (CachePut cachePut : cachePuts) {
ops.add(parsePutAnnotation(ae, defaultConfig, cachePut));
}
}
JSR(JCP Java Community Process)文档查询地址,例如JSR107规范,servlet规范等等,
https://www.docs4dev.com/docs/zh/spring-framework/5.1.3.RELEASE/reference/web.html
以及各类中文版在线开发文档
https://www.docs4dev.com/docs