JDK动态代理原理
通过一个例子引入JDK动态代理
需求描述
对Dao
层类中的方法进行增强。例如:使用日志记录操作数据库所花的时间
类结构图
Mapper接口定义
public interface Mapper {
void insert();
}
Mapper接口实现类
public class RealMapper implements Mapper {
@Override
public void insert() {
System.out.println("do insert...");
}
}
代理处理逻辑的封装
public class MapperInvocationHandler implements InvocationHandler {
Object target;
public MapperInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("do something...");
return method.invoke(target, args);
}
}
代理对象工厂
public class MapperProxyFactory {
public static Object newInstance(Object realMapper, Class<?>... interfaces) {
return Proxy.newProxyInstance(MapperProxyFactory.class.getClassLoader(), interfaces, new MapperInvocationHandler(realMapper));
}
}
客户端
public class Client {
public static void main(String[] args) {
RealMapper real = new RealMapper();
Mapper mapper = (Mapper) MapperProxyFactory.newInstance(real, Mapper.class);
mapper.insert();
}
}
结果验证
可以看到最终我们实现了对原有实现的增强,下面我们源码查看内部的实现原理。
实现原理
Proxy#newProxyInstance
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
// 删除一些安全的验证代码
Class<?> cl = getProxyClass0(loader, intfs);
try {
// 通过反射创建代理对象
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
return cons.newInstance(new Object[]{h});
} catch (Exception e) {
// 异常的处理
}
}
该方法执行了以下几步
- 参数检查以及数据准备
- 调用
getProxyClass0
方法获取Class对象 - 通过反射创建代理对象并返回
获取Class对象(Proxy#getProxyClass0)
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
return proxyClassCache.get(loader, interfaces);
}
ProxyClassFactory#apply
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
// nextUniqueNumber是当前类的静态常量
// 是原子类 AtomicLong nextUniqueNumber = new AtomicLong();
long num = nextUniqueNumber.getAndIncrement();
// proxyPkg是包路径、eg:com.keminapera.proxy
// proxyClassNamePrefix是当前类的静态常量
// 始终固定不变 String proxyClassNamePrefix = "$Proxy";
// 所以使用JDK动态代理生成的类名格式:$Proxy+一个数字 eg:$Proxy0、$Proxy1
String proxyName = proxyPkg + proxyClassNamePrefix + num;
// 这里通过ProxyGenerator生成代理类的字节数组
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
try {
// 将上面生成代理类的字节数组通过指定的loader加载到JVM并得到该代理类的Class对象
// 对应的类名就是上面动态生成的proxyName---com.keminapera.proxy.$Proxy0
return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
// 处理异常
}
}
}
代理类创建时序图
ProxyGenerator#generateProxyClass测试
上面我们知道最终通过调用ProxyGenerator类的generateProxyClass静态方法生成代理类的字节数组,我们通过模拟ProxyClassFactory#apply调用该方法来查看生成代理类的格式是什么样的。
测试类
public class ProxyGeneratorTest {
private static final String proxyClassNamePrefix = "$Proxy";
private static final AtomicLong nextUniqueNumber = new AtomicLong();
public static void main(String[] args) throws Exception {
String proxyClassName = getProxyClassName();
byte[] bytes = ProxyGenerator.generateProxyClass(proxyClassName, new Class[]{Mapper.class}, Modifier.PUBLIC);
genetateClassFile(bytes, "E:\\Chrome_Workspace", proxyClassName);
}
// 动态生成类名
private static String getProxyClassName() {
String name = ProxyGeneratorTest.class.getName();
name = name.substring(0, name.lastIndexOf('.') + 1);
return name + proxyClassNamePrefix + nextUniqueNumber.get();
}
// 读取class文件转换成byte[]
static void genetateClassFile(byte[] bytes, String filePath, String fileName) throws Exception {
fileName = fileName.replace(".","//");
// 此处是上面定义好的Mapper.java编译后的class文件路径
File file = new File("E:\\Chrome_Workspace\\Mapper.class");
if (!file.exists()){
file.createNewFile();
}
try(FileOutputStream fos = new FileOutputStream(file)){
fos.write(bytes);
fos.flush();
}
}
}
生成的内容
package com.keminapera.jdkapi.reflection.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public class $Proxy0 extends Proxy implements Mapper {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void insert() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("com.keminapera.jdkapi.reflection.proxy.Mapper").getMethod("insert");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
生成的代理类有以下特点
- 生成的代理类实现指定的所有接口并继承Proxy类
- 重写了
Object
类的equals
、hashCode
、toString
方法 - 实现了接口的所有方法,内部都是调用
InvocationHandler
实现类的invoke
方法
结论
ProxyGenerator#generateProxyClass方法返回的是编译生成的字节码的字节数组
代理类实例化阶段
通过上面的流程我们已经拿到了代理对象的Class对象,有了Class对象通过反射可以生成一个真实对象。我们再次回到Proxy#newProxyInstance方法查看代理对象实例化部分代码。
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
Class<?> cl = getProxyClass0(loader, intfs);
// 代理对象实例化阶段开始
try {
// 通过反射获取定值参数的代理对象构造器
// constructorParams参数是Proxy类的静态常量
// Class<?>[] constructorParams = { InvocationHandler.class };
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
// 通过构造器反射实例化代理对象
return cons.newInstance(new Object[]{h});
} catch (Exception e) {
//异常的处理
}
通过反射获取指定参数的构造器,每个动态生成的代理类都会有该构造器,获取如下构造方法
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
该构造器调用父类构造器,从上面ProxyGenerator
的返回结果可以知道生成的代理类继承Proxy
类,因此调用Proxy
的构造方法
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}
最终设置Proxy
的成员变量h
代理对象生成时序图
代理类生成的方法(Mapper接口的insert方法)
public final void insert() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
通过查看代理类中生成的方法会发现所有方法最终都调用的成员变量h
的invoke
方法,所以可以看出最后代理逻辑的处理是在用户定义的InvocationHandler实现类的invoke方法中实现的。整体的调用关系图如下:
动态代理的使用场景
- Mybatis框架中使用Mapper接口操作数据库Mybatis源码解析–Mapper代理对象
- rpc框架实现远程方法调用
- Spring中对实现接口的类增强
小结
这块不得不提一下静态代理,从本质上来说动态代理就是将静态代理中由开发人员创建的代理类现在变成由JVM创建,如下图所示:
但静态代理有一个致命的缺点就是如果有成百上千个类需要增强,那就要为每一个类创建对应的增强逻辑,而且增强逻辑一模一样;这样会导致类爆炸和代码冗余
由于对这些类的增强逻辑是相同的,完全可以将其抽到一个类中实现,如果后续增强逻辑变了,只需要修改这一处即可,方便后续维护。由于现在代理类交给JVM去生成,所以它必须知道调用代理对象的方法时该交给谁去处理,所以JDK定义了一个接口InvocationHandler
让开发人员去实现。
通过上面的分析静态代理和动态代理的区别就在于代理类交给JVM在运行期创建,从而减少代理类的数量。实现这一目标的手段是通过约定了一个InvocationHandler
接口,JVM生成的代理类的方法全部交给InvocationHandler#invoke
方法去处理,而该方法需要开发人员去实现。所以一般我们在InvocationHandler
的实现类中会持有一个真正干活的目标对象,对方法拦截增强之后还是要调用目标对象的原有逻辑。
JDK动态代理的缺点:被代理类必须实现接口