How to Use SpEL Expressions for Dynamic Cache Key Generation in @CoCache
Use the keyExpression attribute in the @CoCache annotation to define Spring Expression Language (SpEL) strings that dynamically generate cache keys from method arguments, Spring beans, or computed values.
The @CoCache annotation from the ahoo-wang/cocache repository provides the foundation for automatic cache proxy generation in Spring applications. While static configuration via keyPrefix offers basic namespacing, the keyExpression attribute unlocks dynamic key generation by evaluating SpEL expressions against method parameters at runtime.
Understanding the @CoCache Key Generation Pipeline
When a method annotated with @CoCache is invoked, the CoCache infrastructure constructs the final cache key through a three-step resolution process.
Static Prefix Handling
First, if the keyPrefix attribute is set on the annotation, CoCache prepends this static string to the generated key. This parameter is defined in [CoCache.kt](https://github.com/ahoo-wang/cocache/blob/main/cocache-api/src/main/kotlin/me/ahoo/cache/api/annotation/CoCache.kt#L30) and provides a namespace-like separation for different cache instances.
Dynamic SpEL Evaluation
If keyExpression contains a non-blank SpEL string, the framework parses it using Spring's SpelExpressionParser and evaluates it against the method's execution context. The SpringKeyConverterFactory detects this configuration and instantiates an ExpKeyConverter to handle the evaluation. The resulting object is converted to a String and forms the dynamic portion of the cache key.
Default Fallback Behavior
When keyExpression is empty or omitted, CoCache delegates to the DefaultKeyConverterFactory. This fallback implementation concatenates the method name with serialized parameter values, generating deterministic but less flexible keys.
Configuring SpEL Expressions in @CoCache
The keyExpression attribute accepts any valid SpEL string. Within these expressions, you can reference method arguments by name (e.g., #userId), access the root object (#root), call methods on parameters, or invoke Spring beans using the @beanName syntax.
@CoCache(keyExpression = "#{#user.id}_#{#operation}")
interface UserActivityCache : Cache<String, Activity>
Composite keys combine multiple arguments into a single string, while bean references allow external key generation logic to remain decoupled from the cache interface.
Practical SpEL Expression Examples
The following patterns demonstrate common use cases for dynamic cache key generation in CoCache.
Accessing Method Arguments
Reference individual parameters directly to create targeted cache keys.
// Uses the first parameter as the key
@CoCache(keyExpression = "#{#root}")
interface UserCache : Cache<String, User>
// Combines multiple parameters with a delimiter
@CoCache(keyExpression = "'user:' + #userId + ':' + #tenantId")
interface TenantUserCache : Cache<String, User>
Invoking Spring Beans
Delegate complex key generation to dedicated Spring components.
@CoCache(keyExpression = "#{@uuidGenerator.nextId()}")
interface SessionCache : Cache<String, Session>
Combining Prefix and Expression
Use keyPrefix for static namespaces alongside dynamic expressions.
@CoCache(keyPrefix = "order:", keyExpression = "#orderId")
interface OrderCache : Cache<String, Order>
How SpEL Evaluation Works Under the Hood
The CoCache framework processes SpEL expressions through a dedicated conversion pipeline. When [CoCacheMetadataParser.kt](https://github.com/ahoo-wang/cocache/blob/main/cocache-core/src/main/kotlin/me/ahoo/cache/annotation/CoCacheMetadataParser.kt) parses the annotation, it extracts the keyExpression string into CoCacheMetadata. During cache operation execution, [ExpKeyConverter.kt](https://github.com/ahoo-wang/cocache/blob/main/cocache-core/src/main/kotlin/me/ahoo/cache/converter/ExpKeyConverter.kt) evaluates the parsed expression against the method's argument array, converting the result to a string representation. This evaluated string is then optionally prefixed and wrapped in a CacheKey object for the underlying cache provider (Caffeine, Guava, or Redis).
Summary
- Use
keyExpressionin@CoCacheto define SpEL strings that generate dynamic cache keys at runtime. - Reference method arguments using
#paramNamesyntax or#rootfor the first argument. - Access Spring beans within expressions using
@beanName.method()syntax for complex key generation logic. - Combine with
keyPrefixto maintain static namespaces while allowing dynamic key components. - Fallback behavior automatically uses method name concatenation when
keyExpressionis omitted.
Frequently Asked Questions
What is the default cache key strategy when keyExpression is not specified?
When keyExpression is empty, CoCache uses the DefaultKeyConverterFactory to generate keys by concatenating the method name with its parameter values. This produces deterministic keys based on the method signature and arguments without requiring explicit SpEL configuration.
Can I access Spring beans inside a SpEL expression for cache keys?
Yes. Use the @beanName syntax to reference any Spring bean in the application context. For example, #{@myKeyGenerator.generate(#id)} invokes the generate method on a bean named myKeyGenerator, passing the id parameter from the cached method.
How do I handle null values in SpEL cache key expressions?
Use the null-safe navigation operator ?. within your SpEL expression. For example, #{#user?.id} evaluates to null if user is null, rather than throwing an exception. You can also provide default values using the Elvis operator: #{#user?.id ?: 'anonymous'}.
Does the keyPrefix support SpEL expressions or only static strings?
The keyPrefix attribute only accepts static strings. For dynamic prefix behavior, omit keyPrefix and include the entire key logic within keyExpression, such as "'dynamic:' + #tenant + ':' + #id".
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →