登陆

Java常见几种动态署理的比照

admin 2020-02-14 128人围观 ,发现0个评论

Java中常见的动态署理有:JDK动态署理、cglib、ASM和bytebuddy等,如下所示:

  • JDK动态署理:运行期动态的创立Java常见几种动态署理的比照署理类,只支撑接口;
  • ASM:一个 Java 字节码控制结构。它可以以二进制方法修正已有类或许动态生成类。不过ASM在创立class字节码的过程中,操作的等级是底层JVM的汇编指令等级,这要求ASM运用者要对class安排结构和JVM汇编指令有必定的了解;
  • javassist:一个开源的剖析、修正和创立Java字节码的类库(源码等级的类库)。javassist是jboss的一个子项目,其主要的长处,在于简略,而且快速。直接运用java编码的方法,而不需要了解虚拟机指令,就能动态改动类的结构,或许动态生成类;
  • bytebuddy:一个更高层次操作字节码的东西包。

JDK动态署理

Java在JDK1.3后引进的动态署理机制,使咱们可以在运行期动态的创立署理类。动态署理是在运行期间经过接口生成署理类的,与静态署理比较愈加灵敏,可是也有必定的约束,第一是署理方针有必要完成一个接口,不然会报反常,因为其原理便是依据接口来生成署理方针的。第二是有功能问题,因为是经过反射来完成调用的,所以比正常的直接调用来得慢,而且经过生成类文件也会多耗费部分办法区空间,或许引起Full GC。

一个JDK动态署理示例如下:

public static interface Hello {

void hi(String msg);

}

public static class HelloImpl implements Hello {

@Override

public vJava常见几种动态署理的比照oid hi(String msg) {

System.out.println("hello " + msg);

}

}

/**

* 署理类

*/

@Data

@NoArgsConstructor

@AllArgsConstructor

public static class HelloProxy implements InvocationHandler {

private Object proxied = null;

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

System.out.println("hello proxy");

return method.invoke(proxied, args);

}

}

public static void main(String[] args) {

Hello hello = (Hello) Proxy.newProxyInstance(Hello.class.getClassLoader(), new Class[]{Hello.class}, new HelloProxy(new HelloImpl()));

hello.hi("world");

}

JDK动态署理生成proxy class的流程,以上述示例代码为例,从Proxy.newProxyInstance()办法开端:首要加载被署理类的接口的class,然后经过履行办法java.lang.reflect.Proxy.ProxyClassFactory#apply创立对应proxy class,其内部会调用ProxyGenerator.generateProxyClass()办法生成二进制class数据,然后履行java.lang.reflect.Proxy#defineClass0回来对应class实例,至此生成proxy class流程就完成了。

关于Java动态署理更多材料可参阅:https://mp.weixin.qq.com/s/0dLvgsDo32OvuWYI3wESJw

ASM

ASM 是一个 Java 字节码控制结构。它可以以二进制方法修正已有类或许动态生成类。ASM 可以直接发生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改动类行为(也便是生成的代码可以掩盖本来的类也可以是原始类的子类)。ASM 从类文件中读入信息后,可以改动类行为,剖析类信息,乃至可以依据用户要求生成新类。

不过ASM在创立class字节码的过程中,操作的等级是底层JVM的汇编指令等级,这要求ASM运用者要对class安排结构和JVM汇编指令有必定的了解。ASM供给Java常见几种动态署理的比照了两组API:Core API 和Tree API,Core API是根据访问者形式来操作类的,而Tree是根据树节点来操作类的

留意:Spring和Hibernate中的cglib是一个根据ASM的更高层次的主动代码生成东西。关于ASM更多材料可参阅:https://www.ibm.com/developerworks/cn/java/j-lo-asm30/index.html

javassist

Javassist是一个开源的剖析、修正和创立Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶 滋)所创立的。它已加入了开放源代码JBoss 运用服务器项目,经过运用Javassist对字节码操作为JBoss完成动态"AOP"结构。一句话总结便是:Javassit其实便是一个二方包,供给了运行时操作Java字节码的办法。

Java代码编译完生成.class文件,就JVM(精确说是JIT)会解说履行这些字节码(转换为机器码并履行),因为字节码的解说履行是在运行时进行的,那咱们能否手艺编写或许更改字节码,再由JVM履行呢?答案是必定的,而Javassist就供给了一些便利的办法,让咱们经过这些办法生成字节码,而不必重视class文件数据格式。

相似字节码操作办法还有ASM。几种动态编程办法比较较,在功能上Javassist高于反射,但低于ASM,因为Javassist增加了一层笼统。在完本钱钱上Javassist和反射都很低,而ASM因为直接操作字节码,比较Javassist源码等级的api完本钱钱高许多。几个办法有自己的运用场景,比方Kryo运用的是ASM,寻求功能的最大化。而NBeanCopyUtil选用的是Javassist,在方针复制的功能上也现已显着高于其他的库,并坚持高易用性。实践项目中引荐先用Javassist完成原型,若在功能测验中发现Javassist成为了功能瓶颈,再考虑运Java常见几种动态署理的比照用其他字节码操作办法做优化。

留意;上述说的在功能上Javassist高于反射,但低于ASM是指生成字节码流程的这个功能,而不是生成class的履行功能,因为生成的都是class,二者的履行功能理论上是相同的。

javassist实例代码:

package com.test;

import javassist.ClassPool;

import javassist.CtClass;

import javassist.CtMethod;

import javassist.CtNewMethod;

public class Account {

public void operation(String value) {

System.out.println("operJava常见几种动态署理的比照ation... " + value);

}

public static void main(String[] args) throws Exception {

ClassPool classPool = C拜托了学妹lassPool.getDefault();

CtClass ctClass = classPool.makeClass("test.SubAccount");

ctClass.setSuperclass(classPool.get("com.test.Account"));

// 调用办法

((Account) ctClass.toClass().newInstance()).operation("");

// 增加办法并调用

ctClass = classPool.makeClass("test.SubAccount2");

ctClass.setSuperclass(classPool.get("com.test.Account"));

ctClass.addMethod(CtNewMethod.make(

"public void operation(String value) { System.out.println(\"operation... from Sub\"); }", ctClass));

((Account) ctClass.toClass().newInstance()).operation("");

// 更改现有办法

ctClass = classPool.get("com.test.Account");

CtMethod method = ctClass.getDeclaredMethod("operation");

method.insertBefore("{ System.out.println($1); }"); // $1 表明函数入栈第一个参数

new Account().operation("param1");

}

}

输出:

operation...

operation... from Sub

operation... param1

bytebuddy

Byte Buddy是致力于处理字节码操作和 instrumentation API 的复杂性的开源结构。Byte Buddy 所宣称的方针是将显式的字节码操作隐藏在一个类型安全的范畴特定言语背面。经过运用 Byte Buddy,任何了解 Java 编程言语的人都有望十分容易地进行字节码操作。

Byte Buddy是一个较高层级的笼统的字节码操作东西,相较于ASM而言。其实吧,Byte Buddy 自身也是根据 ASM API 完成的。更多材料请参阅:https://www.infoq.cn/article/Easily-Create-Java-Agents-with-ByteBuddy

如下的示例展示了怎么生成一个简略的类,这个类是 Object 的子类,而且重写了 toString 办法,用来回来“Hello World!”。与原始的 ASM 相似,“intercept”会告知 Byte Buddy 为阻拦到的指令供给办法完成:

Class

.subclass(Object.class)

.method(ElementMatchers.named("toString"))

.intercept(FixedValue.value("Hello World!"))

.make()

.load(Object.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)

.getLoaded();

System.out.println(dynamicType.getSimpleName());

// 输出:Object$ByteBuddy$ilIxkTl1

小结

从Java动态署理完成上来看,可分为两种战略:一种是操作字节码,创立新类或许修正现有类,比方ASM/byte-buddy/Java动态署理;另一种是运用Java编码方法创立新类或许修正现有类,比方javassist。

关于动态署理的功能问题,假如是生成的class是被署理类的子类或许便是(增加了署理逻辑的)被署理类自身,理论上功能和直接直接被署理类逻辑是相同的(署理自身逻辑忽略不计情况下);假如生成的class是经过反射等方法进行调用被署理类逻辑的,那么功能上相对来说是比较弱的,比方Java动态署理。


引荐阅览

请关注微信公众号
微信二维码
不容错过
Powered By Z-BlogPHP