JDK动态代理可通过CacheInvocationHandler实现缓存代理:调用前生成含接口名、方法名、参数类型的稳定key,查ConcurrentHashMap缓存;命中则直接返回,未命中则反射执行原方法并缓存结果。

Java 中可以通过动态代理(如 JDK 动态代理或 CGLIB)为方法调用添加缓存逻辑,实现轻量级的缓存代理模式。核心思路是:在目标方法执行前,先查缓存;命中则直接返回,不调用原方法;未命中则执行原方法,并将结果写入缓存后再返回。
使用 JDK 动态代理 + ConcurrentHashMap 实现缓存代理
JDK 动态代理要求目标类实现接口,适合基于接口编程的场景。缓存可使用 ConcurrentHashMap 存储,key 由接口名 + 方法名 + 参数值(序列化或生成哈希)组成,value 为方法返回值。
- 定义一个通用缓存处理器:
CacheInvocationHandler,实现InvocationHandler - 在
invoke方法中:生成唯一 key → 尝试从缓存 get → 命中则返回;否则反射调用原方法 → put 进缓存 → 返回结果 - 注意参数需可比较(如 String、Integer 等基本类型包装类或自定义对象需重写
equals/hashCode),或用Arrays.deepHashCode和Arrays.deepEquals处理数组
关键细节:如何生成稳定可靠的缓存 key
key 不稳定会导致缓存失效或误命中。推荐组合以下信息:
- 目标接口全限定名(
method.getDeclaringClass().getName()) - 方法名(
method.getName()) - 参数类型签名(如
"String,Integer",避免仅靠参数值导致不同重载方法冲突) - 参数值的标准化哈希(对每个参数调用
Objects.hashCode(arg),再组合成 long 或字符串)
例如:"com.example.Service.doWork:java.lang.String,java.lang.Integer#123#456" 可作为 key 的一种安全形式。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
简单示例:缓存一个计算斐波那契数的方法
假设接口 Calculator 有方法 int fib(int n),代理后首次调用 fib(10) 会真实计算并缓存,后续调用直接返回缓存值。
- 创建
CacheInvocationHandler,内部持有一个ConcurrentMap<String, Object> - 代理创建:
Proxy.newProxyInstance(loader, new Class[]{Calculator.class}, handler) - 代理对象调用
fib(10)时,handler 自动生成 key,查缓存、调方法、存结果一气呵成
注意事项与局限性
这种简易缓存代理不支持过期、刷新、大小限制等高级特性,适用于低复杂度、读多写少、方法幂等的场景。
- 方法必须是 public,且不能是 static / final(JDK 代理无法拦截)
- 返回值和参数建议为可序列化或不可变对象,避免缓存被意外修改
- 若目标类无接口,可用 CGLIB 代理,但需引入依赖,且代理的是类而非接口,需注意构造函数和 final 方法限制
- 异常不会被缓存(通常合理),如需缓存异常结果,需在 invoke 中显式捕获并存储

















