动态代理通过包装集合接口(如List/Map)并拦截其方法调用实现监控,需面向接口编程、避免代理不可变集合,并在InvocationHandler中埋点统计频率与耗时;替代方案包括装饰器模式、继承扩展和字节码增强。

动态代理本身不直接监控集合变量,但可以通过包装集合操作入口(如自定义 List/Map 接口实现类),再用动态代理拦截其方法调用,从而统计存取频率与耗时。关键在于:把集合操作统一收口到接口、让代理在调用前后埋点。
选择可代理的集合接口
Java 动态代理只能代理接口,不能代理具体类(如 ArrayList)。因此必须面向接口编程:
- 使用
List、Map、Set等标准接口,而非ArrayList或HashMap实例 - 若已有具体类实例,需先封装为接口实现(例如写一个
TrackedArrayList实现List) - 避免代理
Arrays.asList()返回的不可变集合——它不支持修改,且部分实现在 JDK 内部,无法安全代理
编写 InvocationHandler 埋点逻辑
代理的核心是 InvocationHandler,它在每次方法调用时触发:
- 记录方法名(如
get、add、put、containsKey)和参数大小(如索引值、key 的哈希码) - 用
System.nanoTime()精确统计执行耗时,避免currentTimeMillis()的毫秒级粗粒度 - 用线程安全的计数器(如
ConcurrentHashMap)累计各方法调用次数 - 对高频方法(如
get(int))或慢操作(如remove(Object)在大 ArrayList 中)可额外打 warning 日志
创建并使用代理集合实例
以监控一个 List 为例:
- 先准备真实对象:
Listtarget = new ArrayList(); - 通过
Proxy.newProxyInstance()创建代理:ListtrackedList = (List ) Proxy.newProxyInstance(..., new TrackingHandler(target)); - 后续所有
trackedList.add()、trackedList.get(0)都会经过 handler 统计 - 注意:代理对象不继承
ArrayList特有方法(如ensureCapacity()),只能调用List接口中声明的方法
补充:比纯动态代理更实用的替代方案
对集合监控这类通用需求,直接手写代理略重。可考虑:
-
装饰器模式:写一个
MonitoringList包装目标List,复用性高、无反射开销、调试友好 -
继承扩展:如
public class TrackedArrayList,重写关键方法添加埋点(适合可控环境)extends ArrayList -
Agent 字节码增强(如 ByteBuddy):在 JVM 启动时自动为所有
ArrayList方法织入监控逻辑,无需改业务代码











