众所周知,jdk动态代理需要实现目标类相同的接口,并对其方法进行增强
newProxyInstance()是Proxy提供的一个静态方法,用于动态创建代理对象
typescript
public class ProxyFactory {
// 目标对象
private final Object target;
public ProxyFactory(Object target) {
this.target = target;
}
/**
* 获取代理对象(静态方法,直接调用)
*/
public static Object getProxy(Object target) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 类加载器
target.getClass().getInterfaces(), // 接口列表
new InvocationHandler() { // 处理器
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 记录开始时间
long startTime = System.currentTimeMillis();
Object result = null;
result = method.invoke(target, args);
long endTime = System.currentTimeMillis();
return result;
}
}
);
}
}
Proxy.newProxyInstance()做了三件事:Proxy.newProxyInstance(classLoader, interfaces, handler);
- 动态生成字节码 :在内存中根据你传入的接口,动态创建一个新的类(这个类实现了你传入的所有接口)。
- 加载到 JVM :使用传入的
classLoader将这个新类的字节码加载到 JVM 内存中。- 创建实例 :实例化这个代理类,并将
InvocationHandler绑定到它上面。
动态代理3大参数详解
类加载器:
- 动态生成的字节码只是内存中的一段二进制数据(
byte[])- JVM 无法识别和使用它
- 必须通过类加载器将它转换为
Class对象,才能newInstance()创建实例
JVM 会根据newProxyInstance的方法信息,在内存中动态生成 .class 文件的二进制数据:
scss
// 以下不是真实代码,而是 JDK 内部字节码生成逻辑的伪代码示意
public byte[] generateProxyClass(Class<?>[] interfaces) {
StringBuilder byteCode = new StringBuilder();
// 1. 类头:继承 Proxy,实现指定接口
byteCode.append("public final class $Proxy0 extends Proxy implements " + interfaces[0].getName() + " {\n");
// 2. 为每个方法生成一个静态的 Method 对象(用于反射调用)
for (Method m : interfaces[0].getMethods()) {
byteCode.append(" private static Method m" + count++ + ";\n");
}
// 3. 生成构造方法(接收 InvocationHandler)
byteCode.append(" public $Proxy0(InvocationHandler h) { super(h); }\n");
// 4. 为每个接口方法生成实现(重点!)
for (Method m : interfaces[0].getMethods()) {
byteCode.append(" public " + m.getReturnType() + " " + m.getName() + "(");
// 参数列表...
byteCode.append(") {\n");
byteCode.append(" try {\n");
// 核心:调用 super.h.invoke(this, mX, args);
byteCode.append(" return (" + m.getReturnType() + ") super.h.invoke(this, m" + index + ", args);\n");
byteCode.append(" } catch (Throwable e) { throw new UndeclaredThrowableException(e); }\n");
byteCode.append(" }\n");
}
byteCode.append("}");
return byteCode.toString().getBytes(); // 返回字节码二进制
}
加载并实例化
ini
// 1. 用 ClassLoader 将上面生成的 byte[] 加载为 Class 对象
Class<?> proxyClass = classLoader.defineClass("$Proxy0", byteCode, 0, byteCode.length);
// 2. 获取构造方法
Constructor<?> constructor = proxyClass.getConstructor(InvocationHandler.class);
// 3. 实例化代理对象,传入你的 InvocationHandler
Object proxy = constructor.newInstance(handler);
接口列表:
- 告诉 JVM 代理类需要实现哪些接口的方法(方法名、参数类型、返回值类型、异常声明)
- 让代理对象可以安全转型(Cast)为接口类型,否则无法赋值给接口变量
SellTickets proxy = (SellTickets) Proxy.newProxyInstance(...)
- JVM 在生成
$Proxy0.class字节码时,必须知道要生成哪些方法,接口列表就是施工图纸
station.getClass().getInterfaces()获取目标类的接口Class对象列表,一一实现其方法(调用的是代理对象的调用处理程序)InvocationHandler.invoke(proxy, method, args)
InvocationHandler
动态代理的大脑和心脏
如果说前两个参数(类加载器、接口列表)都是"骨架",那InvocationHandler 才是真正干活的部分
它是连接代理对象和目标对象的桥梁------所有对代理对象的方法调用,最终都会被转发到 InvocationHandler 的 invoke() 方法中,由你决定怎么处理(增强、转发、拦截等)。
java
package java.lang.reflect;
@FunctionalInterface
public interface InvocationHandler {
/**
* 处理代理对象上的方法调用并返回结果
*
* @param proxy 代理对象本身(就是你调用方法的那个对象)
* @param method 被调用的方法的 Method 对象(反射)
* @param args 方法调用时传入的参数数组
* @return 方法调用的返回值
* @throws Throwable 可以抛出任何异常
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
InvocationHandler的invoke便是动态代理的核心:
- 第一个参数为代理对象本身,用的较少,容易无限递归
- 第二个参数为要调用的方法的反射对象,最后通过
method.invoke(target, args)调用目标方法- 第三个参数为目标方法调用时传入的实际参数
职责一:转发真正的目标对象方法
java
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 把方法调用转发给真实对象 station
return method.invoke(station, args);
}
};
职责二:增强逻辑(AOP本质)
csharp
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// ========== 前置增强 ==========
System.out.println("【日志】" + method.getName() + " 方法开始执行,参数:" + Arrays.toString(args));
System.out.println("【事务】开启事务");
try {
// ========== 转发给目标对象 ==========
Object result = method.invoke(station, args);
// ========== 后置增强(正常返回) ==========
System.out.println("【事务】提交事务");
System.out.println("【日志】" + method.getName() + " 方法执行成功,返回值:" + result);
return result;
} catch (Exception e) {
// ========== 后置增强(异常返回) ==========
System.out.println("【事务】回滚事务");
System.out.println("【日志】" + method.getName() + " 方法执行异常:" + e.getMessage());
throw e;
}
}
};
职责三:完全拦截(Mybatis)
java
// MyBatis 的 Mapper 代理简化版
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 1. 从方法上提取 SQL 注解
String sql = method.getAnnotation(Select.class).value();
// 2. 执行 JDBC 查询
ResultSet rs = jdbcTemplate.query(sql, args);
// 3. 将 ResultSet 映射为对象返回
return mapResultSetToObject(rs, method.getReturnType());
// ⚠️ 注意:这里没有调用任何目标对象!
// 因为 Mapper 本身就没有实现类!
}
};
通过反编译内存中动态生成的代理类,我们可以更好的理解动态代理:
反编译后的动态代理
scala
public final class $Proxy0 extends Proxy implements SellTickets {
private static Method m3; // sell
public $Proxy0(InvocationHandler var1) {
super(var1);
}
public final void sell() throws {
super.h.invoke(this, m3, (Object[])null);
}
// ... 其他方法
}
我们来看这一部分:
ruby
public $Proxy0(InvocationHandler var1) {
super(var1);
}
在构造时将InvocationHandler传递给父类的构造器,我们再来看看父类Proxy做了什么
java
public class Proxy implements java.io.Serializable {
protected InvocationHandler h;
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}
}
父类Proxy将其传递的InvocationHandler赋给自身变量,同时我们再看反编译的代码的这一部分:
java
public final void sell() throws {
super.h.invoke(this, m3, (Object[])null);
}
在这里调用了父类的InvocationHandler的invoke方法,实现真正的动态代理核心
- 创建 InvocationHandler
↓- 调用 Proxy.newProxyInstance(loader, interfaces, h)
↓- 生成代理类 $Proxy0,构造方法接收 InvocationHandler
↓- 通过构造方法将 h 传给父类 Proxy
↓
public class Proxy {
protected InvocationHandler h;
protected Proxy(InvocationHandler h) {
this.h = h;
}
}
↓- $Proxy0 的父类 Proxy 中保存了 h
↓- 调用 $Proxy0.sell() → super.h.invoke(...)
↓- 最终执行你的 InvocationHandler 逻辑
现在我们知道了,动态代理通过实现接口列表,并通过类加载器将二进制数据转为Class字节码对象(JVM才能看懂)
其核心方法为调用InvocationHandler的invoke方法(通过传入的方法对象,再在内部invoke调用)
newProxyInstance 是怎么组装这些参数的?
swift
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
throws IllegalArgumentException {
// 根据接口列表生成代理类的字节码并加载
Class<?> cl = getProxyClass0(loader, intfs);
// 代理类的构造方法签名:public $Proxy0(InvocationHandler h)
final Constructor<?> cons = cl.getConstructor(constructorParams);
// 用构造方法创建实例,传入 InvocationHandler
return cons.newInstance(new Object[]{h});
}
- 先通过
ProxyGenerator根据接口列表在内存中生成$Proxy0类的字节码- 然后用 ClassLoader 加载为 Class 对象
- 再获取其构造方法(接收 InvocationHandler 参数)核心逻辑
- 最后通过构造方法将 InvocationHandler 传入并实例化
Spring 的 ProxyFactory(官方提供)
java
import org.springframework.aop.framework.ProxyFactory;
public class SpringProxyDemo {
public static void main(String[] args) {
Station station = new Station();
ProxyFactory factory = new ProxyFactory();
factory.setTarget(station);
factory.addAdvice(new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("【Spring AOP】前置增强");
Object result = invocation.proceed();
System.out.println("【Spring AOP】后置增强");
return result;
}
});
SellTickets proxy = (SellTickets) factory.getProxy();
proxy.sell();
}
}