JVM@从字节码角度理解动态代理
首先编写一个动态代理实例
interface Subject{
void request();
}
class RealSubject implements Subject{
public void request(){
System.out.println("real subject");
}
}
class DynamicSubject implements InvocationHandler{
private Object subject; // 这个就是我们要代理的真实对象
public DynamicSubject(Object subject)
{
this.subject = subject; // 构造方法,给我们要代理的真实对象赋初值
}
@Override
public Object invoke(Object object, Method method, Object[] args)
throws Throwable
{
// 在代理真实对象前我们可以添加一些自己的操作
System.out.println("before rent house");
System.out.println("Method:" + method);
// 当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
method.invoke(subject, args);
// 在代理真实对象后我们也可以添加一些自己的操作
System.out.println("after rent house");
return null;
}
}
public class Client {
public static void main(String[] args) {
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles",
"true"); //将动态代理生成的class输出文件
// 我们要代理的真实对象
Subject realSubject = new RealSubject();//这里指定被代理类
// 我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
InvocationHandler handler = new DynamicSubject(realSubject);
/*
* 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数
* 第一个参数 handler.getClass().getClassLoader(),我们这里使用代理类的类加载器,也就是被代理的那个真实对象
* 第二个参数realSubject.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
* 第三个参数handler,我们这里将这个代理对象关联到了上方的InvocationHandler 这个对象上
*/
Subject subject = (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(), handler);
subject.request();
}
}
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
运行程序结果:
before rent house
Method:public abstract void controller.Subject.request()
real subject
after rent house
- 通过改变sun.misc.ProxyGenerator.saveGeneratedFiles系统属性,可以将动态代理生成的class文件输出(默认只在内存,不会写到硬盘),得到Proxy0.class文件,使用反编译工具可以得到Proxy0.class文件,使用反编译工具可以得到Proxy0.class文件,使用反编译工具可以得到Proxy0的文件如下:
final class $Proxy0 extends Proxy implements Subject {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(InvocationHandler paramInvocationHandler) {
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject) {
try {
return ((Boolean) this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
} catch (Error | RuntimeException localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString() {
try {
return (String) this.h.invoke(this, m2, null);
} catch (Error | RuntimeException localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void request() {
try {
this.h.invoke(this, m3, null);
return;
} catch (Error | RuntimeException localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode() {
try {
return ((Integer) this.h.invoke(this, m0, null)).intValue();
} catch (Error | RuntimeException localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals",
new Class[] { Class.forName("java.lang.Object") });
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("controller.Subject").getMethod("request", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
} catch (NoSuchMethodException localNoSuchMethodException) {
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
} catch (ClassNotFoundException localClassNotFoundException) {
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
由反编译$Proxy0.class文件内容,可轻易得知:
- 1.$Proxy0是Proxy的子类,实现Subject接口;
- 2.$Proxy0重写了Object中的equal()、toString()、hashCode()方法;
- 3.在调用$Proxy0中方法时,实际上调用父类Proxy的h属性的invoke()方法,其中h为Proxy.newInstance()方法传入的DynamicSubject,也就是说最终会调用DynamicSubject的invoke()方法,并将参数传入—this.h.invoke(this, m3, null);