Javassist实现JDK动态代理_JAVA_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > JAVA > Javassist实现JDK动态代理

Javassist实现JDK动态代理

 2018/8/12 21:35:38  zhanjia  程序员俱乐部  我要评论(0)
  • 摘要:提到JDK动态代理,相信很多人并不陌生。然而,对于动态代理的实现原理,以及如何编码实现动态代理功能,可能知道的人就比较少了。接下一来,我们就一起来看看JDK动态代理的基本原理,以及如何通过Javassist进行模拟实现。JDK动态代理示例以下是一个基于JDK动态代理的helloworld示例,在很多地方都可以看到类似的版本。publicclassDynamicProxyTest{interfaceIHello{voidsayHello();
  • 标签:实现 jdk Java 代理

提到JDK动态代理,相信很多人并不陌生。然而,对于动态代理的实现原理,以及如何编码实现动态代理功能,可能知道的人就比较少了。接下一来,我们就一起来看看JDK动态代理的基本原理,以及如何通过Javassist进行模拟实现。

JDK动态代理

示例

以下是一个基于JDK动态代理的hello world示例,在很多地方都可以看到类似的版本

monospace; display: block; letter-spacing: 0px;" class="java language-java hljs">public?class?DynamicProxyTest?{

????interface?IHello?{
????????function">void?sayHello();
????}

????static?class?Hello?implements?IHello?{
????????@Override
????????public?void?sayHello()?{
????????????System.out.println("hello?world");
????????}
????}

????static?class?DynamicProxy?implements?InvocationHandler?{

????????Object?originalObj;

????????Object?bind(Object?originalObj)?{
????????????this.originalObj?=?originalObj;
????????????return?Proxy.newProxyInstance(originalObj.getClass().getClassLoader(),?originalObj.getClass().getInterfaces(),?this);
????????}

????????@Override
????????public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)?throws?Throwable?{
????????????System.out.println("pre?method");
????????????Object?result?=?method.invoke(originalObj,?args);
????????????System.out.println("post?method");
????????????return?result;
????????}
????}

????public?static?void?main(String[]?args)?{
????????System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles",?"true");
????????IHello?hello?=?(IHello)?new?DynamicProxy().bind(new?Hello());
????????hello.sayHello();
????}
}

生成代理类源码

通过设置参数
sun.misc.ProxyGenerator.saveGeneratedFiles为true,在执行main函数之后,我们将得到一份$Proxy0.class文件,它就是Hello的代理类。

经过反编译,得到$Proxy0的源码(省略了无关内容)如下:

final?class?$Proxy0?extends?Proxy?implements?DynamicProxyTest.IHello?{
????private?static?Method?m1;
????private?static?Method?m3;
????private?static?Method?m2;
????private?static?Method?m0;

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

????public?final?void?sayHello()?{
????????try?{
????????????this.h.invoke(this,?m3,?null);
????????????return;
????????}?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")?});
????????????m3?=?Class.forName("DynamicProxyTest$IHello").getMethod("sayHello",?new?Class[0]);
????????????m2?=?Class.forName("java.lang.Object").getMethod("toString",?new?Class[0]);
????????????m0?=?Class.forName("java.lang.Object").getMethod("hashCode",?new?Class[0]);
????????}?catch?(NoSuchMethodException?localNoSuchMethodException)?{
????????????throw?new?NoSuchMethodError(localNoSuchMethodException.getMessage());
????????}?catch?(ClassNotFoundException?localClassNotFoundException)?{
????????????throw?new?NoClassDefFoundError(localClassNotFoundException.getMessage());
????????}
????}
}

主要实现原理

  • 动态生成一个代理类,实现IHello接口
  • 代理类继承Proxy类,提供一个实例变量InvocationHandler h用于保存代理逻辑(此外,Proxy还提供了相关代理类处理逻辑);
  • 代理类声明一系列Method类变量,用于保存接口相关的反射方法;
  • 代理类实现相关接口方法,核心逻辑是调用InvocationHandler的invoke方法,并传入3个参数:当前代理类对象、接口反射方法对象、实际方法参数。

Javassist实现JDK动态代理

前面简单分析了JDK动态代理的基本原理,其中,最核心的逻辑在于如何生成动态代理类,也就是
java.lang.reflect.Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)方法的实现。

接下来我们将通过Javassist一步步实现newProxyInstance方法。

1. 定义接口

接口基本与Proxy.newProxyInstance相同。为简单说明,我们这里只定义了一个接口类型参数Class而不是数组。

public?static?Object?newProxyInstance(ClassLoader?loader,?Class<?>?interfaceClass,?InvocationHandler?h)?{
????...
}

2. 创建动态代理类

Javassist可以通过简单的Java API来操作源代码,这样就可以在不了解Java字节码相关知识的情况下,动态生成类或修改类的行为。

创建名称为NewProxyClass的代理类。

ClassPool?pool?=?ClassPool.getDefault();
CtClass?proxyCc?=?pool.makeClass("NewProxyClass");

3. 添加实例变量InvocationHandler

添加类型为InvocationHandler的实例变量h。

CtClass?handlerCc?=?pool.get(InvocationHandler.class.getName());
/*?生成代码:private?InvocationHandler?h;?*/
CtField?handlerField?=?new?CtField(handlerCc,?"h",?proxyCc);
handlerField.setModifiers(AccessFlag.PRIVATE);
proxyCc.addField(handlerField);

4. 添加构造函数

创建构造函数,参数类型为InvocationHandler。

//?生成构造函数:public?NewProxyClass(InvocationHandler?h)?{?this.h?=?h;?}
CtConstructor?ctConstructor?=?new?CtConstructor(new?CtClass[]?{?handlerCc?},?proxyCc);
ctConstructor.setBody("$0.h?=?$1;");
proxyCc.addConstructor(ctConstructor);

其中,$0代表this,?$1代表构造函数的第1个参数。

5. 实现IHello接口声明

//?生成接口实现声明:public?class?NewProxyClass?implements?IHello
CtClass?interfaceCc?=?pool.get(interfaceClass.getName());
proxyCc.addInterface(interfaceCc);

6. 实现IHello相关接口方法

6.1 遍历接口方法

CtMethod[]?ctMethods?=?interfaceCc.getDeclaredMethods();
for?(int?i?=?0;?i?<?ctMethods.length;?i++)?{
????//?核心逻辑在下方
}

6.2 代理方法实现

由于代理类调用invoke方法需要传入接口的反射方法对象(Method),因此,我们需要为每个方法添加一个可复用的Method类变量。

6.2.1 反射方法对象声明及初始化

/*?构造方法参数,如:new?Class[]?{?String.class,?Boolean.TYPE,?Object.class?}?*/
String?classParamsStr?=?"new?Class[0]";
if?(ctMethods[i].getParameterTypes().length?>?0)?{
????for?(CtClass?clazz?:?ctMethods[i].getParameterTypes())?{
????????classParamsStr?=?((classParamsStr?==?"new?Class[0]")???clazz.getName()?:?classParamsStr?+?","?+?clazz.getName())?+?".class";
????}
????classParamsStr?=?"new?Class[]?{"?+?classParamsStr?+?"}";
}
//?字段生成模板
String?methodFieldTpl?=?"private?static?java.lang.reflect.Method?%s=Class.forName(\"%s\").getDeclaredMethod(\"%s\",?%s);";
//?根据模板生成方法及参数构造方法字段生成语句
String?methodFieldBody?=?String.format(methodFieldTpl,?"m"?+?i,?interfaceClass.getName(),?ctMethods[i].getName(),?classParamsStr);
//?为代理类添加反射方法字段
CtField?methodField?=?CtField.make(methodFieldBody,?proxyCc);
proxyCc.addField(methodField);

通过以上逻辑,将生成类似代码如下:

private?static?Method?m0?=?Class.forName("chapter9.javassistproxy3.IHello").getDeclaredMethod("sayHello",?new?Class[0]);
private?static?Method?m1?=?Class.forName("chapter9.javassistproxy3.IHello").getDeclaredMethod("sayHello2",?new?Class[]?{?Integer.TYPE?});
private?static?Method?m2?=?Class.forName("chapter9.javassistproxy3.IHello").getDeclaredMethod("sayHello3",?new?Class[]?{?String.class,?Boolean.TYPE,?Object.class?});

6.2.2 接口方法体实现

//?invoke调用逻辑.?其中$args是实际方法传入的参数数组
String?methodBody?=?"$0.h.invoke($0,?"?+?methodFieldName?+?",?$args)";

//?如果方法有返回类型,则需要转换为相应类型后返回
if?(CtPrimitiveType.voidType?!=?ctMethods[i].getReturnType())?{
????//?对8个基本类型进行转型
????//?例如:((Integer)this.h.invoke(this,?this.m2,?new?Object[]?{?paramString,?new?Boolean(paramBoolean),?paramObject?})).intValue();
????if?(ctMethods[i].getReturnType()?instanceof?CtPrimitiveType)?{
????????CtPrimitiveType?ctPrimitiveType?=?(CtPrimitiveType)?ctMethods[i].getReturnType();
????????methodBody?=?"return?(("?+?ctPrimitiveType.getWrapperName()?+?")?"?+?methodBody?+?")."?+?ctPrimitiveType.getGetMethodName()?+?"()";
????}
????//?对于非基本类型直接转型即可
????else?{
????????methodBody?=?"return?("?+?ctMethods[i].getReturnType().getName()?+?")?"?+?methodBody;
????}
}
methodBody?+=?";";

/*?为代理类添加方法?*/
CtMethod?newMethod?=?new?CtMethod(ctMethods[i].getReturnType(),?ctMethods[i].getName(),
????????ctMethods[i].getParameterTypes(),?proxyCc);
newMethod.setBody(methodBody);
proxyCc.addMethod(newMethod);

通过以上逻辑,将生成类似代码如下:

public?void?sayHello()?{
????this.h.invoke(this,?m0,?new?Object[0]);
}

public?String?sayHello2(int?paramInt)?{
????return?(String)this.h.invoke(this,?m1,?new?Object[]?{?new?Integer(paramInt)?});
}

public?int?sayHello3(String?paramString,?boolean?paramBoolean,?Object?paramObject)?{
????return?((Integer)this.h.invoke(this,?m2,?new?Object[]?{?paramString,?new?Boolean(paramBoolean),?paramObject?})).intValue();
}

7. 生成代理类字节码

以下语句,将生成代理类字节码:D:/tmp/NewProxyClass.class

proxyCc.writeFile("D:/tmp");?//?该步骤可选

8. 生成代理对象

最后,通过调用第3步创建的构造函数,传入InvocationHandler对象,生成并返回代理类。

Object?proxy?=?proxyCc.toClass().getConstructor(InvocationHandler.class).newInstance(h);
return?proxy;

完整代码

public?static?Object?newProxyInstance(ClassLoader?loader,?Class<?>?interfaceClass,?InvocationHandler?h)?throws?Throwable?{
????ClassPool?pool?=?ClassPool.getDefault();

????//?1.创建代理类:public?class?NewProxyClass
????CtClass?proxyCc?=?pool.makeClass("NewProxyClass");

????/*?2.给代理类添加字段:private?InvocationHandler?h;?*/
????CtClass?handlerCc?=?pool.get(InvocationHandler.class.getName());
????CtField?handlerField?=?new?CtField(handlerCc,?"h",?proxyCc);
????handlerField.setModifiers(AccessFlag.PRIVATE);
????proxyCc.addField(handlerField);

????/*?3.添加构造函数:public?NewProxyClass(InvocationHandler?h)?{?this.h?=?h;?}?*/
????CtConstructor?ctConstructor?=?new?CtConstructor(new?CtClass[]?{?handlerCc?},?proxyCc);
????ctConstructor.setBody("$0.h?=?$1;");?//?$0代表this,?$1代表构造函数的第1个参数
????proxyCc.addConstructor(ctConstructor);

????/*?4.为代理类添加相应接口方法及实现?*/
????CtClass?interfaceCc?=?pool.get(interfaceClass.getName());

????//?4.1?为代理类添加接口:public?class?NewProxyClass?implements?IHello
????proxyCc.addInterface(interfaceCc);

????//?4.2?为代理类添加相应方法及实现
????CtMethod[]?ctMethods?=?interfaceCc.getDeclaredMethods();
????for?(int?i?=?0;?i?<?ctMethods.length;?i++)?{
????????String?methodFieldName?=?"m"?+?i;?//?新的方法名

????????//?4.2.1?为代理类添加反射方法字段
????????//?如:private?static?Method?m1?=?Class.forName("chapter9.javassistproxy3.IHello").getDeclaredMethod("sayHello2",?new?Class[]?{?Integer.TYPE?});
????????/*?构造反射字段声明及赋值语句?*/
????????String?classParamsStr?=?"new?Class[0]";
????????if?(ctMethods[i].getParameterTypes().length?>?0)?{
????????????for?(CtClass?clazz?:?ctMethods[i].getParameterTypes())?{
????????????????classParamsStr?=?((classParamsStr?==?"new?Class[0]")???clazz.getName()?:?classParamsStr?+?","?+?clazz.getName())?+?".class";
????????????}
????????????classParamsStr?=?"new?Class[]?{"?+?classParamsStr?+?"}";
????????}
????????String?methodFieldTpl?=?"private?static?java.lang.reflect.Method?%s=Class.forName(\"%s\").getDeclaredMethod(\"%s\",?%s);";
????????String?methodFieldBody?=?String.format(methodFieldTpl,?"m"?+?i,?interfaceClass.getName(),?ctMethods[i].getName(),?classParamsStr);
????????//?为代理类添加反射方法字段
????????CtField?methodField?=?CtField.make(methodFieldBody,?proxyCc);
????????proxyCc.addField(methodField);

????????System.out.println("methodFieldBody:?"?+?methodFieldBody);

????????/*?4.2.2?为方法添加方法体?*/
????????/*?构造方法体?*/
????????String?methodBody?=?"$0.h.invoke($0,?"?+?methodFieldName?+?",?$args)";
????????//?如果有返回类型,则需要转换为相应类型后返回
????????if?(CtPrimitiveType.voidType?!=?ctMethods[i].getReturnType())?{
????????????//?对8个基本类型进行转型
????????????//?例如:((Integer)this.h.invoke(this,?this.m2,?new?Object[]?{?paramString,?new?Boolean(paramBoolean),?paramObject?})).intValue();
????????????if?(ctMethods[i].getReturnType()?instanceof?CtPrimitiveType)?{
????????????????CtPrimitiveType?ctPrimitiveType?=?(CtPrimitiveType)?ctMethods[i].getReturnType();
????????????????methodBody?=?"return?(("?+?ctPrimitiveType.getWrapperName()?+?")?"?+?methodBody?+?")."?+?ctPrimitiveType.getGetMethodName()?+?"()";
????????????}?else?{?//?对于非基本类型直接转型即可
????????????????methodBody?=?"return?("?+?ctMethods[i].getReturnType().getName()?+?")?"?+?methodBody;
????????????}
????????}
????????methodBody?+=?";";
????????/*?为代理类添加方法?*/
????????CtMethod?newMethod?=?new?CtMethod(ctMethods[i].getReturnType(),?ctMethods[i].getName(),
????????????????ctMethods[i].getParameterTypes(),?proxyCc);
????????newMethod.setBody(methodBody);
????????proxyCc.addMethod(newMethod);

????????System.out.println("Invoke?method:?"?+?methodBody);
????}

????proxyCc.writeFile("D:/tmp");

????//?5.生成代理实例.?将入参InvocationHandler?h设置到代理类的InvocationHandler?h变量
????@SuppressWarnings("unchecked")
????Object?proxy?=?proxyCc.toClass().getConstructor(InvocationHandler.class).newInstance(h);

????return?proxy;
}

Javassist官网:http://www.javassist.org/

?

转载请注明来源:http://zhanjia.iteye.com/blog/2428564

?

个人公众号

二进制之路

?

上一篇: 递归打印文件名 下一篇: 没有下一篇了!
发表评论
用户名: 匿名