# How CoCache's Proxy-Based Caching Works Under the Hood

> Discover how CoCache's proxy-based caching works under the hood. Learn how JDK dynamic proxies manage distributed data layers seamlessly for developers.

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

---

**CoCache's proxy-based caching creates JDK dynamic proxies that wrap CoherentCache instances, using CoCacheInvocationHandler to intercept method calls and coordinate client-side, distributed, and source data layers without exposing complexity to callers.**

CoCache (ahoo-wang/cocache) implements a sophisticated caching mechanism that leverages Java's dynamic proxy pattern to provide coherent caching capabilities. By generating runtime proxies through `DefaultCacheProxyFactory`, the framework transparently layers multiple cache tiers behind a simple interface, allowing developers to interact with what appears to be a standard cache while benefiting from automatic consistency management across distributed systems.

## Core Architecture Components

The proxy-based system relies on several key components working in concert:

- **CacheProxyFactory** (`me.ahoo.cache.proxy.CacheProxyFactory`): The factory interface responsible for creating proxy instances from `CoCacheMetadata`.
- **DefaultCacheProxyFactory** (`me.ahoo.cache.proxy.DefaultCacheProxyFactory`): The concrete implementation that constructs the `CoherentCache` and generates the JDK proxy.
- **CoCacheInvocationHandler** (`me.ahoo.cache.proxy.CoCacheInvocationHandler`): The invocation handler that routes method calls to the appropriate underlying implementation.
- **CoherentCache** (`me.ahoo.cache.consistency.CoherentCache`): The runtime engine that synchronizes client-side, distributed, and source caches.

## The Proxy Creation Pipeline

CoCache's proxy instantiation follows a three-stage pipeline that separates cache construction from proxy generation.

### Stage 1: Constructing the Coherent Cache

In `DefaultCacheProxyFactory.create`, the factory first assembles the multi-layered cache infrastructure:

```kotlin
val clientId = clientIdGenerator.generate()
val clientSideCaching: ClientSideCache<Any> = clientSideCacheFactory.create(cacheMetadata)
val distributedCaching: DistributedCache<Any> = distributedCacheFactory.create(cacheMetadata)
val cacheSource = cacheSourceFactory.create<Any, Any>(cacheMetadata)
val keyConverter = keyConverterFactory.create<Any>(cacheMetadata)

val delegate = coherentCacheFactory.create(
    CoherentCacheConfiguration(
        cacheName = cacheMetadata.cacheName,
        clientId = clientId,
        keyConverter = keyConverter,
        clientSideCache = clientSideCaching,
        distributedCache = distributedCaching,
        cacheSource = cacheSource
    )
)

```

This creates a `CoherentCache` delegate that manages three distinct tiers:
- **Client-side cache**: Local in-process storage for low-latency reads.
- **Distributed cache**: Redis or Hazelcast for cross-JVM consistency.
- **Cache source**: The authoritative data source (database or external service).

### Stage 2: Generating the Dynamic Proxy

After constructing the delegate, `DefaultCacheProxyFactory` creates a JDK dynamic proxy that implements the user-defined interface plus internal mix-in interfaces:

```kotlin
val invocationHandler = CoCacheInvocationHandler(cacheMetadata, delegate)

return Proxy.newProxyInstance(
    this.javaClass.classLoader,
    arrayOf(
        cacheMetadata.proxyInterface.java,
        CoherentCache::class.java,
        CacheDelegated::class.java,
        CacheMetadataCapable::class.java
    ),
    invocationHandler
) as CACHE

```

The proxy implements four interfaces:
1. The user-defined cache interface (e.g., `UserCache`).
2. `CoherentCache` for accessing coherence operations.
3. `CacheDelegated` to expose the underlying delegate.
4. `CacheMetadataCapable` to provide cache metadata access.

### Stage 3: Method Interception and Routing

The `CoCacheInvocationHandler` intercepts all method invocations and applies specific routing logic:
- **Cache operations** (`get`, `put`, `invalidate`): Forwarded to the `CoherentCache` delegate.
- **Metadata queries** (`cacheName()`, `keyConverter()`): Served directly from `CoCacheMetadata`.
- **Standard methods** (`toString`, `hashCode`, `equals`): Delegated to the underlying cache or default implementations.

This centralized handling ensures that all cache operations automatically benefit from coherent behavior—including client-side shortcuts, distributed synchronization, and source fallback—without requiring callers to manage these complexities.

## Spring Framework Integration

CoCache provides first-class Spring support through dedicated configuration classes that automate proxy creation.

### Auto-Configuration Components

The Spring integration layer includes:
- **CacheProxyFactoryBean** (`me.ahoo.cache.spring.proxy.CacheProxyFactoryBean`): A Spring FactoryBean that creates cache proxies as managed beans.
- **EnableCoCacheRegistrar** (`me.ahoo.cache.spring.EnableCoCacheRegistrar`): Registers the proxy factory during context initialization.
- **CoCacheAutoConfiguration** (`me.ahoo.cache.spring.boot.starter.CoCacheAutoConfiguration`): Supplies the `DefaultCacheProxyFactory` bean with auto-configured dependencies.

### Usage Example with Spring Boot

Define your cache interface:

```kotlin
interface UserCache {
    fun getUser(id: String): User?
    fun putUser(id: String, user: User)
}

```

Inject the proxy into your service:

```kotlin
@Service
class UserService(
    @CacheProxyFactoryBean(UserCache::class) private val userCache: UserCache
) {
    fun findUser(id: String): User? = userCache.getUser(id)
}

```

The `@CacheProxyFactoryBean` annotation triggers the creation of a `CoherentCache`-backed proxy that implements `UserCache`, automatically routing calls through the multi-layer caching stack.

## Manual Proxy Creation

For non-Spring applications, instantiate the proxy factory directly:

```kotlin
val metadata = CoCacheMetadata(
    cacheName = "userCache",
    proxyInterface = UserCache::class,
    // Additional configuration: TTL, key converter, etc.
)

val proxyFactory = DefaultCacheProxyFactory(
    coherentCacheFactory,
    clientIdGenerator,
    clientSideCacheFactory,
    distributedCacheFactory,
    cacheSourceFactory,
    keyConverterFactory
)

val userCache: UserCache = proxyFactory.create(metadata)

```

Once created, the proxy operates identically to the Spring-managed version, transparently coordinating cache layers:

```kotlin
val user = userCache.getUser("123")  // Hits client-side → distributed → source
userCache.putUser("123", user)       // Updates all layers coherently

```

## Summary

- **CoCache's proxy-based caching** uses JDK dynamic proxies generated by `DefaultCacheProxyFactory` to wrap `CoherentCache` instances.
- The **CoCacheInvocationHandler** routes method calls to appropriate handlers, separating cache operations from metadata queries.
- Each proxy implements **four interfaces**: the user API, `CoherentCache`, `CacheDelegated`, and `CacheMetadataCapable`.
- The underlying **CoherentCache** coordinates three tiers: client-side caching, distributed caching, and the authoritative cache source.
- **Spring Boot integration** automates proxy creation through `CacheProxyFactoryBean` and `CoCacheAutoConfiguration`.

## Frequently Asked Questions

### What is the role of CoCacheInvocationHandler?

`CoCacheInvocationHandler` serves as the dispatch center for all proxy method invocations. Located in `me.ahoo.cache.proxy.CoCacheInvocationHandler`, it intercepts calls to the proxy and determines whether to route them to the `CoherentCache` delegate (for cache operations), serve them from `CoCacheMetadata` (for configuration queries), or handle them as standard Object methods. This single handler eliminates the need for boilerplate delegation code while ensuring consistent coherent caching behavior across all operations.

### How does CoCache handle cache misses across different layers?

When a `get` operation reaches the proxy, `CoCacheInvocationHandler` delegates to the `CoherentCache` instance, which implements a cascading read strategy. First, it checks the `ClientSideCache` for local data. If absent, it queries the `DistributedCache` (e.g., Redis). If still missing, it falls back to the `CacheSource` (database), then propagates the result back through the chain to populate the upper tiers. This automatic population ensures subsequent requests hit the faster layers.

### Can CoCache be used without the Spring Framework?

Yes. While CoCache provides convenient Spring Boot auto-configuration through `CoCacheAutoConfiguration` and `EnableCoCacheRegistrar`, the core proxy mechanism in `DefaultCacheProxyFactory` has no Spring dependencies. You can manually construct the factory with your choice of `CoherentCacheFactory`, cache source implementations, and other components, then call `create()` with a `CoCacheMetadata` instance to obtain a functional cache proxy in plain Kotlin or Java applications.

### What interfaces does the generated proxy implement?

Every proxy generated by `DefaultCacheProxyFactory` implements four interfaces: (1) the user-defined cache interface specified in `CoCacheMetadata.proxyInterface`, (2) `CoherentCache` to expose coherence operations, (3) `CacheDelegated` to allow access to the underlying delegate, and (4) `CacheMetadataCapable` to expose cache configuration like name and key converter. This multi-interface design allows both application code and framework infrastructure to interact with the proxy appropriately.