JDK动态代理
准备
被代理类
public class Demo implements DemoInterface{
@Override
public int get(int num) {
return num + 1;
}
}
代理类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DemosProxy implements InvocationHandler {
private Object target;
public DemosProxy(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法返回开始");
Object obj = method.invoke(target, args);
System.out.println("方法返回结束");
return obj;
}
}
接口
public interface DemoInterface {
int get(int num);
}
测试类
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
DemoInterface demoInterface = (DemoInterface)Proxy.newProxyInstance(DemosProxy.class.getClassLoader(),
new Class[]{DemoInterface.class}, new DemosProxy(new Demo()));
int res = demoInterface.get(99);
System.out.println("res = " + res);
}
}
基础解释
JDK动态代理是基于接口的动态代理,这与它的实现机制有很大的关系。
原理解释
动态代理的目标是在被代理类方法执行前后添加附加额外操作,更加准确的说是为被代理类扩展额外功能。
具体实现
被代理类 - Demo
代理类 - DemosProxy
接口 - DemoInterface
测试类 - Test
1、注意Demo实现DemoInterface接口,而DemosProxy实现InvocationHandler接口。
2、测试类
DemoInterface demoInterface = (DemoInterface)Proxy.newProxyInstance(DemosProxy.class.getClassLoader(), new Class[]{DemoInterface.class}, new DemosProxy(new Demo()));
int res = demoInterface.get(99);
这段代码中的demoInterface对象实际上是代理类的对象。
根据多态性,接口类型引用
可以引用其实现类的对象
,而这个代理类并非
是DemosProxy类,而是JDK动态代理生成的临时类$Proxy0
,而这个类又在内部
去调用DemosProxy的对应的方法
,而DemosProxy类又在其内部
去调用Demo类的对应方法
完成操作。
通过观察newProxyInstance方法的底层源码,我们能够得知demoInterface确实是$Proxy对象。
// 获取代理类的构造器。interfaces为DemoInterface,而DemosProxy实现的是
// InvocationHandler接口;$Proxy实现的是DemoInterface接口
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) {
Objects.requireNonNull(h);
final Class<?> caller = System.getSecurityManager() == null
? null
: Reflection.getCallerClass();
Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);
return newProxyInstance(caller, cons, h);
}
// 根据构造器和参数返回一个对象,这里是$Proxy类构造器,说明返回的是$Proxy类对象。
private static Object newProxyInstance(Class<?> caller,
Constructor<?> cons,
InvocationHandler h) {
try {
if (caller != null) {
checkNewProxyPermission(caller, cons.getDeclaringClass());
}
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException | InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
}
}
给出本人的调试截图,
位置在源码的Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);
处。
3、 $Proxy0.class
System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
该语句位于测试类方法被调用前,作用是保存$Proxy0.class文件。
低版本的jdk请使用
System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
4、使用idea打开这个文件
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.sun.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import org.example.DemoInterface;
public final class $Proxy0 extends Proxy implements DemoInterface {
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 int get(int var1) throws {
try {
return (Integer)super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
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("org.example.DemoInterface").getMethod("get", Integer.TYPE);
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());
}
}
}
public final class $Proxy0 extends Proxy implements DemoInterface {
从中可以看出$Proxy类继承Proxy类并实现DemoInterface接口,这与Demo实现的是同一个接口。
public final int get(int var1) throws {
部分,能够发现$Proxy内部的方法实现是与DemoInterface接口中的方法绑定的,所以当调用get方法时的对象其实是$Proxy的get()方法。
接下来关注get方法的内部
return (Integer)super.h.invoke(this, m3, new Object[]{var1});
通过idea的辅助,能够得知h的类型为InvocationHandler
,而在本类中传入的InvocationHandler对象正好是new DemosProxy(new Demo()),因此$Proxy的get方法调用的是DemosProxy类的invoke方法。
接下来,参数被传递到DemosProxy类的invoke方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法返回开始");
Object obj = method.invoke(target, args);
System.out.println("方法返回结束");
return obj;
}
参数映射为 this - proxy, m3 - method,new Object[]{var1} - args。
var1=new Object[]{99}
m3 = Class.forName("org.example.DemoInterface").getMethod("get", Integer.TYPE);
m3证明调用的是DemoInterface接口的get方法,结合Object obj = method.invoke(target, args)
的target对象,可以得到,new Demo.get(agrs);
的结论。
至此,我们穿过了动态代理实现的全过程。
总结
1、创建$Proxy对象并赋予DemoInterface引用
2、通过demoInterface对象调用$Proxy类的get方法($Proxy类已经绑定DemoInterface接口中的方法)
3、$Proxy类的get方法内部调用DemosProxy的invoke方法
4、invoke方法调用Demo类的get方法
关于为什么要使用/$Proxy类?
请参考连接面试官:你说你懂动态代理,那你知道为什么JDK中的代理类都要继承Proxy吗?
END