1、class文件:里面存放了许多16进制的字节(码)(如二进制字节码可表示为:010101101),如下图所示;但是这个文件,是二进制的,例如图形文件等都属于二进制文件。
2、javap -v class文件名 > 输出文件名 命令:将class文件,翻译成可读懂的字节码指令文件,里面包含了各种各样的指令,部分内容如下图所示。
3、总结:字节码文件(Java中是16进制文件)是经过编译器预处理过的一种文件,是Java的执行文件存在形式,它本身是二进制文件,但是不可以被系统直接执行,而是需要虚拟机解释执行。
1.ASM & Javassis
ASM: ASM是一个JAVA字节码分析、创建和修改的开源应用框架。它可以动态生成二进制格式的stub类或其他代理类,或者在类被JAVA虚拟机装入内存之前,动态修改类。在ASM中提供了诸多的API用于对类的内容进行字节码操作的方法。
Javassis: Javassist是一个开源的分析、编辑和创建Java字节码的类库,可以直接编辑和生成Java生成的字节码。相对于bcel, asm等这些工具,开发者不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。
前言: javassit 主要类似于用java源码加反射的机制实现改变一些操作。而 asm 主要是针对字节码进行操作。
JavaAssist和Asm都是一种在运行时,修改字节码的框架,如下图所示:
1.1.ASM
- ASM是一个通用的Java字节码操作和分析框架。它可以直接以二进制形式修改现有类或动态生成类。提供了通用的转换和分析算法,允许轻松地组装定制的复杂转换和代码分析工具。
- ASM提供了与其他字节码框架类似的功能,但它关注的是使用的简单性和性能。因为它的设计和实现尽可能的小和快,所以它非常适合在动态系统中使用。
- ASM是一个Java类操作工具,用于动态生成和操作Java类,这是实现可适应系统的有用技术。ASM基于一种新的方法,与现有的相似工具相比,后者包括使用“访问者”设计模式,而不显式地用对象表示被访问的树。对于大多数实际需求,这种新方法比现有的工具提供了更好的性能。
要想驾驭ASM,先要了解一下JAVA的CLASS文件格式。JAVA的CLASS文件通常是树型结构。根节点包含以下元素:
- ConstantPool:符号表;
- FieldInfo:类中的成员变量信息;
- MethodInfo:类中的方法描述;
- Attribute:可选的附加节点。
- FieldInfo节点包含成员变量的名称,诸如public,private,static等的标志。
- ConstantValue属性用来存储静态的不变的成员变量的值。
- Deprecated和Synthetic被用来标记一个成员变量是不被推荐的或由编译器生成的。
- MethodInfo节点包含方法的名称,参数的类型和和它的返回值,方法是公有的,私有的或静态的等标志。
- MethodInfo包含可选的附加属性,其中最重要的是Code属性,它包含非抽象的方法的代码。
- Exceptions属性包含方法将抛出的Exception的名称。
- Deprecated和Synthetic属性的信息同上面的FieldInfo的定义一样。
- 根节点的可选属性有SourceFile,InnerClasses和Deprecated。
- SourceFile用来存储被编译成字节码的源代码文件的原始名称;
- InnerClasses存储内部类的信息。由于这些属性的存在,java 的类格式是可以扩展的,也就是说可以在一个class中 附加一些非标准的属性, java虚拟机会忽略这些不可识别的属性,正常的加载这个class。
- ConstantPool是一个由数字或字符串常量的索引组成的队列,或由此类的树的其他节点引用的,由其他对象创建的被引用常量的索引组成的队列。这个表的目标是为了减少冗余。例如,FieldInfo节点不包含节点的名称,只包含它在这一表中的索引。同样的,GETFIELD和PUTFIELD不直接包含成员变量的名称,只包含名称的索引。
1.2.JAVASSIST
Javassist(Java Programming Assistant)是一个用于在运行时操作字节码的 Java 库,它允许开发人员动态生成、修改和分析 Java 类的字节码。Javassist提供了一种更高级别的 API,以 Java 代码的方式来操作字节码,而不需要直接操作复杂的字节码指令。这使得动态代码生成和修改变得更加容易和可维护。以下是 Javassist 的一些重要特点和使用方式:
-
动态代码生成: Javassist 允许您在运行时通过编写 Java 代码的方式来生成新的类和方法。这种方式使动态代码生成变得非常直观和易于理解。
-
类修改和增强: 您可以使用 Javassist 修改已经存在的类的字节码,例如添加、修改或删除字段、方法等。
-
字节码操作: Javassist 提供了一套 API,用于操作类的字节码指令,如创建方法、添加指令、修改参数等。这使得您能够以更高级别的抽象方式来进行字节码操作,而无需深入了解底层字节码细节。
-
AOP 支持: Javassist 可以用于实现 AOP(面向切面编程)的功能,通过在方法前后插入代码来实现横切关注点的处理。
-
简化反射: Javassist 可以帮助您避免使用繁琐的 Java 反射 API,通过生成字节码来直接调用方法和访问字段。
-
跨版本支持: Javassist 支持处理不同版本的 Java 字节码,因此您可以在不同的 Java 版本间进行字节码操作。
-
Javassist (Java编程助手)使操作Java字节码变得简单。它是一个用于编辑Java字节码的类库;跟其他类似的字节码编辑器不同的是,它使Java程序能够在运行时定义一个新类,并在JVM加载类文件时修改它。
-
Javassist提供了两种级别的API:源级别和字节码级别。如果用户使用源代码级API,他们可以不需要了解Java字节码的规范的前提下编辑类文件。整个API仅使用Java语言的词汇表设计。甚至你可以以源文本的形式插入字节码中;Javassist动态编译它。另一方面,字节码级API允许用户作为编辑器直接编辑类文件。
-
Javassist允许您检查、编辑和创建Java二进制类。
-
Javassist并不是唯一处理字节码的库,但它有一个特别功能,使其成为一个重要的开始来尝试字节码工作:你可以使用Javassist改变一个Java类的字节码而不需要学习任何关于字节码或Java虚拟机(JVM)的体系结构。
-
面向切面编程:Javassist可以是一个很好的工具用于向类中添加新方法,以及在调用方和被调用方两边插入before/after/around通知。
-
反射:Javassist另一个应用就是运行时反射;Javassist允许Java程序使用一个元对象,该元对象控制基级别对象上的方法调用。不需要专门的编译器或虚拟机。
-
Javassist还提供了用于直接编辑类文件的低级API。要使用此级别的API,您需要详细了解Java字节码和类文件格式,而此级别的API允许您对类文件进行任何类型的修改。
我们常用到的动态特性主要是反射,在运行时查找对象属性、方法,修改作用域,通过方法名称调用方法等。在线的应用不会频繁使用反射,因为反射的性能开销较大。其实还有一种和反射一样强大的特性,但是开销却很低,它就是Javassit。
Javassit其实就是一个二方包,提供了运行时操作Java字节码的方法。大家都知道,Java代码编译完会生成.class文件,就是一堆字节码。JVM(准确说是JIT)会解释执行这些字节码(转换为机器码并执行),由于字节码的解释执行是在运行时进行的,那我们能否手工编写字节码,再由JVM执行呢?答案是肯定的,而Javassist就提供了一些方便的方法,让我们通过这些方法生成字节码。
类似字节码操作方法ASM。几种动态编程方法相比较,在性能上Javassist高于反射,但低于ASM,因为Javassist增加了一层抽象。在实现成本上Javassist和反射都很低,而ASM由于直接操作字节码,相比Javassist源码级别的api实现成本高很多。几个方法有自己的应用场景,比如Kryo使用的是ASM,追求性能的最大化。而NBeanCopyUtil采用的是Javassist,在对象拷贝的性能上也已经明显高于其他的库,并保持高易用性。实际项目中推荐先用Javassist实现原型,若在性能测试中发现Javassist成为了性能瓶颈,再考虑使用其他字节码操作方法做优化。
Javassist对比ASM:
- Javassist源代码级API比ASM中实际的字节码操作更容易使用。
- Javassist在复杂的字节码级操作上提供了更高级别的抽象层。Javassist源代码级API只需要很少的字节码知识,甚至不需要任何实际字节码知识,因此实现起来更容易、更快。
- Javassist使用反射机制,这使得它比运行时使用Classworking技术的ASM慢。
- 总的来说ASM比Javassist快得多,并且提供了更好的性能。Javassist使用Java源代码的简化版本,然后将其编译成字节码。这使得Javassist非常容易使用,但是它也将字节码的使用限制在Javassist源代码的限制之内。
- 在ASM中,开发者需要手动编写Visitor类,通过visitMethod、visitField等方法来访问字节码文件中的方法、字段等元素,并进行修改。而在Javassist中,则是通过CtClass、CtMethod、CtField等类来访问字节码文件中的元素,通过提供的API进行修改。
- 总之,如果有人需要更简单的方法来动态操作或创建Java类,那么应该使用Javassist API 。如果需要注重性能地方,应该使用ASM库。