Java中动态代理通过InvocationHandler实现在运行时拦截方法调用,支持缓存拦截(查/存缓存)与精准/批量缓存清理,需规避Object类默认方法干扰,确保缓存键稳定性和代理生命周期安全。

Java 中 Object 类本身不直接参与动态代理的缓存拦截与清理,但它作为所有类的根父类,在代理逻辑中承担关键角色:方法签名统一、反射调用基础、以及 equals/hashCode/toString 等默认行为可能影响缓存键生成。真正实现缓存拦截与清理的是基于 InvocationHandler 的动态代理机制,配合缓存容器(如 ConcurrentHashMap 或 Caffeine)和自定义规则。
缓存拦截:代理层拦截方法调用并查/存缓存
通过 InvocationHandler 拦截目标对象方法调用,在执行前检查缓存;命中则直接返回,未命中则调用原方法并将结果写入缓存。
- 缓存键通常由 类名 + 方法名 + 参数值(经标准化处理) 构成,注意
Object的hashCode和equals可能被重写,需确保参数对象具备稳定、一致的哈希行为 - 基本类型、字符串、包装类、不可变对象可直接用于键;含可变字段或未重写
equals/hashCode的自定义对象,建议用Objects.hash(...)或序列化后取摘要(如 SHA-256)避免哈希冲突 - 示例片段:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getDeclaringClass() == Object.class) { return method.invoke(this.target, args); // 直接委托 Object 方法,不缓存 } String cacheKey = buildCacheKey(method, args); Object cached = cache.get(cacheKey); if (cached != null) return cached; Object result = method.invoke(target, args); cache.put(cacheKey, result); return result; }
缓存清理:识别“写操作”方法并主动失效对应缓存
不能仅靠 LRU 或 TTL 自动清理,需在代理中识别增删改类方法(如命名含 save、update、delete、clear),触发相关缓存条目删除。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 清理策略分两类:精准失效(如
updateUser(User u)删除findUserById(u.getId())对应键)和 批量失效(如clearAllUsers()清空整个用户缓存区) - 可通过方法名约定、自定义注解(如
@CacheEvict(key = "#user.id"))、或参数类型+方法签名组合推断影响范围 -
Object类方法(如clone()、finalize())不涉及业务数据,无需清理;但若代理对象自身被回收,需在InvocationHandler中监听或配合弱引用管理代理生命周期
注意 Object 方法的特殊处理
动态代理生成的代理类也继承 Object,其 toString()、hashCode()、equals(Object) 默认行为会作用于代理实例本身,而非目标对象——这可能导致缓存键误判或调试困难。
立即学习“Java免费学习笔记(深入)”;
- 务必在
invoke中对method.getDeclaringClass() == Object.class做快速分支,直接委托给目标对象或按代理语义处理(如proxy.toString()应返回"Proxy for " + target.toString()) - 若目标对象重写了
equals且业务依赖该逻辑(如缓存键比较),代理层不应覆盖,而应在构建缓存键时显式调用目标对象的equals或使用独立键生成器 - 避免把代理对象本身作为缓存值存储(易引发内存泄漏),缓存值应为业务返回结果,代理仅作拦截容器
轻量级实践建议
不依赖 Spring AOP 时,可用 JDK 动态代理 + 简单 ConcurrentHashMap 实现基础能力,重点控制好键生成与清理边界。
- 用
ConcurrentHashMap<string object></string>存缓存,搭配LongAdder统计命中率便于验证 - 为防止缓存雪崩,读操作加简单双重检查(DCL)或使用
computeIfAbsent;写操作清理时用remove或replaceAll配合正则匹配前缀 - 测试时特别验证:
new User(1).equals(new User(1))是否为 true、Arrays.asList(1,2).hashCode()是否稳定、null 参数是否被正确处理

















