代理与注解系统
CoCache 使用声明式、注解驱动的方式进行缓存配置。开发者只需定义带有 @CoCache 注解的缓存接口,框架会自动创建由 DefaultCoherentCache 支撑的 JDK 动态代理实现。这种系统消除了繁琐的缓存连接代码,使缓存行为完全通过注解进行配置。
概览
flowchart TD
subgraph sg_14 ["开发者定义"]
Interface["Cache Interface<br>+ @CoCache annotation"]
end
subgraph sg_15 ["启动注册"]
Enable["@EnableCoCache<br>caches = [...]"]
Registrar["EnableCoCacheRegistrar"]
Parse["CoCacheMetadataParser<br>parse interface"]
RegisterBean["Register CacheProxyFactoryBean<br>as Spring Bean"]
end
subgraph sg_16 ["Bean 创建"]
FactoryBean["CacheProxyFactoryBean<br>.getObject()"]
ProxyFactory["DefaultCacheProxyFactory<br>.create(metadata)"]
InvocationHandler["CoCacheInvocationHandler"]
Proxy["JDK Proxy<br>implements Cache interface"]
end
Interface --> Enable
Enable --> Registrar
Registrar --> Parse
Parse --> RegisterBean
RegisterBean --> FactoryBean
FactoryBean --> ProxyFactory
ProxyFactory --> InvocationHandler
InvocationHandler --> Proxy
style Interface fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style Enable fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style Registrar fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style Parse fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style RegisterBean fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style FactoryBean fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style ProxyFactory fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style InvocationHandler fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style Proxy fill:#2d333b,stroke:#6d5dfc,color:#e6edf3@EnableCoCache 注解
入口是 @EnableCoCache,一个通过 @Import 触发注册流程的 Spring 注解:
@Import(EnableCoCacheRegistrar::class)
@Target(AnnotationTarget.CLASS)
annotation class EnableCoCache(
val caches: Array<KClass<out Cache<*, *>>> = []
)在 Spring 配置类中的使用方式:
@EnableCoCache(caches = [UserProfileCache::class, ProductCache::class])
class CacheConfigurationEnableCoCacheRegistrar -- Bean 定义注册
EnableCoCacheRegistrar 实现了 Spring 的 ImportBeanDefinitionRegistrar 接口。在应用启动时,它:
- 从
@EnableCoCache注解中读取caches数组 - 将
JoinCache类型与普通Cache类型分开 - 将每个接口解析为
CoCacheMetadata或JoinCacheMetadata - 为每个缓存注册 Spring
FactoryBean定义
sequenceDiagram
autonumber
participant SC as Spring Container
participant R as EnableCoCacheRegistrar
participant P as CoCacheMetadataParser
participant F as CacheProxyFactoryBean
participant JF as JoinCacheProxyFactoryBean
SC->>R: registerBeanDefinitions(metadata, registry)
R->>R: getCacheTypes from @EnableCoCache annotation
loop For each non-JoinCache type
R->>P: parse(KClass) via toCoCacheMetadata()
P-->>R: CoCacheMetadata
R->>R: Register CoCacheMetadata bean
R->>R: Register CacheProxyFactoryBean bean
end
loop For each JoinCache type
R->>R: parse via toJoinCacheMetadata()
R->>R: Register JoinCacheProxyFactoryBean bean
end
SC->>F: getObject() [lazy bean creation]
F-->>SC: Cache proxy instanceregisterBeanDefinitions() 的核心逻辑位于第 45 行:
override fun registerBeanDefinitions(importingClassMetadata: AnnotationMetadata, registry: BeanDefinitionRegistry) {
val cacheMetadataList = resolveCacheMetadataList(importingClassMetadata)
cacheMetadataList.forEach { cacheMetadata ->
registry.registerCacheMetadata(cacheMetadata)
val beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(CacheProxyFactoryBean::class.java)
beanDefinitionBuilder.addConstructorArgValue(cacheMetadata)
beanDefinitionBuilder.setPrimary(true)
registry.registerBeanDefinition(cacheMetadata.cacheName, beanDefinitionBuilder.beanDefinition)
}
val joinCacheMetadataList = resolveJoinCacheMetadataList(importingClassMetadata)
joinCacheMetadataList.forEach { cacheMetadata ->
val beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(JoinCacheProxyFactoryBean::class.java)
beanDefinitionBuilder.addConstructorArgValue(cacheMetadata)
beanDefinitionBuilder.setPrimary(true)
registry.registerBeanDefinition(cacheMetadata.cacheName, beanDefinitionBuilder.beanDefinition)
}
}CoCacheMetadata 和 CoCacheMetadataParser
CoCacheMetadata
CoCacheMetadata 是一个数据类,保存从缓存接口中解析出的所有配置:
data class CoCacheMetadata(
override val proxyInterface: KClass<*>,
override val name: String,
val keyPrefix: String,
val keyExpression: String,
override val ttl: Long,
override val ttlAmplitude: Long,
val keyType: KType,
val valueType: KType
) : ComputedNamedCache, TtlConfiguration {
override val cacheName: String = name.ifBlank {
proxyInterface.simpleName!!
}
}如果 name 为空,cacheName 默认使用接口的简单类名。
CoCacheMetadataParser
CoCacheMetadataParser 在第 30 行将 KClass 解析为 CoCacheMetadata:
flowchart TD
Input["KClass<out Cache<*, *>>"] --> IsInterface{"Is it an interface?"}
IsInterface -->|No| Error["Throw error:<br>must be interface"]
IsInterface -->|Yes| FindAnnotation["Find @CoCache annotation<br>(or use defaults)"]
FindAnnotation --> FindSuper["Find Cache<K, V> supertype"]
FindSuper --> ExtractTypes["Extract keyType and valueType<br>from generic arguments"]
ExtractTypes --> BuildMetadata["Build CoCacheMetadata"]
style Input fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style IsInterface fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style Error fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style FindAnnotation fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style FindSuper fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style ExtractTypes fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style BuildMetadata fill:#2d333b,stroke:#6d5dfc,color:#e6edf3解析器强制要求目标必须是接口。它读取 @CoCache 注解(如果没有则使用默认值),从 Cache<K, V> 父类型中提取泛型参数,并生成 CoCacheMetadata 实例。
JDK 动态代理创建
CoCacheProxy -- 抽象 InvocationHandler
CoCacheProxy 是一个抽象的 InvocationHandler,提供核心委托逻辑:
abstract class CoCacheProxy<DELEGATE> : InvocationHandler, CacheDelegated<DELEGATE>
where DELEGATE : Cache<*, *> {
abstract val proxyInterface: Class<*>
private val declaredDefaultMethods by lazy {
proxyInterface.declaredMethods.filter { it.isDefault }
}
override fun invoke(proxy: Any, method: Method, args: Array<out Any>?): Any? {
val methodArgs = args ?: EMPTY_ARGS
if (method.isDefault && declaredDefaultMethods.contains(method)) {
return InvocationHandler.invokeDefault(proxy, method, *methodArgs)
}
return method.invoke(delegate, *methodArgs)
}
}代理区分两种方法:
- 默认方法(在接口本身上声明并带有方法体) -- 通过
InvocationHandler.invokeDefault()调用 - 抽象方法(来自
Cache<K, V>及其父接口) -- 委托给DefaultCoherentCache实例
CoCacheInvocationHandler -- 具体处理器
CoCacheInvocationHandler 继承 CoCacheProxy,增加了对 delegate 和 cacheMetadata 访问器方法的特殊处理:
class CoCacheInvocationHandler<DELEGATE>(
override val cacheMetadata: CoCacheMetadata,
override val delegate: DELEGATE
) : CacheDelegated<DELEGATE>, CacheMetadataCapable, CoCacheProxy<DELEGATE>()
where DELEGATE : Cache<*, *>, DELEGATE : NamedCache {
override fun invoke(proxy: Any, method: Method, args: Array<out Any>?): Any? {
if (DELEGATE_METHOD_SIGN == method.name) return delegate
if (CACHE_METADATA_METHOD_SIGN == method.name) return cacheMetadata
return super.invoke(proxy, method, args)
}
}这允许调用者从代理访问底层的 delegate(即 DefaultCoherentCache)和 cacheMetadata,无需强制类型转换即可进行内省。
DefaultCacheProxyFactory -- 工厂
DefaultCacheProxyFactory 在第 40 行编排缓存代理的创建:
sequenceDiagram
autonumber
participant F as DefaultCacheProxyFactory
participant CID as ClientIdGenerator
participant CSF as ClientSideCacheFactory
participant DF as DistributedCacheFactory
participant SF as CacheSourceFactory
participant KCF as KeyConverterFactory
participant CHF as CoherentCacheFactory
participant IH as CoCacheInvocationHandler
participant P as JDK Proxy
F->>CID: generate()
CID-->>F: clientId
F->>CSF: create(metadata)
CSF-->>F: ClientSideCache (L2)
F->>DF: create(metadata)
DF-->>F: DistributedCache (L1)
F->>SF: create(metadata)
SF-->>F: CacheSource (L0)
F->>KCF: create(metadata)
KCF-->>F: KeyConverter
F->>CHF: create(CoherentCacheConfiguration)
CHF-->>F: DefaultCoherentCache (delegate)
F->>IH: new CoCacheInvocationHandler(metadata, delegate)
F->>P: Proxy.newProxyInstance(classLoader, interfaces, handler)
P-->>F: CACHE proxy instance代理同时实现四个接口:
- 用户的缓存接口(如
UserProfileCache) CoherentCache<K, V>-- 完整的一致性缓存 APICacheDelegated-- 访问底层委托CacheMetadataCapable-- 访问解析的元数据
CacheProxyFactoryBean -- Spring 集成
CacheProxyFactoryBean 将 Spring FactoryBean 契约与代理工厂桥接起来:
class CacheProxyFactoryBean(private val cacheMetadata: CoCacheMetadata) :
FactoryBean<Cache<Any, Any>>, ApplicationContextAware {
override fun getObject(): Cache<Any, Any> {
val cacheProxyFactory = appContext.getBean(CacheProxyFactory::class.java)
return cacheProxyFactory.create(cacheMetadata)
}
override fun getObjectType(): Class<*> {
return cacheMetadata.proxyInterface.java
}
}它在首次调用 getObject() 时从 Spring ApplicationContext 中延迟解析 CacheProxyFactory。
JoinCache 代理流程
对于 JoinCache 接口(用于组合两个缓存值),存在一个并行的注册路径,通过 JoinCacheProxyFactoryBean 和 DefaultJoinCacheProxyFactory 实现。
sequenceDiagram
autonumber
participant F as DefaultJoinCacheProxyFactory
participant CF as CacheFactory
participant JK as JoinKeyExtractorFactory
participant SC as SimpleJoinCache
participant IH as JoinCacheInvocationHandler
participant P as JDK Proxy
F->>CF: getCache(firstCacheName)
CF-->>F: firstCache (Cache<K1, V1>)
F->>CF: getCache(joinCacheName)
CF-->>F: joinCache (Cache<K2, V2>)
F->>JK: create(metadata)
JK-->>F: JoinKeyExtractor
F->>SC: new SimpleJoinCache(firstCache, joinCache, joinKeyExtractor)
SC-->>F: delegate
F->>IH: new JoinCacheInvocationHandler(metadata, delegate)
F->>P: Proxy.newProxyInstance(classLoader, interfaces, handler)
P-->>F: JoinCache proxy instanceDefaultJoinCacheProxyFactory.create() 位于第 30 行:
- 通过
firstCacheName查找主缓存(如果名称为空则按类型查找) - 通过
joinCacheName查找关联缓存(或按类型查找) - 创建
JoinKeyExtractor,从主缓存值中提取关联键 - 将它们包装在
SimpleJoinCache委托中 - 创建实现用户
JoinCache接口的 JDK 代理
完整注册流程图
graph TB
subgraph sg_17 ["1. Annotation Parsing"]
A1["@EnableCoCache<br>caches = [MyCache::class]"]
A2["EnableCoCacheRegistrar"]
A3["CoCacheMetadataParser.parse()"]
A4["CoCacheMetadata"]
end
subgraph sg_18 ["2. Bean Definition"]
B1["Register CoCacheMetadata bean<br>(name: cacheName.CacheMetadata)"]
B2["Register CacheProxyFactoryBean<br>(name: cacheName, primary: true)"]
end
subgraph sg_19 ["3. Proxy Construction"]
C1["CacheProxyFactoryBean.getObject()"]
C2["DefaultCacheProxyFactory.create()"]
C3["Create L2 + L1 + L0 + KeyConverter"]
C4["CoherentCacheFactory.create()"]
C5["DefaultCoherentCache<br>(registers with Event Bus)"]
C6["CoCacheInvocationHandler"]
C7["JDK Proxy.newProxyInstance()"]
end
A1 --> A2 --> A3 --> A4
A4 --> B1
A4 --> B2
B2 --> C1 --> C2 --> C3 --> C4 --> C5 --> C6 --> C7
style A1 fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style A2 fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style A3 fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style A4 fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style B1 fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style B2 fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style C1 fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style C2 fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style C3 fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style C4 fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style C5 fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style C6 fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
style C7 fill:#2d333b,stroke:#6d5dfc,color:#e6edf3关键类关系
| 类/接口 | 职责 | 模块 | 源码 |
|---|---|---|---|
@EnableCoCache | 通过 @Import 触发注册 | cocache-spring | EnableCoCache.kt |
EnableCoCacheRegistrar | 解析注解,注册 Bean 定义 | cocache-spring | EnableCoCacheRegistrar.kt |
CoCacheMetadata | 解析后的缓存配置 | cocache-core | CoCacheMetadata.kt |
CoCacheMetadataParser | 基于反射的接口解析器 | cocache-core | CoCacheMetadataParser.kt |
CoCacheProxy | 支持默认方法的抽象 InvocationHandler | cocache-core | CoCacheProxy.kt |
CoCacheInvocationHandler | 带有 delegate/metadata 访问的具体处理器 | cocache-core | CoCacheInvocationHandler.kt |
DefaultCacheProxyFactory | 组装所有组件并创建代理 | cocache-core | DefaultCacheProxyFactory.kt |
CacheProxyFactoryBean | Spring FactoryBean 桥接 | cocache-spring | CacheProxyFactoryBean.kt |
JoinCacheProxyFactoryBean | JoinCache 的 Spring FactoryBean | cocache-spring | JoinCacheProxyFactoryBean.kt |
DefaultJoinCacheProxyFactory | 创建带有两个缓存组合的 JoinCache 代理 | cocache-core | DefaultJoinCacheProxyFactory.kt |
源码参考
| 文件 | 行号 | 说明 |
|---|---|---|
EnableCoCache.kt | 20-24 | @EnableCoCache 注解定义 |
EnableCoCacheRegistrar.kt | 31-98 | Bean 定义注册器 |
CoCacheMetadata.kt | 20-33 | 解析后的元数据数据类 |
CoCacheMetadataParser.kt | 30-57 | 反射解析器 |
CoCacheProxy.kt | 34-41 | 抽象 InvocationHandler |
CoCacheInvocationHandler.kt | 37-46 | 具体调用处理器 |
DefaultCacheProxyFactory.kt | 40-68 | 组装所有组件的代理工厂 |
CacheProxyFactoryBean.kt | 23-39 | Spring FactoryBean |
JoinCacheProxyFactoryBean.kt | 23-39 | JoinCache FactoryBean |
DefaultJoinCacheProxyFactory.kt | 30-65 | JoinCache 代理工厂 |
相关页面
- 架构概览 -- 高层系统架构与模块图
- 缓存层级详解 -- L0/L1/L2 层级详情
- 缓存一致性与事件总线 -- 分布式失效机制