java动态代理通过proxy类和invocationhandler接口在运行时为接口生成代理实例,所有方法调用统一委托给invoke()处理,实现反射拦截;需定义接口、实现invocationhandler、调用newproxyinstance创建代理对象。

Java 动态代理通过 Proxy 类和 InvocationHandler 接口,在运行时为接口生成代理实例,并在方法调用时插入自定义逻辑,实现“反射拦截”——即不修改原代码,就能对方法调用进行监听、增强或控制。
核心原理:代理对象如何转发调用?
动态代理只能代理接口(不能代理具体类),JVM 会在运行时生成一个实现了目标接口的代理类(如 $Proxy0)。该类所有方法都会统一委托给绑定的 InvocationHandler 实例的 invoke() 方法处理。这一步就是“拦截”的发生点——你无需重写任何方法,只要在 invoke 中决定如何处理目标方法即可。
三步写出可工作的动态代理
-
定义被代理的接口和实现类(仅作目标,不直接调用):
例如UserService接口含getUserById(int id),真实实现是UserServiceImpl。 -
编写 InvocationHandler 实现类:
重写invoke(Object proxy, Method method, Object[] args);
在其中可做日志、权限检查、耗时统计等;
用method.invoke(target, args)手动触发原方法(这就是反射调用)。 -
用 Proxy.newProxyInstance 创建代理对象:
参数包括:类加载器(通常用接口的getClassLoader())、要代理的接口数组、以及你的InvocationHandler实例;
返回的是实现了该接口的代理对象,类型安全,可直接转型使用。
关键细节:为什么叫“反射拦截”?
拦截的本质在于:InvocationHandler.invoke() 中的 method.invoke(...) 是典型的反射调用——它绕过了编译期绑定,直到运行时才确定调用哪个对象的哪个方法。你可以在反射调用前后插入任意逻辑:
- 检查 method.getName() 做方法级开关;
- 用 method.getAnnotation(…) 读取注解控制行为;
- 对 args 做参数校验或脱敏;
- 捕获异常并统一包装返回结果。
注意限制与常见误区
- 代理对象只能强转成接口类型,不能转成实现类类型;
-
InvocationHandler中若忘记调用method.invoke(target, args),方法就真的不会执行; - 被代理接口中继承自
Object的方法(如toString()、hashCode())也会进入invoke,需留意区分; - 如果目标方法抛出受检异常,
invoke必须声明抛出相同异常或其父类,否则编译报错。
不复杂但容易忽略:整个过程没有字节码操作,也不依赖第三方库,纯 JDK 提供的能力,适合轻量级 AOP 场景。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











