java源码-JDK动态代理

it2022-05-05  126

一、实验:

首先借用别人的一个例子:

接口:

public interface Star { String sing(String name); String dance(String name); } public class LiuDeHua implements Star { @Override public String sing(String name) { System.out.println("给我一杯忘情水"); return "唱完"; } @Override public String dance(String name) { System.out.println("开心的马骝"); return "跳完"; } }

代理类生成辅助类

public class StarProxy implements InvocationHandler { // 目标类,也就是被代理对象 private Object target; public void setTarget(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 这里可以做增强 System.out.println("收钱"); Object result = method.invoke(target, args); System.out.println("收完钱"); return result; } // 生成代理类 public Object CreatProxyedObj() { return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } }

OK,我们接下来测试代码:

public static void main(String[] args) { StarProxy proxy = new StarProxy(); proxy.setTarget(new LiuDeHua()); Object obj = proxy.CreatProxyedObj(); Star star = (Star) obj; star.dance("dance"); star.sing("sing"); }

看下输出结果:

收钱 开心的马骝 收完钱 收钱 给我一杯忘情水 收完钱

二、原理:

核心就是如何生成代理类的,方法对应的就是

Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);

我们深入看下源码,最终会走到ProxyGenerator.generateClassFile();方法,递归调用的顺序下图:

看一下这个方法,我们就知道原理了:

其实核心就是把各种方法都记录成对象(因为输入值是interface,所以这里只考虑方法而不考虑成员变量);

然后把对象转换成java可以接受的二进制流,最后由ClassLoader去加载。

private byte[] generateClassFile() { this.addProxyMethod(hashCodeMethod, Object.class); this.addProxyMethod(equalsMethod, Object.class); this.addProxyMethod(toStringMethod, Object.class); Class[] var1 = this.interfaces; int var2 = var1.length; int var3; Class var4; for(var3 = 0; var3 < var2; ++var3) { var4 = var1[var3]; Method[] var5 = var4.getMethods(); int var6 = var5.length; for(int var7 = 0; var7 < var6; ++var7) { Method var8 = var5[var7]; this.addProxyMethod(var8, var4); } } Iterator var11 = this.proxyMethods.values().iterator(); List var12; while(var11.hasNext()) { var12 = (List)var11.next(); checkReturnTypes(var12); } Iterator var15; try { this.methods.add(this.generateConstructor()); var11 = this.proxyMethods.values().iterator(); while(var11.hasNext()) { var12 = (List)var11.next(); var15 = var12.iterator(); while(var15.hasNext()) { ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next(); this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10)); this.methods.add(var16.generateMethod()); } } this.methods.add(this.generateStaticInitializer()); } catch (IOException var10) { throw new InternalError("unexpected I/O Exception", var10); } if (this.methods.size() > 65535) { throw new IllegalArgumentException("method limit exceeded"); } else if (this.fields.size() > 65535) { throw new IllegalArgumentException("field limit exceeded"); } else { this.cp.getClass(dotToSlash(this.className)); this.cp.getClass("java/lang/reflect/Proxy"); var1 = this.interfaces; var2 = var1.length; for(var3 = 0; var3 < var2; ++var3) { var4 = var1[var3]; this.cp.getClass(dotToSlash(var4.getName())); } this.cp.setReadOnly(); ByteArrayOutputStream var13 = new ByteArrayOutputStream(); DataOutputStream var14 = new DataOutputStream(var13); try { var14.writeInt(-889275714); var14.writeShort(0); var14.writeShort(49); this.cp.write(var14); var14.writeShort(this.accessFlags); var14.writeShort(this.cp.getClass(dotToSlash(this.className))); var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy")); var14.writeShort(this.interfaces.length); Class[] var17 = this.interfaces; int var18 = var17.length; for(int var19 = 0; var19 < var18; ++var19) { Class var22 = var17[var19]; var14.writeShort(this.cp.getClass(dotToSlash(var22.getName()))); } var14.writeShort(this.fields.size()); var15 = this.fields.iterator(); while(var15.hasNext()) { ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next(); var20.write(var14); } var14.writeShort(this.methods.size()); var15 = this.methods.iterator(); while(var15.hasNext()) { ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next(); var21.write(var14); } var14.writeShort(0); return var13.toByteArray(); } catch (IOException var9) { throw new InternalError("unexpected I/O Exception", var9); } } }

 

最终生成的Class类是com.sun.proxy.$Proxy0,反编译出来会是如下这样:

public final class $proxy1 extends Proxy implements Service { public $proxy1(InvocationHandler invocationhandler) { super(invocationhandler); } public final boolean equals(Object obj) { try { return ((Boolean)super.h.invoke(this, m1, new Object[] { obj })).booleanValue(); } catch(Error _ex) { } catch(Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final String toString() { try { return (String)super.h.invoke(this, m2, null); } catch(Error _ex) { } catch(Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final void add() { try { super.h.invoke(this, m3, null); return; } catch(Error _ex) { } catch(Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final int hashCode() { try { return ((Integer)super.h.invoke(this, m0, null)).intValue(); } catch(Error _ex) { } catch(Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } private static Method m1; private static Method m2; private static Method m3; private static Method m4; private static Method m0; 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("com.proxy.Star").getMethod("sing", new Class[0]); m4 = Class.forName("com.proxy.Star").getMethod("dance", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); } catch(NoSuchMethodException nosuchmethodexception) { throw new NoSuchMethodError(nosuchmethodexception.getMessage()); } catch(ClassNotFoundException classnotfoundexception) { throw new NoClassDefFoundError(classnotfoundexception.getMessage()); } } }

所以,我们最终使用的就是这个代理类。由于这个类是直接生成出来的二进制流加载到classLoader里面的,所以没有对应的.class文件。(当然我们也可以通过修改VM-option配置sun.misc.ProxyGenerator.saveGeneratedFiles使其保存下来)


最新回复(0)