2.8 Java帝国之动态代理

沿接口取得、代理创建、调用拦截、目标委托和返回抛错追踪动态代理生命周期,用递归陷阱和异常边界实验解释横切行为。

学习目标

  • 能沿取得接口、创建代理、拦截调用、委托目标和返回或抛错追踪一次动态代理调用
  • 能用 InvocationHandler 的输入、目标调用、返回值和异常边界解释横切行为如何插入
  • 能在正常、边界和故障场景中定位代理自调用、接口缺失或异常吞没的首个偏离,并从干净状态重放

为什么需要这一机制

“动态代理”解决的不是把业务对象变得神秘,而是把日志、事务、鉴权等横切行为放到统一的调用入口。手写在每个业务类里的重复代码,容易漏改、顺序不一,也很难证明每次调用都经过同一条策略。JDK 动态代理为接口创建代理实例,InvocationHandler 在转发真实方法前后插入行为,并必须正确处理返回值与异常。

核心合同

最小合同写成:

result=handler(proxy,method,args)result=handler(proxy,method,args)

这里的 proxy 是调用入口,method 是接口方法,args 是本次参数;处理器还必须绑定一个独立 target。最终结果不是“处理器执行过”这么简单,而是前置行为、目标调用、后置行为和异常传播共同构成的轨迹。

动态代理调用链:入口统一,目标独立处理器插入横切行为,但不能把代理入口误当成目标对象1取得接口方法合同调用证据2创建代理入口身份调用证据3拦截调用前置横切拦截边界4委托目标真实执行目标身份5返回或抛错传播语义调用证据proxy 是入口,target 才是真实执行者;异常也必须沿合同返回
专属图示:把接口约束、横切事件、目标身份和异常传播放进一条轨迹。

四个官方概念到机制证据

2.8 Java帝国之动态代理

它描述的是一条可观察的调用链,不是“自动加功能”的口号。每次运行都要保存代理身份、目标身份、方法签名、参数、前后置事件和最终判定。

深夜奏对

这个概念用于检查角色边界:调用者不应直接知道处理器细节,处理器不应把代理入口误当成目标,目标也不应丢失原始异常。

明察暗访

观察不能只看最终结果。要比较 before → target → afterbefore → target throws → after/propagate 两条路径,才能知道横切行为是否覆盖了失败场景。

Java 动态代理

它受接口约束:接口集合、方法签名和返回类型必须满足代理合同。没有接口或目标绑定错误时,正确行为是记录拒绝原因,而不是伪造成功。

五个节点到机制证据

取得接口

保存接口名称、方法签名和目标身份;缺少接口或签名不兼容时,在代理创建前拒绝。

创建代理

记录类加载器、接口顺序、代理身份和处理器绑定。代理不是目标的复制品,它只负责承接符合接口的调用。

拦截调用

为每次调用生成一个事件 ID,保存前置行为、方法和参数摘要。处理器再次调用代理自身是需要立即拒绝的故障信号。

委托目标

记录目标身份、方法调用次数和目标产生的结果或异常。不能用 proxy 代替 target,否则会把一次调用重新送回拦截器。

返回或抛错

分别验证正常返回、目标抛错和处理器自身抛错。后置清理是否执行、异常是否被包装或吞掉,都属于可测试合同。

最小可重放实现

interfaces = target.interfaces()
proxy = createProxy(loader, interfaces, handler(target))
result = proxy.call(args)
assertTrace(before, target, after, resultOrThrown)
assertTrue(resetAndRun() == baselineTrace)

这段草图只表达接口代理合同,不复制书中叙事或代码。实际复核应保存接口列表、加载器、代理与目标身份、事件顺序、返回值、异常和复位结果。

五步复核一条代理调用

分步1 / 5

1. 固定接口和目标

记录接口方法、目标对象、返回类型和预期异常。先预测基线的 before → target → after → return 轨迹。

Lab

代理入口与目标委托实验

一次只改变目标绑定或异常路径,观察事件顺序、调用深度和最终传播。

处理器前置后调用独立目标,随后返回结果

proxy=P1 → before → target=T1.call → after → return 200

判定

通过:目标调用一次,事件顺序与返回值一致

当前场景:基线委托;记录代理/目标身份、方法、前后置事件、调用深度、异常链和复位。

正常、边界与故障证据

动态代理证据矩阵先看接口与身份,再看事件顺序和异常传播观察项正常边界故障接口匹配缺失签名错身份proxy/target多代理自调用事件前后完整清理紧顺序乱异常原样传播类型边界被吞掉代理调用只有在目标身份、事件顺序和异常链都可核对时才算通过
专属图示:分别验收接口、身份、横切事件和失败传播。
场景只改变的变量预期判定必存证据
正常接口、目标和处理器均兼容前置、目标、后置顺序稳定,返回值一致身份、方法、事件、返回值
边界无接口、返回类型边界或目标抛错在代理创建或异常路径明确拒绝/传播接口集、类型、异常、清理事件
故障处理器自调用代理或吞掉异常首个递归/传播偏离可定位,修复后重放调用深度、目标次数、异常链、复位

专属因果实验

先运行基线,预测代理调用的五个节点和事件顺序;再一次只切换目标绑定、接口集合或异常路径。实验状态要同时显示代理/目标身份、调用深度、目标调用次数和结果判定,避免把“有日志”误当成“委托正确”。

Lab

代理入口与目标委托实验

一次只改变目标绑定或异常路径,观察事件顺序、调用深度和最终传播。

处理器前置后调用独立目标,随后返回结果

proxy=P1 → before → target=T1.call → after → return 200

判定

通过:目标调用一次,事件顺序与返回值一致

当前场景:基线委托;记录代理/目标身份、方法、前后置事件、调用深度、异常链和复位。

故障诊断:从代理入口向目标边界找首错

  1. 核对接口集合:确认目标实现的接口、方法签名和返回类型满足 JDK 代理合同。
  2. 核对身份绑定:比较代理、处理器和目标对象的身份,排除处理器保存了代理而非目标。
  3. 核对事件顺序:检查前置、目标、后置事件,定位递归增长、目标零调用或后置缺失。
  4. 核对异常传播:区分目标异常、处理器异常和包装异常,从清空代理与缓存的基线重放。

术语表

名词解释

本章出现的专业名词,用大白话再讲一遍。

2.8 Java帝国之动态代理

由接口、代理实例、处理器、目标对象和返回/异常路径组成的调用模型。

深夜奏对

调用者、处理器和目标对象之间的角色分工与调用对话。

明察暗访

用前后置事件观察代理横切行为的证据方法。

Java 动态代理

基于接口创建代理实例并由 InvocationHandler 负责转发的 JDK 机制。

取得接口

读取接口集合与方法签名的代理准备阶段。

创建代理

用接口、加载器和处理器形成代理调用入口的阶段。

拦截调用

处理器接收代理方法和参数并插入横切行为的阶段。

委托目标

处理器调用独立目标对象执行真实方法的阶段。

返回或抛错

按合同返回兼容值或保留异常传播语义的终点。

练习

练习

问题 1(2.8 Java帝国之动态代理、深夜奏对): 为什么处理器中调用 proxy.method() 可能造成无限递归?

问题 2(明察暗访): 动态代理为什么必须先检查接口集合和方法签名?

问题 3(Java 动态代理): 目标方法抛错时,如何证明代理没有吞掉异常?

资料与写作方式声明

本章以码农翻身权威目录界定学习范围,并结合正文列出的技术资料独立重写;不宣称复现原书正文,也不沿用原作表述。

原作版权归作者与出版社所有;本站原创教学结构与表述仅供学习交流。

本页小结

2.8 Java帝国之动态代理的关键不是给业务对象贴上“自动增强”的标签,而是能沿取得接口、创建代理、拦截调用、委托目标和返回或抛错保存证据。完成标准是识别代理自调用、接口约束和异常传播的首个偏离,并用清空状态后的重放证明横切行为可复现。

讨论

评论区加载中…