5.springboot-redis spring cache中篇

1.spring cache解析

1.1.RedisCache和RedisCacheManager

1.1.1.结构

5.springboot-redis spring cache中篇_第1张图片

1.1.2.解析

  • RedisCache使用RedisCacheWriter接口,用于对redis的读写;
  • RedisCacheWriter
    • RedisCacheWriter和Cache接口的差异:
      • 所有方法都需要指定name,执行redis命令时如需加锁,name为锁的key;
      • RedisCacheWriter可根据正则清除缓存;
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;
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 execute(String name, Function callback) {
		RedisConnection connection = connectionFactory.getConnection();
		try {

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

    //**执行redis命令
	private void executeLockFree(Consumer 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));
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 keySerializationPair;
	//**value的序列化器,持有RedisSerializer的引用,value可以是任意类型
	private final SerializationPair 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 cacheKeyConverter) {
		configureKeyConverters(it -> it.addConverter(cacheKeyConverter));
	}

	//**conversionService配置
	public void configureKeyConverters(Consumer 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进行序列化和反序列化;
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 get(Object key, Callable 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
public class RedisCacheManager extends AbstractTransactionSupportingCacheManager {
    //**redis读写
	private final RedisCacheWriter cacheWriter;
	//**redis配置
	private final RedisCacheConfiguration defaultCacheConfig;
	//**初始化缓存配置
	private final Map 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 loadCaches() {
		List caches = new LinkedList<>();
		for (Map.Entry 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 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;
    • 不支持事务;
@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?;
public class CompositeCacheManager implements CacheManager, InitializingBean {
    //**管理的其它CacheManager
	private final List 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 getCacheNames() {
		Set names = new LinkedHashSet<>();
		for (CacheManager manager : this.cacheManagers) {
			names.addAll(manager.getCacheNames());
		}
		return Collections.unmodifiableSet(names);
	}
}

你可能感兴趣的:(#,spring,cache)