8

Java动态代理--jdk代理原理深究

 2 years ago
source link: https://wakzz.cn/2016/12/25/java/Java%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86--jdk%E4%BB%A3%E7%90%86%E5%8E%9F%E7%90%86%E6%B7%B1%E7%A9%B6/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Java动态代理--jdk代理原理深究

祈雨的博客
2016-12-25

首先实例一个简单的jdk动态代理实例,为下文分析方便,所以实例拆分得比较厉害,可能和常看到的动态代理模板所不同。

public interface Function {

public void sayHello();
public String getBay();
//jdk动态代理基于接口代理,所以该方法在代理类中不存在
// public void doNothing();
}

接口实现类【被代理对象】

public class FunctionImpl implements Function {

public void sayHello() {
System.out.println("hello,world");
}

public final String getBay() {
System.out.println("bay");
return "bay";
}

public void doNothing() {

}
}

InvocationHandler实现类

public class MyHandler implements InvocationHandler {

Object delegate=null;

public MyHandler(Object delegate) {
this.delegate=delegate;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (!method.getName().equals("getBay")) {
return method.invoke(delegate, args);
} else {
System.out.println("this is a proxy");
return "再见"; //方法返回值
}
}

}

动态代理入口

public class DynamicProxy {

public static Object getInstance(Object obj) {
Class<?> cls = obj.getClass();
return Proxy.newProxyInstance(
cls.getClassLoader(), cls.getInterfaces(), new MyHandler(obj));
}
}
Function f = new FunctionImpl();
Function function = (Function) DynamicProxy.getInstance(f);
function.sayHello();
function.getBay();

最终测试结果为f实例被动态代理,并且在执行getBay()方法时被拦截,而执行自定义的方法。
那么,问题来了,jdk动态代理是如何做到拦截指定方法并执行自定义方法的呢?
我在一次学习中无意发现了一段代码,用于从内存中输出动态代理类的class文件。此方法要在动态代理对象在内存存活期间运行。

// 把动态代理对象类生成class文件输出
public void createProxyClass() throws Exception {
//$Proxy0为代理对象的名字,可通过proxy.getClass().getName()获得:com.sun.proxy.$Proxy0
byte[] data = ProxyGenerator.generateProxyClass("$Proxy0", FunctionImpl.class.getInterfaces());
FileOutputStream out = new FileOutputStream(new File("D:/proxy.class"));
out.write(data);
out.close();

}

输出得到class文件后,通过反编译得到代理对象$Proxy源码

public final class $Proxy0 extends Proxy implements Function {
private static Method m4;
private static Method m1;
private static Method m0;
private static Method m3;
private static Method m2;

public $Proxy0(InvocationHandler paramInvocationHandler) {
super(paramInvocationHandler);
}

public final void sayHello() {
try {
this.h.invoke(this, m4, null);
return;
} catch (Error|RuntimeException localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}

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 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);
}
}

public final String getBay() {
try {
return (String)this.h.invoke(this, m3, null);
} 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);
}
}

static {
try {
m4 = Class.forName("proxy.Function").getMethod("sayHello", new Class[0]);
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m3 = Class.forName("proxy.Function").getMethod("getBay", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
} catch (NoSuchMethodException localNoSuchMethodException) {
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
} catch (ClassNotFoundException localClassNotFoundException) {
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}

从而我制作出这张jdk代理原理图

img

可见,我们测试中的被代理对象f 就是图中的实体对象,代理对象function 就是$Proxy,jdk动态代理对象实现了被代理对象的接口,并继承了Proxy类,而我们的InvocationHandler的实现类就是Proxy的成员变量。

所以代理对象f 执行sayHello()方法时,实际上是通过反射获取被代理对象接口的sayHello()方法,然后执行代理对象父类Proxy中的InvocationHandler实现类的invoke()方法,在invoke()方法中默认执行被代理对象的原方法method.invoke(delegate, args),我们的代理增强也就是在这里执行的。其中delegate则就是我们最开始的被代理对象了。

所以此处也就能解释为何Spring在A方法中直接调用本类的B方法时会出现事务失效、日志失效之类的问题,原因就是使用代理增强必须调用代理对象才行,A中直接调用本类的B方法实际上是在被代理对象中调用B方法,绕过了代理,因此代理增强并没有执行。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK