The ideal would be to use aspects, but I didn't want to delve into that as it was just for debugging to understand the actual underlying behavior.
So I did the below, using kotlin but you can adapt it to your needs.
First, created a delegator class:
class DelegatingCache<K, V>(
val name: String,
private val delegate: Cache<K, V>
) : Cache<K, V> {
private val logger = LoggerFactory.getLogger(javaClass)
override fun getIfPresent(key: K): V? = delegate.getIfPresent(key)
.also { logger.warn("$name getIfPresent(key=$key) = $it") }
override fun get(key: K, mappingFunction: Function<in K, out @PolyNull V>?): @PolyNull V =
delegate.get(key, mappingFunction)
.also { logger.warn("$name get(key=$key) = $it") }
override fun getAllPresent(keys: Iterable<K>): Map<K, V> = delegate.getAllPresent(keys)
.also { logger.warn("$name getAllPresent") }
override fun getAll(
keys: MutableIterable<K>?,
mappingFunction: Function<in MutableSet<out K>, out MutableMap<out K, out V>>?
): MutableMap<K, V>? = delegate.getAll(keys, mappingFunction)
.also { logger.warn("$name getAll") }
override fun put(key: K, value: V) = delegate.put(key, value)
.also { logger.warn("$name put(key=$key, value=$value)") }
override fun putAll(map: Map<out K, V>) = delegate.putAll(map)
.also { logger.warn("$name putAll") }
override fun invalidate(key: K) = delegate.invalidate(key)
.also { logger.warn("$name invalidate(key=$key)") }
override fun invalidateAll(keys: Iterable<K>) = delegate.invalidateAll(keys)
.also { logger.warn("$name invalidateAll(keys: Iterable<K>)") }
override fun invalidateAll() = delegate.invalidateAll()
.also { logger.warn("$name invalidateAll") }
override fun estimatedSize(): Long = delegate.estimatedSize()
.also { logger.warn("$name estimatedSize = $it") }
override fun stats(): CacheStats = delegate.stats()
.also { logger.warn("$name stats = $it") }
override fun asMap(): ConcurrentMap<K, V> = delegate.asMap()
.also { logger.warn("$name asMap") }
override fun cleanUp() = delegate.cleanUp()
.also { logger.warn("$name cleanUp") }
override fun policy(): Policy<K, V> = delegate.policy()
.also { logger.warn("$name policy = $it") }
}
Then, when you are building your Cache instances on your @Configuration
class, you just wrap the cache in this delegator class before returning it.
private fun <K, V> buildCache(
cacheName: String,
): Cache<K, V> {
return Caffeine.newBuilder()
// ...
.build<K, V>()
.let { DelegatingCache(cacheName, it) }
}
Cheers