使用Redis实现Caffeine缓存接口

## `RedisCache`抽象类实现`Cache`接口 ```kotlin abstract class RedisCache<V>( name: String, private val redisTemplate: StringRedisTemplate, private val expiration: Duration? = null, ) : Cache<String, V> { private val op = redisTemplate.opsForValue() private val prefix = "Cache:$name" private fun internalKey(key: Any) = "$prefix:$key" override fun getIfPresent(key: Any): V? { return op.get(internalKey(key))?.let { valueFromString(it) } } override fun get(key: String, mappingFunction: Function<in String, out V>): V? { return if (redisTemplate.hasKey(internalKey(key))) { getIfPresent(key) } else { val value = mappingFunction.apply(key) put(key, value) value } } override fun getAllPresent(keys: MutableIterable<*>): MutableMap<String, V> { val map = mutableMapOf<String, V>() for (key in keys) { if (key != null && redisTemplate.hasKey(internalKey(key))) { val value = getIfPresent(key) if (value != null) { map[key.toString()] = value } } } return map } override fun put(key: String, value: V) { op.set(internalKey(key), valueToString(value)) if (expiration != null) { redisTemplate.expire(internalKey(key), expiration) } } override fun putAll(map: MutableMap<out String, out V>) { for (entry in map) { put(entry.key, entry.value) } } override fun invalidate(key: Any) { redisTemplate.delete(internalKey(key)) } override fun invalidateAll(keys: MutableIterable<*>) { for (key in keys) { if (key != null) { invalidate(key) } } } override fun invalidateAll() { invalidateAll(redisTemplate.keys("$prefix:*")) } override fun estimatedSize(): Long { return redisTemplate.keys("$prefix:*").size.toLong() } override fun stats() = CacheStats.empty() override fun asMap(): ConcurrentMap<String, V> { val map = ConcurrentHashMap<String, V>() for (key in redisTemplate.keys("$prefix:*")) { val rawkey = key.toString().replace("$prefix:", "") val value = getIfPresent(rawkey) if (value != null) { map[rawkey] = value } } return map } override fun cleanUp() { } override fun policy(): Policy<String, V> { TODO("Not yet implemented") } fun valueToString(value: V): String = value.toString() abstract fun valueFromString(value: String): V } ``` ## Redis缓存实现类 ```kotlin class StringRedisCache( name: String, redisTemplate: StringRedisTemplate, expiration: Duration? = null, ) : RedisCache<String>(name, redisTemplate, expiration) { override fun valueFromString(value: String) = value } class IntRedisCache( name: String, redisTemplate: StringRedisTemplate, expiration: Duration? = null, ) : RedisCache<Int>(name, redisTemplate, expiration) { override fun valueFromString(value: String) = value.toInt() } class LongRedisCache( name: String, redisTemplate: StringRedisTemplate, expiration: Duration? = null, ) : RedisCache<Long>(name, redisTemplate, expiration) { override fun valueFromString(value: String) = value.toLong() } class BoolRedisCache( name: String, redisTemplate: StringRedisTemplate, expiration: Duration? = null, ) : RedisCache<Boolean>(name, redisTemplate, expiration) { override fun valueFromString(value: String) = value.toBoolean() } ``` ## 缓存服务类`CacheService` ```kotlin @Service class CacheService(private val redisTemplate: StringRedisTemplate) { private val logger = LoggerFactory.getLogger(CacheService::class.java) var redisEnabled: Boolean = false @PostConstruct fun init() { try { redisTemplate.hasKey("") redisEnabled = true logger.info("Use Redis Cache.") } catch (e: RedisConnectionFailureException) { logger.warn("Cannot connect to Redis server, use local memory cache.") } } fun cache(name: String, expiration: Duration? = null, maximumSize: Long? = null): Cache<String, String> { return if (redisEnabled) { StringRedisCache(name, redisTemplate, expiration) } else { localCache(expiration, maximumSize) } } fun intCache(name: String, expiration: Duration? = null, maximumSize: Long? = null): Cache<String, Int> { return if (redisEnabled) { IntRedisCache(name, redisTemplate, expiration) } else { localCache(expiration, maximumSize) } } fun boolCache(name: String, expiration: Duration? = null, maximumSize: Long? = null): Cache<String, Boolean> { return if (redisEnabled) { BoolRedisCache(name, redisTemplate, expiration) } else { localCache(expiration, maximumSize) } } fun <K, V> localCache( expiration: Duration? = null, maximumSize: Long? = null ): Cache<K, V> { val builder = Caffeine.newBuilder() if (maximumSize != null) { builder.maximumSize(maximumSize) } if (expiration != null) { builder.expireAfterWrite(expiration.toMillis(), TimeUnit.MILLISECONDS) } return builder.build() } } ```