java动态代理——代理方法的假设和验证及Proxy源码分析五

假定我们现在定义一个简单的接口,并生成该接口的代理类
接口定义

public interface TestInterface {
int put(String a);
}
满足3个特性的代理类初步定义如下

复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class $Proxy11 extends Proxy implements TestInterface {
protected $Proxy11(InvocationHandler h) {
super(h);
}

@Override
public int put(String a) {
    return 0;
}

}
复制代码
然而在这种情况下h的代理是无法生效的,因为put方法中并没有h的参与
现在我们回顾一下InvocationHandler的invoke方法的定义

public Object invoke(Object proxy, Method method, Object[] args)
第一个proxy是代理自身,method是被代理的方法,args是方法的参数
因此为了使得代理生效,我们可以修改方法,如下

复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class $Proxy11 extends Proxy implements TestInterface {
protected $Proxy11(InvocationHandler h) {
super(h);
}

@Override
public int put(String a) {
    try {
        return (int) h.invoke(this, TestInterface.class.getMethod("put", String.class), new Object[]{a});
    } catch (Throwable e) {
        return 0;
    }
}

}
复制代码
这样我们就能使得h的代理生效了
当然,这只是我们所设想的最基本的一种代理形式。有了这个思路之后,我们就可以看看源码中是如何生成方法的字节码

接着我们来看重点,proxy方法的写入
还是回到generateClassFile()方法中关注下面这行代码

this.methods.add(var16.generateMethod());
这个方法就是proxy方法实际执行的code部分了,因为代码比较多,所以我就直接将注释写到代码中

如果你前面4篇文章都没落下,那我想你一定会有兴趣看完下面所有的代码,并且会对proxy的实现和class字节码有更深刻的理解

当然,如果你看到源码就非常头疼也没有关系,可以跳过这部分源码直接看最后的验证部分

复制代码
private ProxyGenerator.MethodInfo generateMethod() throws IOException {
/**
* 获取方法描述,如果还打开着之前javap的工具的话,就能看到类似于
* // java/lang/Object.""😦)V
* // Test.calc:(II)I
/
String methodDescriptor = ProxyGenerator.getMethodDescriptor(this.parameterTypes, this.returnType);
/
*
* 这里和之前构造器一样,先生成一个MethodInfo对象
* 这里17表示public final
* Modifier.FINAL | Modifier.PUBLIC
/
ProxyGenerator.MethodInfo methodInfo = ProxyGenerator.this.new MethodInfo(this.methodName, methodDescriptor, 17);
/
*
* 新建一个存放静态池编号的数组
/
int[] parameterTypesOrders = new int[this.parameterTypes.length];
/
*
* 这个值是指静态池中的编号,如果还打开着之前javap的话,类似于
* Constant pool:
* #1 = Methodref #8.#19 // java/lang/Object.""😦)V
* #2 = Methodref #7.#20 // Test.calc:(II)I
* #3 = Double 2.0d
* #5 = Methodref #21.#22 // java/lang/Math.pow:(DD)D
* 前面的#1,#2,#3,#5
* 我们注意到缺少了#4,因为double需要占用8个字节,而其他的都只需要占用4个字节
*/
int constantPoolNumber = 1;

for(int i = 0; i < parameterTypesOrders.length; ++i) {
    parameterTypesOrders[i] = constantPoolNumber;
    /**
     * 如果是Long或者Double类型的参数,则+2,否则+1,因为Long和Double都是占用8个字节
     */
    constantPoolNumber += ProxyGenerator.getWordsPerType(this.parameterTypes[i]);
}

DataOutputStream dataOutputStream = new DataOutputStream(methodInfo.code);
/**
 * aload_0,加载栈帧本地变量表的第一个参数,因为是实例方法,所以是就是指this
 */
ProxyGenerator.this.code_aload(0, dataOutputStream);
/**
 * getfield,获取this的实例字段
 */
dataOutputStream.writeByte(180);
/**
 * 从Proxy类中,获取类型是InvocationHandler,字段名为h的对象
 */
dataOutputStream.writeShort(ProxyGenerator.this.cp.getFieldRef("java/lang/reflect/Proxy", "h", "Ljava/lang/reflect/InvocationHandler;"));
/**
 * aload_0
 */
ProxyGenerator.this.code_aload(0, dataOutputStream);
/**
 * getstatic,获取静态字段
 */
dataOutputStream.writeByte(178);
/**
 * 获取当前代理类,名字是methodFieldName,类型是Method的对象(之前在写入静态池的时候,用的也是methodFieldName)
 */
dataOutputStream.writeShort(ProxyGenerator.this.cp.getFieldRef(ProxyGenerator.dotToSlash(ProxyGenerator.this.className), this.methodFieldName, "Ljava/lang/reflect/Method;"));
/**
 * 准备写入参数
 */
if (this.parameterTypes.length > 0) {
    /**
     * 写入参数的数量,如果再仔细看一下code_ipush
     * 当length小于等于5时,写入的命令是iconst_m1~iconst_5
     * 当length在-128~127闭区间时,写入的命令是bipush
     * 否则就写入sipush
     */
    ProxyGenerator.this.code_ipush(this.parameterTypes.length, dataOutputStream);
    /**
     * anewarray,创建一个数组
     */
    dataOutputStream.writeByte(189);
    /**
     * 数组的类型是object
     */
    dataOutputStream.writeShort(ProxyGenerator.this.cp.getClass("java/lang/Object"));

    /**
     * 循环参数
     */
    for(int i = 0; i < this.parameterTypes.length; ++i) {
        /**
         * dup,复制栈顶的操作数
         */
        dataOutputStream.writeByte(89);
        /**
         * iconst、bipush、sipush
         */
        ProxyGenerator.this.code_ipush(i, dataOutputStream);
        /**
         * 对参数类型等做一个编码
         */
        this.codeWrapArgument(this.parameterTypes[i], parameterTypesOrders[i], dataOutputStream);
        /**
         * aastore,将对象存入数组
         */
        dataOutputStream.writeByte(83);
    }
} else {
    /**
     * 如果没参数的话
     * aconst_null,push一个null
     */
    dataOutputStream.writeByte(1);
}
/**
 * invokeinterface 调用接口方法
 */
dataOutputStream.writeByte(185);
/**
 * 找到InvocationHandler的invoke方法
 */
dataOutputStream.writeShort(ProxyGenerator.this.cp.getInterfaceMethodRef("java/lang/reflect/InvocationHandler", "invoke", "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;"));
/**
 * iconst_1,将1压入操作栈
 */
dataOutputStream.writeByte(4);
/**
 * nop,不做事情
 */
dataOutputStream.writeByte(0);

if (this.returnType == Void.TYPE) {
    /**
     * 如果是void方法
     * pop,将栈顶的操作数弹出
     */
    dataOutputStream.writeByte(87);
    /**
     * return
     */
    dataOutputStream.writeByte(177);
} else {
    /**
     * 对返回值进行编码
     */
    this.codeUnwrapReturnValue(this.returnType, dataOutputStream);
}

byte startPc = 0;
short handlerPc;
short endPc = handlerPc = (short)methodInfo.code.size();
/**
 * 获取方法可能抛出的异常
 */
List catchList = ProxyGenerator.computeUniqueCatchList(this.exceptionTypes);
if (catchList.size() > 0) {
    Iterator exceptionIterator = catchList.iterator();

    /**
     * 对异常进行预处理
     */
    while(exceptionIterator.hasNext()) {
        Class var12 = (Class)exceptionIterator.next();
        /**
         * 这里注意startPc, endPc, handlerPc参数,和pc register有关,用于抛出Exception时能确定接下去要执行的指令
         */
        methodInfo.exceptionTable.add(new ProxyGenerator.ExceptionTableEntry(startPc, endPc, handlerPc, ProxyGenerator.this.cp.getClass(ProxyGenerator.dotToSlash(var12.getName()))));
    }
    /**
     * athrow,抛出异常
     */
    dataOutputStream.writeByte(191);
    /**
     * 重新获取异常的处理点
     */
    handlerPc = (short)methodInfo.code.size();
    /**
     * 添加异常的基类
     */
    dataOutputStream.exceptionTable.add(new ProxyGenerator.ExceptionTableEntry(startPc, endPc, handlerPc, ProxyGenerator.this.cp.getClass("java/lang/Throwable")));
    /**
     * 根据constantPoolNumber的值
     * astore_0 = 75 (0x4b)
     * astore_1 = 76 (0x4c)
     * astore_2 = 77 (0x4d)
     * astore_3 = 78 (0x4e)
     * astore
     */
    ProxyGenerator.this.code_astore(constantPoolNumber, dataOutputStream);
    /**
     * new 创建一个新对象
     */
    dataOutputStream.writeByte(187);
    /**
     * 对象是UndeclaredThrowableException
     */
    dataOutputStream.writeShort(ProxyGenerator.this.cp.getClass("java/lang/reflect/UndeclaredThrowableException"));
    /**
     * dup 复制栈顶操作数
     */
    dataOutputStream.writeByte(89);
    /**
     * 根据constantPoolNumber的值
     * aload_0 = 42 (0x2a)
     * aload_1 = 43 (0x2b)
     * aload_2 = 44 (0x2c)
     * aload_3 = 45 (0x2d)
     * aload
     */
    ProxyGenerator.this.code_aload(constantPoolNumber, dataOutputStream);
    /**
     * invokespecial,调用父类的方法
     */
    dataOutputStream.writeByte(183);
    /**
     * 父类的构造函数
     */
    dataOutputStream.writeShort(ProxyGenerator.this.cp.getMethodRef("java/lang/reflect/UndeclaredThrowableException", "<init>", "(Ljava/lang/Throwable;)V"));
    /**
     * athrow,抛出异常
     */
    dataOutputStream.writeByte(191);
}

if (var2.code.size() > 65535) {
    throw new IllegalArgumentException("code size limit exceeded");
} else {
    var2.maxStack = 10;
    var2.maxLocals = (short)(var4 + 1);
    var2.declaredExceptions = new short[this.exceptionTypes.length];

    for(int var14 = 0; var14 < this.exceptionTypes.length; ++var14) {
        var2.declaredExceptions[var14] = ProxyGenerator.this.cp.getClass(ProxyGenerator.dotToSlash(this.exceptionTypes[var14].getName()));
    }

    return var2;
}

}
复制代码

那么为了看看我们一开始对于方法的猜测是否正确,我们略微改造之前定义的接口和类,然后实际看看
接口和Proxy定义

复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.concurrent.TimeoutException;

public class Proxy11 extends Proxy implements TestInterface {
protected Proxy11(InvocationHandler h) {
super(h);
}

public void put(String a, Double b) throws TimeoutException {
    try {
        h.invoke(this, TestInterface.class.getMethod("put", String.class, Double.class), new Object[]{a, b});
    } catch (Throwable e) {
    }
}

public int get(String a, Long b) throws IndexOutOfBoundsException {
    try {
        return (int) h.invoke(this, TestInterface.class.getMethod("get", String.class, Long.class), new Object[]{a, b});
    } catch (Throwable e) {
        return 0;
    }
}

}

interface TestInterface {
void put(String a, Double b) throws TimeoutException;

int get(String a, Long b) throws IndexOutOfBoundsException;

}
复制代码
我们生成class后,将字节码的指令集与我们之前的分析一一对比,虽然其中还是有些不同,不过大体上是符合之前源码的顺序

最后为了实际考察Proxy生成类的源码,我们还是需要将Proxy的字节码转换回java文件

首先我们需要添加vm启动参数

-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
有了这个参数,当我们使用Proxy时,就会把class写入到文件中了
写入的目录是项目下的com/sun/proxy/$Proxy11.class
为了更好地可读性,我们需要使用一个在线工具
http://www.javadecompilers.com/
传入我们之前生成出来class文件
结果如下

复制代码
package com.sun.proxy;

import java.util.concurrent.TimeoutException;
import java.lang.reflect.UndeclaredThrowableException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import cn.tera.aopproxy.TestInterface;
import java.lang.reflect.Proxy;

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

public $Proxy11(final InvocationHandler h) {
    super(h);
}

public final boolean equals(final Object o) {
    try {
        return (boolean)super.h.invoke(this, $Proxy11.m1, new Object[] { o });
    }
    catch (Error | RuntimeException error) {
        throw;
    }
    catch (Throwable undeclaredThrowable) {
        throw new UndeclaredThrowableException(undeclaredThrowable);
    }
}

public final int get(final String s, final Long n) throws IndexOutOfBoundsException {
    try {
        return (int)super.h.invoke(this, $Proxy11.m3, new Object[] { s, n });
    }
    catch (Error | RuntimeException error) {
        throw;
    }
    catch (Throwable undeclaredThrowable) {
        throw new UndeclaredThrowableException(undeclaredThrowable);
    }
}

public final String toString() {
    try {
        return (String)super.h.invoke(this, $Proxy11.m2, null);
    }
    catch (Error | RuntimeException error) {
        throw;
    }
    catch (Throwable undeclaredThrowable) {
        throw new UndeclaredThrowableException(undeclaredThrowable);
    }
}

public final void put(final String s, final Double n) throws TimeoutException {
    try {
        super.h.invoke(this, $Proxy11.m4, new Object[] { s, n });
    }
    catch (Error | RuntimeException | TimeoutException error) {
        throw;
    }
    catch (Throwable undeclaredThrowable) {
        throw new UndeclaredThrowableException(undeclaredThrowable);
    }
}

public final int hashCode() {
    try {
        return (int)super.h.invoke(this, $Proxy11.m0, null);
    }
    catch (Error | RuntimeException error) {
        throw;
    }
    catch (Throwable undeclaredThrowable) {
        throw new UndeclaredThrowableException(undeclaredThrowable);
    }
}

static {
    try {
        $Proxy11.m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
        $Proxy11.m3 = Class.forName("cn.tera.aopproxy.TestInterface").getMethod("get", Class.forName("java.lang.String"), Class.forName("java.lang.Long"));
        $Proxy11.m2 = Class.forName("java.lang.Object").getMethod("toString", (Class<?>[])new Class[0]);
        $Proxy11.m4 = Class.forName("cn.tera.aopproxy.TestInterface").getMethod("put", Class.forName("java.lang.String"), Class.forName("java.lang.Double"));
        $Proxy11.m0 = Class.forName("java.lang.Object").getMethod("hashCode", (Class<?>[])new Class[0]);
    }
    catch (NoSuchMethodException ex) {
        throw new NoSuchMethodError(ex.getMessage());
    }
    catch (ClassNotFoundException ex2) {
        throw new NoClassDefFoundError(ex2.getMessage());
    }
}

}

深圳网站建设www.sz886.com

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值