核心思想比喻:找个"万能秘书"帮你干活
想象一下,你是老板(调用者 ),有很多事情(方法调用)要处理,比如签合同、开会、回邮件。但你不想亲自处理所有细节,特别是那些重复性的工作(比如记录每个操作日志、检查权限、处理错误等)。
于是你雇了一个万能秘书(动态代理对象) 。这个秘书的特点是:
- 她/他必须遵守你的"工作手册"(接口) :秘书只会处理工作手册里写明的事情(接口中定义的方法)。你告诉秘书:"以后所有找我签合同、开会、回邮件的事情,都先找你"。
- 她/他有个"万能处理本"(InvocationHandler) :当有人真的来找秘书办事(调用代理对象的方法)时,秘书不会自己决定怎么做。她/他会立刻翻开她的"万能处理本",把这次办事的详细信息(哪个方法?什么参数? )记录在本子上,然后严格按照本子上写的步骤(你写的代码) 去处理这件事。
- 秘书是"临时工",按需生成 :这个秘书不是你提前招聘好的固定员工。而是在你确定了工作手册(接口)和处理本(
InvocationHandler
)的内容后,瞬间"变"出来的一个人。这就是"动态"的含义。
映射到技术概念:
- 老板(调用者) :你的代码中需要调用某个对象方法的代码。
- 工作手册(接口) :定义了一组方法的 Java 接口 (
Interface
)。 - 原始员工(目标对象/被代理对象) :真正实现接口方法、完成核心业务逻辑的对象实例(
RealObject
)。老板本来应该直接找这位员工。 - 万能秘书(动态代理对象) :在运行时动态生成的类 (
$Proxy0
,$Proxy1
, ...) 的实例。它实现了你指定的接口。 - 万能处理本(InvocationHandler) :一个实现了
java.lang.reflect.InvocationHandler
接口的对象。它只有一个关键方法invoke
。代理对象所有方法的调用都会被"转发"到这个invoke
方法里。 - 办事请求(方法调用) :对代理对象方法的调用。
- "变"出来(动态生成) :Java 运行时系统(在 Android 中是 ART/Dalvik)利用
java.lang.reflect.Proxy
类在内存中创建代理类的字节码并加载它。
详解动态代理
1. 如何使用 (How to Use)
在 Android 中使用动态代理的步骤非常清晰:
-
定义接口 (Interface):
arduino// 你的"工作手册" public interface IService { void doSomething(String task); int calculate(int a, int b); }
-
实现目标对象 (Real Object):
java// 真正的"核心员工",干实活的 public class RealService implements IService { @Override public void doSomething(String task) { Log.d("RealService", "Doing real work: " + task); } @Override public int calculate(int a, int b) { return a + b; // 简单示例,实际可能是复杂计算 } }
-
实现 InvocationHandler:
typescript// 秘书的"万能处理本" public class MyInvocationHandler implements InvocationHandler { private Object realObject; // 保存对真实核心员工的引用 public MyInvocationHandler(Object realObject) { this.realObject = realObject; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 秘书接到办事请求,翻开处理本执行以下步骤: // 1. 前置处理 (Before Advice) Log.d("Proxy", "秘书: 有人要调用方法 '" + method.getName() + "'"); if (args != null) { Log.d("Proxy", "秘书: 参数是: " + Arrays.toString(args)); } // 2. 核心:把活儿交给真正的员工干 (也可以选择不交!) Object result = method.invoke(realObject, args); // 关键!调用真实对象的方法 // 3. 后置处理 (After Advice) Log.d("Proxy", "秘书: 方法调用完成,结果是: " + result); // 4. 把结果报告给老板 (调用者) return result; } }
-
创建并使用动态代理对象:
java// 创建真正的核心员工 RealService realService = new RealService(); // 创建秘书的"万能处理本",并告诉秘书她需要代理谁(核心员工) MyInvocationHandler handler = new MyInvocationHandler(realService); // 使用 Proxy 类"变"出万能秘书(动态代理对象)! // 参数1:ClassLoader - 通常用目标接口的ClassLoader // 参数2:Class[] - 代理对象要实现的接口列表 (工作手册列表) // 参数3:InvocationHandler - 秘书的处理本 IService proxyService = (IService) Proxy.newProxyInstance( IService.class.getClassLoader(), new Class[]{IService.class}, // 告诉Proxy,秘书要实现的接口 handler); // 老板(调用者)开始通过秘书办事 proxyService.doSomething("重要项目"); // 调用会被handler.invoke()拦截处理 int sum = proxyService.calculate(5, 3); // 调用也会被handler.invoke()拦截处理 Log.d("Main", "计算结果: " + sum);
输出示例:
javascript
D/Proxy: 秘书: 有人要调用方法 'doSomething'
D/Proxy: 秘书: 参数是: [重要项目]
D/RealService: Doing real work: 重要项目
D/Proxy: 秘书: 方法调用完成,结果是: null (因为doSomething返回void)
D/Proxy: 秘书: 有人要调用方法 'calculate'
D/Proxy: 秘书: 参数是: [5, 3]
D/Proxy: 秘书: 方法调用完成,结果是: 8
D/Main: 计算结果: 8
关键点:
- 调用
proxyService
的任何方法 (doSomething
,calculate
),实际上都是在调用handler.invoke()
方法。 invoke
方法内部通过method.invoke(realObject, args)
将调用委托给真正的目标对象realService
。- 在委托调用前后 ,你可以插入任何自定义逻辑(日志、权限检查、性能监控、缓存、事务管理、错误处理、RPC调用封装等)。这就是 AOP(面向切面编程) 思想的体现。
2. 原理 (Principle)
动态代理的核心魔法发生在 Proxy.newProxyInstance()
方法内部。它的工作原理可以概括为:
-
接口检查: 确保传入的
Class[]
都是接口,且对当前ClassLoader
可见。 -
查找或生成代理类:
-
系统(
Proxy
类)首先会尝试查找是否已经为指定的接口列表(按顺序)生成过代理类。它内部维护了一个缓存(WeakCache
)。 -
如果缓存中没有: 系统会动态生成 一个新的代理类(如
$Proxy0
,$Proxy1
)的字节码。这个生成的类:-
extends java.lang.reflect.Proxy
(所以所有动态代理对象都是Proxy
的子类)。 -
implements
你传入的所有接口(如IService
)。 -
这个类中会为每一个接口方法生成对应的实现。这些实现都长得差不多:
javapublic final void doSomething(String task) { try { // 关键!调用父类Proxy中保存的InvocationHandler的invoke方法 super.h.invoke(this, m3, new Object[]{task}); // m3是Method对象doSomething的标识 } catch (...) { ... } } public final int calculate(int a, int b) { try { return ((Integer) super.h.invoke(this, m1, new Object[]{a, b})).intValue(); // m1是Method对象calculate的标识 } catch (...) { ... } }
-
这些方法的核心逻辑就是调用父类
Proxy
持有的InvocationHandler
实例 (h
) 的invoke
方法,并把当前代理对象 (this
)、代表该方法的Method
对象(通过常量池或预加载获得)、以及参数数组传递进去。
-
-
加载代理类: 将生成的字节码通过
ClassLoader
加载到 JVM (ART/Dalvik) 中,得到Class
对象。
-
-
实例化代理对象: 使用反射,通过代理类的构造方法(它需要一个
InvocationHandler
参数)实例化代理对象。构造方法内部会调用super(new InvocationHandler h)
,将你提供的MyInvocationHandler
实例保存到父类Proxy
的protected InvocationHandler h;
字段中。 -
返回代理对象: 将实例化好的代理对象强制转换为你指定的接口类型(如
IService
)返回给你。
核心奥秘:
- 动态代理对象 (
$Proxy0
) 只是一个空壳 和转发器 。它实现了接口,但自身没有任何业务逻辑。 - 所有接口方法的实现逻辑都委托 给了关联的
InvocationHandler
的invoke
方法。 InvocationHandler
持有对真实目标对象 的引用,并在invoke
方法中决定何时、如何调用真实对象的方法(甚至完全不调用,或者调用其他对象的方法)。
3. 源码调用链路 (Source Call Chain)
当你调用 proxyService.doSomething("重要项目")
时,发生了什么?让我们追踪一下(简化版,聚焦核心):
-
proxyService.doSomething("重要项目")
proxyService
是$Proxy0
的实例。
-
$Proxy0.doSomething(String task)
(动态生成的类中的方法)typescriptpublic final void doSomething(String task) { try { // h 是父类Proxy中的字段,指向你的MyInvocationHandler实例 // m3 是在类初始化时获取的Method对象 (代表IService.doSomething) super.h.invoke(this, m3, new Object[]{task}); // 关键调用! } catch (RuntimeException | Error e) { ... } catch (Throwable e) { ... } }
-
MyInvocationHandler.invoke(Object proxy, Method method, Object[] args)
-
proxy
参数传入的是proxyService
(即$Proxy0
实例本身)。 -
method
参数传入的是m3
(代表IService.doSomething
)。 -
args
参数传入的是new Object[]{"重要项目"}
。 -
现在执行你写在
invoke
方法里的代码:-
记录日志:
"秘书: 有人要调用方法 'doSomething'"
-
记录参数:
"秘书: 参数是: [重要项目]"
-
核心委托:
Object result = method.invoke(realObject, args);
method
是IService.doSomething
的反射对象。realObject
是你的RealService
实例。args
是["重要项目"]
。- 这行代码最终调用了
RealService.doSomething("重要项目")
!
-
真实对象执行:
Log.d("RealService", "Doing real work: 重要项目")
-
记录结果:
"秘书: 方法调用完成,结果是: null"
(因为返回void) -
返回
null
。
-
-
-
控制流回到
$Proxy0.doSomething
:它不做其他事,直接结束。 -
控制流回到你的调用代码 :继续执行下一行 (
int sum = proxyService.calculate(5, 3);
),过程类似。
关键路径:
scss
调用者代码 -> $Proxy0.接口方法() -> Proxy.h.invoke() (即MyInvocationHandler.invoke()) -> Method.invoke(realObject, args) -> RealObject.接口方法()
这个路径中,InvocationHandler.invoke
是绝对的核心枢纽。
4. 相关的关键概念 (Key Concepts)
-
接口 (Interface): 动态代理的基石。代理对象必须实现一个或多个接口。它定义了代理对象能"代理"哪些方法。没有接口,就无法使用JDK动态代理 (这是它与 CGLIB 等基于继承的代理的主要区别)。
-
java.lang.reflect.Proxy
: 核心工厂类。提供静态方法newProxyInstance(...)
来创建动态代理对象。它负责在运行时生成代理类字节码、加载类并实例化对象。 -
java.lang.reflect.InvocationHandler
: 核心处理器接口。只有一个方法Object invoke(Object proxy, Method method, Object[] args)
。所有对代理对象方法的调用都会被路由到这里。你在这里实现代理的逻辑(增强、委托等)。 -
代理对象 (
$Proxy0
,$Proxy1
, ...): 在运行时动态生成的类。它:- 继承自
java.lang.reflect.Proxy
。 - 实现你指定的接口。
- 为每个接口方法生成实现,该实现只是简单地调用关联的
InvocationHandler
的invoke
方法。
- 继承自
-
目标对象 / 被代理对象 (Real Object / Target Object): 真正执行业务逻辑的对象实例。
InvocationHandler
持有它的引用,并在invoke
方法内部决定是否及如何调用它的方法。 -
反射 (Reflection):
Method
对象及其invoke
方法的使用是动态代理实现委托的核心技术。它允许在运行时动态调用方法。 -
AOP (Aspect-Oriented Programming - 面向切面编程): 动态代理是实现 AOP 的一种重要技术。通过在
InvocationHandler.invoke
中添加代码(称为"通知" - Advice),你可以将横切关注点 (如日志、事务、安全、性能监控)与核心业务逻辑解耦,模块化地应用到多个目标方法上。 -
委托 (Delegation): 动态代理模式的本质是委托模式 。代理对象将方法的调用委托给
InvocationHandler
,而InvocationHandler
通常又委托给目标对象,并在委托前后添加额外逻辑。
Android 中的注意事项
- 性能: 动态代理涉及反射调用 (
method.invoke()
) 和动态类生成,相比直接调用会带来一定的性能开销。在性能极度敏感的路径(如高频循环)中需谨慎使用。Android 7.0 (API 24) 之前 ,动态生成代理类使用的是sun.misc.ProxyGenerator
(或类似私有API),性能相对较差。Android 7.0 及以后 ,ART 使用了更优化的java.lang.reflect.ProxyBuilder
(内部 Native 实现),性能有显著提升。 - 接口限制: 只能代理接口方法,不能代理类本身的非接口方法。
- 混淆 (ProGuard/R8): 如果你的接口、
InvocationHandler
或目标对象会被混淆,需要确保它们在混淆规则中保留(使用-keep
规则),否则运行时动态代理会因找不到类或方法而崩溃。 InvocationHandler
中的耗时操作: 在invoke
方法中执行的逻辑,尤其是委托调用 (method.invoke()
),会阻塞调用者线程。如果这里有网络请求等耗时操作,必须在后台线程执行,否则会阻塞 UI 线程导致 ANR。- 调试: 生成的代理类名字如
$Proxy0
,在调试时看起来可能不太直观。
扩展应用场景
-
权限检查代理:
kotlinclass PermissionInvocationHandler( private val target: Any, private val permission: String ) : InvocationHandler { override fun invoke(proxy: Any, method: Method, args: Array<out Any>?): Any? { if (hasPermission(permission)) { return method.invoke(target, *(args ?: emptyArray())) } else { requestPermission(permission) return null } } }
-
日志记录代理:
kotlinclass LoggingInvocationHandler( private val target: Any ) : InvocationHandler { override fun invoke(proxy: Any, method: Method, args: Array<out Any>?): Any? { Log.d("EventProxy", "Calling ${method.name} with ${args?.contentToString()}") val result = method.invoke(target, *(args ?: emptyArray())) Log.d("EventProxy", "Method ${method.name} completed") return result } }
-
性能监控代理:
kotlinclass PerformanceInvocationHandler( private val target: Any ) : InvocationHandler { override fun invoke(proxy: Any, method: Method, args: Array<out Any>?): Any? { val start = System.currentTimeMillis() val result = method.invoke(target, *(args ?: emptyArray())) val duration = System.currentTimeMillis() - start if (duration > 16) Log.w("Perf", "${method.name} took ${duration}ms") return result } }
总结
Android 中的动态代理是一种强大的运行时技术,它允许你创建一个实现了指定接口的对象,但实际的方法调用会被转发到一个 InvocationHandler
。这个 Handler 就像是一个集中处理器,它可以在调用真实对象方法前后插入自定义逻辑 (日志、权限、缓存、监控等),是实现 AOP、解耦、增强功能的利器。
核心流程:
- 定义接口 (
IService
)。 - 实现目标对象 (
RealService
)。 - 实现
InvocationHandler
(MyInvocationHandler
),在其中持有目标对象引用,并在invoke
方法中处理调用(包括委托给目标对象)。 - 用
Proxy.newProxyInstance()
创建代理对象 ,传入接口列表和InvocationHandler
。 - 通过代理对象调用方法 ,实际工作由
InvocationHandler.invoke()
完成。