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

> Learn how CoCache integrates with Spring's cache abstraction using @EnableCoCache proxy beans and CoCacheManager for seamless caching. A complete guide.

- Repository: [Ahoo Wang/cocache](https://github.com/ahoo-wang/cocache)
- Tags: how-to-guide
- Published: 2026-02-23

---

**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`](https://github.com/ahoo-wang/cocache/blob/main/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.

```kotlin
@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`](https://github.com/ahoo-wang/cocache/blob/main/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`](https://github.com/ahoo-wang/cocache/blob/main/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

```kotlin
// 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`](https://github.com/ahoo-wang/cocache/blob/main/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`](https://github.com/ahoo-wang/cocache/blob/main/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.

```kotlin
@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`](https://github.com/ahoo-wang/cocache/blob/main/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:

```kotlin
// 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:

```kotlin
@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

- **`@EnableCoCache`** triggers `EnableCoCacheRegistrar` to scan and register proxy beans for cache interfaces defined in [`cocache-spring/src/main/kotlin/me/ahoo/cache/spring/EnableCoCache.kt`](https://github.com/ahoo-wang/cocache/blob/main/cocache-spring/src/main/kotlin/me/ahoo/cache/spring/EnableCoCache.kt).
- **`CoSpringCache`** implements Spring's `Cache` interface in [`cocache-spring-cache/src/main/kotlin/me/ahoo/cache/spring/cache/CoSpringCache.kt`](https://github.com/ahoo-wang/cocache/blob/main/cocache-spring-cache/src/main/kotlin/me/ahoo/cache/spring/cache/CoSpringCache.kt), delegating operations to the underlying CoCache instance.
- **`CoCacheManager`** extends `AbstractCacheManager` in [`cocache-spring-cache/src/main/kotlin/me/ahoo/cache/spring/cache/CoCacheManager.kt`](https://github.com/ahoo-wang/cocache/blob/main/cocache-spring-cache/src/main/kotlin/me/ahoo/cache/spring/cache/CoCacheManager.kt), exposing all CoCaches through Spring's standard `CacheManager` API.
- **`SpringCacheFactory`** in [`cocache-spring/src/main/kotlin/me/ahoo/cache/spring/SpringCacheFactory.kt`](https://github.com/ahoo-wang/cocache/blob/main/cocache-spring/src/main/kotlin/me/ahoo/cache/spring/SpringCacheFactory.kt) bridges Spring's `BeanFactory` with CoCache's resolution mechanism.
- **Auto-configuration** via `cocache-spring-boot-starter` automatically registers the `CoCacheManager` bean through [`CoCacheAutoConfiguration.kt`](https://github.com/ahoo-wang/cocache/blob/main/CoCacheAutoConfiguration.kt), enabling immediate use of `@Cacheable` and other Spring caching annotations.

## 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.