5.springboot-redis spring cache中篇

1.spring cache解析

1.1.RedisCache和RedisCacheManager

1.1.1.结构

RedisCacheManager结构

1.1.2.解析

  • RedisCache使用RedisCacheWriter接口,用于对redis的读写;
  • RedisCacheWriter
    • RedisCacheWriter和Cache接口的差异:
      • 所有方法都需要指定name,执行redis命令时如需加锁,name为锁的key;
      • RedisCacheWriter可根据正则清除缓存;
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
public interface RedisCacheWriter {

    //**静态方法,创建不带锁的RedisCacheWriter实例
    static RedisCacheWriter nonLockingRedisCacheWriter(RedisConnectionFactory connectionFactory) {
        Assert.notNull(connectionFactory, "ConnectionFactory must not be null!");
        return new DefaultRedisCacheWriter(connectionFactory);
    }

    //**静态方法,创建带锁的RedisCacheWriter实例
    static RedisCacheWriter lockingRedisCacheWriter(RedisConnectionFactory connectionFactory) {
        Assert.notNull(connectionFactory, "ConnectionFactory must not be null!");
        return new DefaultRedisCacheWriter(connectionFactory, Duration.ofMillis(50));
    }

    //**保存键值对,可指定过期时间
    void put(String name, byte[] key, byte[] value, @Nullable Duration ttl);

    //**根据key获取value
    byte[] get(String name, byte[] key);

    //**如果键值对不存在则保存,返回缓存中当前的value
    byte[] putIfAbsent(String name, byte[] key, byte[] value, @Nullable Duration ttl);

    //**删除键值对
    void remove(String name, byte[] key);

    //**清除符合正则的缓存
    void clean(String name, byte[] pattern);
}
  • DefaultRedisCacheWriter
    • DefaultRedisCacheWriter是DefaultRedisCacheWriter接口唯一的实现类;
    • DefaultRedisCacheWriter是作用域为包内可见,用户无法创建,只能使用DefaultRedisCacheWriter接口提供的静态方法;
    • 如果sleepTime为Zero或者为负数,则不会加锁,否则在执行redis命令时会加锁,默认为Zero;
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
class DefaultRedisCacheWriter implements RedisCacheWriter {
    //**redis连接工厂
    private final RedisConnectionFactory connectionFactory;
    //**等待锁,在有锁时线程休眠sleepTime毫秒,然后重新获取锁。如果sleepTime为Zero或者为负数,则不会加锁,否则在执行redis命令时会加锁
    private final Duration sleepTime;

    //**设置键值对
    public void put(String name, byte[] key, byte[] value, @Nullable Duration ttl) {
        ...
        //**获取到锁后执行
        execute(name, connection -> {
             //**如果ttl有效,则保存键值对并设置有效期
            if (shouldExpireWithin(ttl)) {
                connection.set(key, value, Expiration.from(ttl.toMillis(), TimeUnit.MILLISECONDS), SetOption.upsert());
            } else {  //**否则,则保存键值对
                connection.set(key, value);
            }
            return "OK";
        });
    }

    //**如果键值对不存在则保存,返回缓存中当前的value
    public byte[] putIfAbsent(String name, byte[] key, byte[] value, @Nullable Duration ttl) {
        ...
        //**获取到锁后执行
        return execute(name, connection -> {
            //**如果需要加锁,则加锁
            if (isLockingCacheWriter()) {
                doLock(name, connection);
            }

            try {
                //**键值对不存在时,设置键值对
                if (connection.setNX(key, value)) {
                    //**如果ttl有效,则设置有效期
                    if (shouldExpireWithin(ttl)) {
                        connection.pExpire(key, ttl.toMillis());
                    }
                    return null;
                }
                //**返回当前保存的值
                return connection.get(key);
            } finally {
                //**解锁
                if (isLockingCacheWriter()) {
                    doUnlock(name, connection);
                }
            }
        });
    }

    //**清除符合正则的缓存
    public void clean(String name, byte[] pattern) {
        ...
        //**获取到锁后执行
        execute(name, connection -> {
            //**是否加锁成功
            boolean wasLocked = false;
            try {
                //**如果需要加锁,则加锁,并设置加锁成功
                if (isLockingCacheWriter()) {
                    doLock(name, connection);
                    wasLocked = true;
                }
                //**根据正则获取key
                byte[][] keys = Optional.ofNullable(connection.keys(pattern)).orElse(Collections.emptySet())
                        .toArray(new byte[0][]);
                //**如果有配置的key,则全部删除
                if (keys.length > 0) {
                    connection.del(keys);
                }
            } finally {
                //**如果加锁成功并且需要加锁,则解锁
                if (wasLocked && isLockingCacheWriter()) {
                    doUnlock(name, connection);
                }
            }

            return "OK";
        });
    }

    //**加锁
    void lock(String name) {
        execute(name, connection -> doLock(name, connection));
    }

    //**解锁
    void unlock(String name) {
        executeLockFree(connection -> doUnlock(name, connection));
    }

    //**加锁
    private Boolean doLock(String name, RedisConnection connection) {
        return connection.setNX(createCacheLockKey(name), new byte[0]);
    }

    //**解锁
    private Long doUnlock(String name, RedisConnection connection) {
        return connection.del(createCacheLockKey(name));
    }

    //**检查是否加锁
    boolean doCheckLock(String name, RedisConnection connection) {
        return connection.exists(createCacheLockKey(name));
    }

    //**是否加锁
    private boolean isLockingCacheWriter() {
        return !sleepTime.isZero() && !sleepTime.isNegative();
    }

    //**执行redis命令(没有获取到锁时会一直等待,直到获取锁,然后执行redis命令)
    private <T> T execute(String name, Function<RedisConnection, T> callback) {
        RedisConnection connection = connectionFactory.getConnection();
        try {

            checkAndPotentiallyWaitUntilUnlocked(name, connection);
            return callback.apply(connection);
        } finally {
            connection.close();
        }
    }

    //**执行redis命令
    private void executeLockFree(Consumer<RedisConnection> callback) {
        RedisConnection connection = connectionFactory.getConnection();
        try {
            callback.accept(connection);
        } finally {
            connection.close();
        }
    }
   
    //**等待锁(有锁时线程休眠sleepTime毫秒,然后重新获取锁)
    private void checkAndPotentiallyWaitUntilUnlocked(String name, RedisConnection connection) {
        if (!isLockingCacheWriter()) {
            return;
        }
        try {
            while (doCheckLock(name, connection)) {
                Thread.sleep(sleepTime.toMillis());
            }
        } catch (InterruptedException ex) {
            // Re-interrupt current thread, to allow other participants to react.
            Thread.currentThread().interrupt();
            throw new PessimisticLockingFailureException(String.format("Interrupted while waiting to unlock cache %s", name),
                    ex);
        }
    }

    //**ttl是否有有效
    private static boolean shouldExpireWithin(@Nullable Duration ttl) {
        return ttl != null && !ttl.isZero() && !ttl.isNegative();
    }

    //**根据name创建锁的key
    private static byte[] createCacheLockKey(String name) {
        return (name + "~lock").getBytes(StandardCharsets.UTF_8);
    }
}
  • RedisCacheConfiguration
    • RedisCache可通过RedisCacheConfiguration配置常用选项;
    • RedisCacheConfiguration使用了建造者模式,由于RedisCacheConfiguration各项属性都为final,所以先初始化所有属性创建一个默认的对象,然后在每个初始化属性的方法中都是重新创建一个对象;
      • 创建示例:RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(60));
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
public class RedisCacheConfiguration {
    //**缓存有效期
    private final Duration ttl;
    //**是否缓存空值
    private final boolean cacheNullValues;
    //**key前缀计算器
    private final CacheKeyPrefix keyPrefix;
    //**是否使用前缀
    private final boolean usePrefix;
   
    //**key的序列化器,持有RedisSerializer的引用,key只能是string
    private final SerializationPair<String> keySerializationPair;
    //**value的序列化器,持有RedisSerializer的引用,value可以是任意类型
    private final SerializationPair<Object> valueSerializationPair;
   
    //**spring ConversionService类型转换
    private final ConversionService conversionService;

    //**静态方法,返回默认的RedisCacheConfiguration实例
    public static RedisCacheConfiguration defaultCacheConfig() {
        return defaultCacheConfig(null);
    }

    //**静态方法,返回默认的RedisCacheConfiguration实例
    //**创建一个无过期时间,允许缓存空值,key需使用前缀且前缀为"name::"格式,key序列化器为StringRedisSerializer,
    //**value序列化器这jdk序列化器,conversionService为DefaultFormattingConversionService的RedisCacheConfiguration
    public static RedisCacheConfiguration defaultCacheConfig(@Nullable ClassLoader classLoader) {
        DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
        registerDefaultConverters(conversionService);
        return new RedisCacheConfiguration(Duration.ZERO, true, true, CacheKeyPrefix.simple(),
                SerializationPair.fromSerializer(RedisSerializer.string()),
                SerializationPair.fromSerializer(RedisSerializer.java(classLoader)), conversionService);
    }

    //**创建一个RedisCacheConfiguration,并设置缓存过期时间
    public RedisCacheConfiguration entryTtl(Duration ttl) {
        Assert.notNull(ttl, "TTL duration must not be null!");
        return new RedisCacheConfiguration(ttl, cacheNullValues, usePrefix, keyPrefix, keySerializationPair,
                valueSerializationPair, conversionService);
    }
   
    ...一系列的建造者模式初始化属性的方法
   
    //**根据cacheName生成key的前缀
    public String getKeyPrefixFor(String cacheName) {
        Assert.notNull(cacheName, "Cache name must not be null!");
        return keyPrefix.compute(cacheName);
    }

    //**conversionService中添加Converter
    public void addCacheKeyConverter(Converter<?, String> cacheKeyConverter) {
        configureKeyConverters(it -> it.addConverter(cacheKeyConverter));
    }

    //**conversionService配置
    public void configureKeyConverters(Consumer<ConverterRegistry> registryConsumer) {
        ...
        registryConsumer.accept((ConverterRegistry) getConversionService());
    }

    //**注册两个Converter
    public static void registerDefaultConverters(ConverterRegistry registry) {
        Assert.notNull(registry, "ConverterRegistry must not be null!");
        registry.addConverter(String.class, byte[].class, source -> source.getBytes(StandardCharsets.UTF_8));
        registry.addConverter(SimpleKey.class, String.class, SimpleKey::toString);
    }
}
  • RedisCache
    • 使用RedisCacheWriter操作redis读写;
    • 使用cacheConfig的keySerializationPair和valueSerializationPair对key和value进行序列化和反序列化;
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
public class RedisCache extends AbstractValueAdaptingCache {
    //**使用jdk序列化器序列化NullValue
    private static final byte[] BINARY_NULL_VALUE = RedisSerializer.java().serialize(NullValue.INSTANCE);
    //**cache的name
    private final String name;
    //**RedisCacheWriter实例
    private final RedisCacheWriter cacheWriter;
    //**redis配置
    private final RedisCacheConfiguration cacheConfig;
    //**spring ConversionService类型转换
    private final ConversionService conversionService;

    //**获取value,返回object对象
    protected Object lookup(Object key) {
        //**使用cacheWriter获取value
        byte[] value = cacheWriter.get(name, createAndConvertCacheKey(key));
        if (value == null) {
            return null;
        }
       
        //**cacheConfig的value序列化器反序列化value
        return deserializeCacheValue(value);
    }

    //**获取value,返回object对象,如果value不存在,则使用valueLoader生成value
    public synchronized <T> T get(Object key, Callable<T> valueLoader) {
        //**调用lookup方法,获取value,并转换为ValueWrapper对象
        ValueWrapper result = get(key);
        //**如果value不为空,则返回value,并强制转换为指定类型
        if (result != null) {
            return (T) result.get();
        }
        //**如果value为空,则把valueLoader的结果做为value保存,并返回
        T value = valueFromLoader(key, valueLoader);
        put(key, value);
        return value;
    }

    //**保存键值对
    public void put(Object key, @Nullable Object value) {
        //**对value进行转换,如果value为null并且允许空值,则转换为NullValue
        Object cacheValue = preProcessCacheValue(value);
        //**如果value为null并且不允许空值,则抛出异常
        if (!isAllowNullValues() && cacheValue == null) {

            throw new IllegalArgumentException(String.format(
                    "Cache '%s' does not allow 'null' values. Avoid storing null via '@Cacheable(unless="#result == null")' or configure RedisCache to allow 'null' via RedisCacheConfiguration.",
                    name));
        }
        //**使用cacheConfig的value序列化器序列化value,然后使用cacheWriter保存键值对,并设置过期时间
        cacheWriter.put(name, createAndConvertCacheKey(key), serializeCacheValue(cacheValue), cacheConfig.getTtl());
    }

    //**删除键值对
    public void evict(Object key) {
        cacheWriter.remove(name, createAndConvertCacheKey(key));
    }

    //**清除所有键值对
    public void clear() {
        byte[] pattern = conversionService.convert(createCacheKey("*"), byte[].class);
        cacheWriter.clean(name, pattern);
    }

    //**对value进行转换,如果value为null并且允许空值,则转换为NullValue
    protected Object preProcessCacheValue(@Nullable Object value) {
        if (value != null) {
            return value;
        }
        return isAllowNullValues() ? NullValue.INSTANCE : null;
    }

    //**使用cacheConfig的key序列化器序列化key
    protected byte[] serializeCacheKey(String cacheKey) {
        return ByteUtils.getBytes(cacheConfig.getKeySerializationPair().write(cacheKey));
    }

    //**使用cacheConfig的value序列化器序列化value
    protected byte[] serializeCacheValue(Object value) {
        if (isAllowNullValues() && value instanceof NullValue) {
            return BINARY_NULL_VALUE;
        }
        return ByteUtils.getBytes(cacheConfig.getValueSerializationPair().write(value));
    }

    //**使用cacheConfig的value序列化器反序列化value
    protected Object deserializeCacheValue(byte[] value) {
        if (isAllowNullValues() && ObjectUtils.nullSafeEquals(value, BINARY_NULL_VALUE)) {
            return NullValue.INSTANCE;
        }
        return cacheConfig.getValueSerializationPair().read(ByteBuffer.wrap(value));
    }

    //**根据指定的key,生成保存在redis中的key
    protected String createCacheKey(Object key) {
        //**把key转换为string
        String convertedKey = convertKey(key);
        if (!cacheConfig.usePrefix()) {
            return convertedKey;
        }
        //**如果key需要前缀,则使用cacheConfig的keyPrefix计算器计算key值,默认"name::key"
        return prefixCacheKey(convertedKey);
    }

    //**把key转换为string
    protected String convertKey(Object key) {
        //**如果key为string类型,则直接返回
        if (key instanceof String) {
            return (String) key;
        }
       
        TypeDescriptor source = TypeDescriptor.forObject(key);
        //**如果key能转换为string,则转换为string,并返回
        if (conversionService.canConvert(source, TypeDescriptor.valueOf(String.class))) {
            ...
            return conversionService.convert(key, String.class);
            ...
        }

        //**调用key的toString方法转换为string,并返回
        Method toString = ReflectionUtils.findMethod(key.getClass(), "toString");
        if (toString != null && !Object.class.equals(toString.getDeclaringClass())) {
            return key.toString();
        }
        ...
    }
}

  • RedisCacheManager
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
public class RedisCacheManager extends AbstractTransactionSupportingCacheManager {
    //**redis读写
    private final RedisCacheWriter cacheWriter;
    //**redis配置
    private final RedisCacheConfiguration defaultCacheConfig;
    //**初始化缓存配置
    private final Map<String, RedisCacheConfiguration> initialCacheConfiguration;
    //**name对应的cache不存在时,是否允许创建新的cache
    private final boolean allowInFlightCacheCreation;

    //**静态方法,根据connectionFactory创建默认的RedisCacheManager
    public static RedisCacheManager create(RedisConnectionFactory connectionFactory) {
        Assert.notNull(connectionFactory, "ConnectionFactory must not be null!");
        return new RedisCacheManager(new DefaultRedisCacheWriter(connectionFactory),
                RedisCacheConfiguration.defaultCacheConfig());
    }

    //**建造者模式,根据connectionFactory创建RedisCacheManagerBuilder
    public static RedisCacheManagerBuilder builder(RedisConnectionFactory connectionFactory) {
        Assert.notNull(connectionFactory, "ConnectionFactory must not be null!");
        return RedisCacheManagerBuilder.fromConnectionFactory(connectionFactory);
    }

    //**建造者模式,根据cacheWriter创建RedisCacheManagerBuilder
    public static RedisCacheManagerBuilder builder(RedisCacheWriter cacheWriter) {
        Assert.notNull(cacheWriter, "CacheWriter must not be null!");
        return RedisCacheManagerBuilder.fromCacheWriter(cacheWriter);
    }

    //**根据初始化缓存配置加载cache
    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;
    }

    //**如果name对应的cache不存在时,并且允许创建缓存,则根据defaultCacheConfig创建新的cache
    protected RedisCache getMissingCache(String name) {
        return allowInFlightCacheCreation ? createRedisCache(name, defaultCacheConfig) : null;
    }

    //**根据name创建RedisCache
    protected RedisCache createRedisCache(String name, @Nullable RedisCacheConfiguration cacheConfig) {
        return new RedisCache(name, cacheWriter, cacheConfig != null ? cacheConfig : defaultCacheConfig);
    }

    //**建造者
    public static class RedisCacheManagerBuilder {

        private final RedisCacheWriter cacheWriter;
        //**创建默认的RedisCacheConfiguration
        private RedisCacheConfiguration defaultCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
        //**默认不初始化cache
        private final Map<String, RedisCacheConfiguration> initialCaches = new LinkedHashMap<>();
        //**是否启用事务
        private boolean enableTransactions;
        //**name对应的cache不存在时,默认允许允许创建新的cache
        boolean allowInFlightCacheCreation = true;
    }
}

1.1.3.RedisCacheManager配置示例

  • RedisCacheConfiguration
    • key序列化器为StringRedisSerializer;
    • value序列化器为GenericJackson2JsonRedisSerializer;
    • 缓存过期时间为60秒;
  • DefaultRedisCacheWriter
    • 执行redis命令时不会加锁;
  • RedisCache
    • 允许空值;
  • RedisCacheManager
    • 没有初始化cache;
    • 根据name获取cache,cache不存在时创建cache;
    • 不支持事务;
1
2
3
4
5
6
7
8
9
10
11
@Bean
@ConditionalOnMissingBean(name = "cacheManager")
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
    //**redis默认配置文件,并且设置过期时间为60秒
    RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(60));
    //**设置序列化器
    redisCacheConfiguration = redisCacheConfiguration.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json()));
    //**创建RedisCacheManager生成器
    RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(redisCacheConfiguration);
    return builder.build();
}

1.2.CompositeCacheManager

  • 组合其它的cacheManager,可同时使用多种缓存;
  • 问题:默认情况下getCache方法,当name对应的cache不存在时,会创建一个cache并返回。导致遍历的第一个cacheManager永远会返回cache,后面的cache就永远不会调用到,所以CompositeCacheManager管理的cacheManager必须初始化cache并禁止动态创建cache?;
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
public class CompositeCacheManager implements CacheManager, InitializingBean {
    //**管理的其它CacheManager
    private final List<CacheManager> cacheManagers = new ArrayList<>();
    //**是否在末尾添加一个NoOpCacheManager
    private boolean fallbackToNoOpCache = false;

    //**在末尾添加一个NoOpCacheManager,调用getCache匹配不到CacheManager的都会返回NoOpCacheManager
    public void afterPropertiesSet() {
        if (this.fallbackToNoOpCache) {
            this.cacheManagers.add(new NoOpCacheManager());
        }
    }

    //**根据name获取cache(问题:默认情况下getCache方法,当name对应的cache不存在时,会创建一个cache并返回
    //**导致遍历的第一个cacheManager永远会返回cache,后面的cache就永远不会调用到)
    public Cache getCache(String name) {
        for (CacheManager cacheManager : this.cacheManagers) {
            Cache cache = cacheManager.getCache(name);
            if (cache != null) {
                return cache;
            }
        }
        return null;
    }

    //**获取所有的name
    public Collection<String> getCacheNames() {
        Set<String> names = new LinkedHashSet<>();
        for (CacheManager manager : this.cacheManagers) {
            names.addAll(manager.getCacheNames());
        }
        return Collections.unmodifiableSet(names);
    }
}