关键在于统一接口+JDK代理:定义公共接口让子类实现,用Proxy.newProxyInstance创建代理,InvocationHandler统一拦截;若无接口则用CGLIB工厂封装,按子类动态生成代理并避免反射调用原方法导致循环或栈溢出。

要对基于多态架构的方法实现非侵入式拦截,关键在于不修改原始类结构、不侵入业务代码,同时能覆盖不同子类的同名方法调用。动态代理本身不直接支持多态方法的统一拦截(尤其当目标对象类型在运行时才确定),需结合代理机制选型与设计策略来达成目标。
明确多态场景下的代理限制
Java 中的 JDK 动态代理仅支持接口,所有被代理对象必须实现同一接口;而 CGLIB 代理基于继承,只能代理非 final 类的非 final 方法。若多态体系由抽象类 + 多个子类构成,且未统一接口:
- JDK 代理无法直接使用——缺少公共接口作为代理契约
- CGLIB 可为每个具体子类单独生成代理,但无法用一个代理对象通吃所有子类实例
- 若子类重写了父类方法,CGLIB 能拦截该重写后的方法;但若调用发生在父类引用指向子类实例(如 Animal a = new Dog()),代理仍需作用于 Dog 实例本身,而非 Animal 类型
推荐配置路径:统一接口 + JDK 代理(最轻量可靠)
这是最符合“非侵入”和“多态拦截”双重目标的方案。只需为多态体系提炼出公共行为契约,不要求修改已有类逻辑,仅新增接口并让各子类实现它(可利用 default 方法降低改造成本):
- 定义接口如 RunnableAction,含 execute() 方法
- 让 Dog、Cat 等类实现该接口(已有方法可直接委托或覆写)
- 使用 Proxy.newProxyInstance 创建代理,InvocationHandler 中统一处理前置/后置逻辑
- 后续所有多态调用走接口引用:RunnableAction action = proxy; → action.execute();
替代路径:CGLIB + 工厂封装(适用于无接口 legacy 系统)
当无法引入接口时,可用 CGLIB 为每个具体子类动态创建代理,并通过工厂屏蔽差异:
- 编写通用 MethodInterceptor,用 method.getDeclaringClass() 区分子类来源,按需执行不同拦截逻辑
- 增强器配置需显式指定 setSuperclass(target.getClass()),并确保目标类有无参构造器
- 封装 CglibProxyFactory.createProxy(Object target),内部根据 target.getClass() 自动选择适配的 Enhancer 配置
- 对外暴露统一的代理对象,调用方仍以原始类型引用接收,语义不变
避免常见陷阱
无论采用哪种方式,以下细节决定拦截是否真正生效:
- 不要在 invoke 或 intercept 中直接反射调用原方法(如 method.invoke(obj, args)),JDK 代理中会触发代理链循环,CGLIB 中会导致栈溢出
- CGLIB 代理对象的 getClass() 返回的是生成的子类,获取原始类型要用 obj.getClass().getSuperclass()
- 代理对象不能序列化传输(除非显式实现 Serializable 并处理回调)、不能跨类加载器安全共享
- final 方法、static 方法、private 方法永远无法被任何 Java 动态代理机制拦截

















