竹笋

首页 » 问答 » 环境 » 硬核资源阿里内部流传的JDK手册Gi
TUhjnbcbe - 2023/4/29 18:43:00

深入JDK动态代理实现

基本使用

没有花里胡哨的,就下面的代码:

UserServiceuserService=newUserServiceImpl();

UserServiceo=(UserService)Proxy.newProxyInstance(UserService.class.getClassLoader(),newClass[]{UserService.class},newInvocationHandler(){

Override

publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{

System.out.println(--before--);

Objectinvoke=method.invoke(userService,args);

System.out.println(--after--);

returninvoke;

}

});

可以看出,关键在于代理的创建:Proxy.newProxyInstance。后面我们也以这里为入口深入。

关键步骤解析

生成代理类

生成代理类是动态代理中的最复杂的步骤,而真正生成代理类字节码的方法位于:java.lang.reflect.Proxy.ProxyClassFactory#apply。

精简一下方法,主要有以下几个步骤:

//前置校验

interfaceClass=Class.forName(intf.getName(),false,loader);

if(xxx){

thrownewIllegalArgumentException(xxx);

}

...

//数据准备

intaccessFlags=Modifier.PUBLIC

Modifier.FINAL;

...

StringproxyName=proxyPkg+proxyClassNamePrefix+num;

...

//生成代理类bytes

byte[]proxyClassFile=ProxyGenerator.generateProxyClass(

proxyName,interfaces,accessFlags);

...

//加载

returndefineClass0(loader,proxyName,

proxyClassFile,0,proxyClassFile.length);

生成代理类与加载是动态代理的核心。

组装ProxyMethod

Step1:AssembleProxyMethodobjectsforallmethodstogenerateproxydispatchingcodefor.

这里只是将收集到的方法元信息封装为sun.misc.ProxyGenerator.ProxyMethod类存储,并没有真正的生成code。

这里还特地也保存了了hashCode,equals,toString方法。

addProxyMethod(hashCodeMethod,Object.class);

addProxyMethod(equalsMethod,Object.class);

addProxyMethod(toStringMethod,Object.class);

...

sigmethods.add(newProxyMethod(name,parameterTypes,returnType,

exceptionTypes,fromClass));

组装FieldInfo/MethodInfo

Step2:AssembleFieldInfoandMethodInfostructsforalloffieldsandmethodsintheclasswearegenerating.

添加构造方法,并根据上一步组装好的ProxyMethod添加成员属性。

在这一步中,会调用sun.misc.ProxyGenerator.ProxyMethod#generateMethod方法,为其生成code。

//生成构造方法

this.methods.add(this.generateConstructor());

...

//生成成员属性

fields.add(newFieldInfo(pm.methodFieldName,

Ljava/lang/reflect/Method;,

ACC_PRIVATE

ACC_STATIC));

//添加方法

methods.add(pm.generateMethod());

...

//添加静态初始化代码块

methods.add(generateStaticInitializer());

构造最终类

Step3:Writethefinalclassfile.

根据上面几步收集到的信息,生成最终byte数组。

//u4magic;

dout.writeInt(0xCAFEBABE);

//u2minor_version;

dout.writeShort(CLASSFILE_MINOR_VERSION);

//u2major_version;

dout.writeShort(CLASSFILE_MAJOR_VERSION);

cp.write(dout);//(writeconstantpool)

//u2access_flags;

dout.writeShort(accessFlags);

//u2this_class;

dout.writeShort(cp.getClass(dotToSlash(className)));

//u2super_class;

dout.writeShort(cp.getClass(superclassName));

...

最终结果

通过配置sun.misc.ProxyGenerator.saveGeneratedFiles=true可以将生成的代理类字节码保存下来。

删除了我们无需关心hashCode,toString等方法后,反编译结果如下:

package

1
查看完整版本: 硬核资源阿里内部流传的JDK手册Gi