How CoCache Integrates with Spring's Cache Abstraction: A Complete Guide

CoCache integrates with Spring's caching abstraction by registering proxy beans via @EnableCoCache, wrapping them in CoSpringCache implementations of Spring's Cache interface, and exposing them through a CoCacheManager that extends Spring's AbstractCacheManager.

The CoCache library (ahoo-wang/cocache) provides a high-performance, distributed caching solution that seamlessly plugs into Spring's standard caching infrastructure. Through a dedicated Spring integration module, CoCache exposes its cache implementations as standard Spring Cache beans, allowing developers to use familiar annotations like @Cacheable while leveraging CoCache's advanced features such as client-side caching and TTL management.

The Entry Point: @EnableCoCache and Proxy Registration

Integration begins with the @EnableCoCache annotation, which triggers the EnableCoCacheRegistrar. When added to a Spring configuration class, this registrar scans the cache interfaces specified in the caches attribute and registers proxy beans for each one.

In cocache-spring/src/main/kotlin/me/ahoo/cache/spring/EnableCoCacheRegistrar.kt, the registrar creates a cache metadata bean for each interface and registers either a CacheProxyFactoryBean (for standard caches) or a JoinCacheProxyFactoryBean (for join caches) into the Spring container. These factory beans produce proxies that implement both the cache interface and Spring's caching contracts.

@EnableCoCache(caches = [UserCache::class, OrderCache::class])
@SpringBootApplication
class Application

The annotation is defined in cocache-spring/src/main/kotlin/me/ahoo/cache/spring/EnableCoCache.kt, which imports the registrar configuration that handles the actual bean registration logic.

Bridging CoCache to Spring's Cache Interface

Each proxy bean ultimately produces a CoSpringCache, which implements org.springframework.cache.Cache. Located in cocache-spring-cache/src/main/kotlin/me/ahoo/cache/spring/cache/CoSpringCache.kt, this class delegates every cache operation to the underlying Cache<Any, Any?> while exposing Spring's standard semantics.

The adapter handles:

  • get(Object key): Returns a ValueWrapper containing the cached value
  • put(Object key, Object value): Delegates to the underlying CoCache's set operation
  • evict(Object key): Maps to CoCache's delete operation
  • clear(): Invokes the cache's clear mechanism
// CoSpringCache wraps any CoCache implementation
class CoSpringCache(
    private val cache: Cache<Any, Any?>,
    private val name: String
) : Cache {
    override fun getName(): String = name
    override fun getNativeCache(): Any = cache
    
    override fun get(key: Any): Cache.ValueWrapper? {
        val value = cache[key]
        return value?.let { SimpleValueWrapper(it) }
    }
}

CacheManager Integration via CoCacheManager

The CoCacheManager class extends Spring's AbstractCacheManager, making all CoCache instances available to Spring's caching infrastructure. Defined in cocache-spring-cache/src/main/kotlin/me/ahoo/cache/spring/cache/CoCacheManager.kt, this manager obtains cache instances from a CacheFactory and wraps them in CoSpringCache objects.

The manager relies on SpringCacheFactory (cocache-spring/src/main/kotlin/me/ahoo/cache/spring/SpringCacheFactory.kt) to resolve cache beans from the Spring BeanFactory. This allows Spring's dependency injection container to manage the lifecycle of CoCache instances while presenting them through the standard CacheManager interface.

@Bean
fun cacheManager(cacheFactory: CacheFactory): CoCacheManager {
    return CoCacheManager(cacheFactory)
}

Once registered, application code can inject Spring's CacheManager and retrieve caches by name, with all operations delegated to the underlying CoCache implementation.

Spring Boot Auto-Configuration

When using the cocache-spring-boot-starter dependency, the integration happens automatically via CoCacheAutoConfiguration in cocache-spring-boot-starter/src/main/kotlin/me/ahoo/cache/spring/boot/starter/CoCacheAutoConfiguration.kt. This auto-configuration class registers a CoCacheManager bean as soon as the starter is on the classpath, eliminating the need for manual bean definition.

The auto-configuration ensures that:

  • The CoCacheManager is available for injection throughout the application context
  • Cache beans resolved by SpringCacheFactory are automatically discovered
  • Standard Spring caching annotations (@Cacheable, @CachePut, @CacheEvict) trigger CoCache operations

Practical Implementation Example

The following example demonstrates defining a CoCache-backed interface, enabling it with Spring, and using standard Spring caching patterns:

// 1. Define the cache interface with CoCache annotations
@CoCache(keyPrefix = "user:", ttl = 120)
@GuavaCache(maximumSize = 1_000_000, expireUnit = TimeUnit.SECONDS, expireAfterAccess = 120)
interface UserCache : Cache<String, User>

// 2. Enable CoCache in your Spring Boot application
@EnableCoCache(caches = [UserCache::class])
@SpringBootApplication
class Application

// 3. Use Spring's caching abstraction in your service layer
@Service
class UserService(
    private val cacheManager: CacheManager,
    private val userCache: UserCache  // Direct injection also works
) {
    // Method 1: Using CacheManager programmatically
    fun findUser(id: String): User? {
        return cacheManager.getCache("UserCache")?.get(id, User::class.java)
    }

    // Method 2: Using Spring's declarative caching
    @Cacheable(cacheNames = ["UserCache"], key = "#id")
    fun loadUser(id: String): User {
        return userRepository.findById(id)
    }

    // Method 3: Direct CoCache API usage (bypasses Spring abstraction)
    fun getUserDirectly(id: String): User? {
        return userCache[id]
    }
}

You can also customize client-side caching behavior by providing your own beans:

@Configuration
class UserCacheConfiguration {
    @Bean
    fun customizeUserClientSideCache(
        @Qualifier("UserCache.CacheMetadata") metadata: CoCacheMetadata
    ): ClientSideCache<User> {
        return MapClientSideCache(ttl = metadata.ttl, ttlAmplitude = metadata.ttlAmplitude)
    }

    @Bean
    fun customizeUserCacheSource(): CacheSource<String, User> = CacheSource.noOp()
}

Summary

Frequently Asked Questions

How does CoCache support Spring's @Cacheable annotation?

CoCache supports @Cacheable by implementing Spring's Cache interface through CoSpringCache and registering these caches with a CoCacheManager. When you annotate a method with @Cacheable and specify a cache name that matches your CoCache interface, Spring invokes the CoSpringCache.put() and CoSpringCache.get() methods, which delegate to the underlying CoCache implementation.

Can I use CoCache without the Spring Boot starter?

Yes, you can use CoCache in standard Spring applications by manually configuring the integration beans. Import the cocache-spring and cocache-spring-cache modules, then define a CoCacheManager bean that accepts a SpringCacheFactory. You must also add @EnableCoCache to trigger the proxy bean registration, but you do not need the auto-configuration provided by the starter.

What is the difference between injecting the CacheManager versus the cache interface directly?

Injecting the CacheManager provides access to Spring's generic caching API and works with @Cacheable annotations, while injecting the specific cache interface (e.g., UserCache) gives you direct access to CoCache's extended API methods that may not be available through the standard Spring Cache interface. Both approaches operate on the same underlying cache instance.

How does CoCache handle cache name resolution in Spring?

The cache name used in Spring annotations (such as @Cacheable(cacheNames = ["UserCache"])) corresponds to the bean name generated by EnableCoCacheRegistrar, which is typically the simple class name of your cache interface. The CoCacheManager uses SpringCacheFactory to resolve these names to the actual CoCache proxy beans registered in the Spring context.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →