`
lijingyao8206
  • 浏览: 219302 次
  • 性别: Icon_minigender_2
  • 来自: 杭州
社区版块
存档分类
最新评论

请您先登录,才能继续操作

ASM系列一 利用Core API 解析和生成字节码

阅读更多

 

 

ASM是一个提供字节码解析和操作的框架。Cglib框架就是基于ASM框架实现的,被广泛应用的Hibernate,Spring就是基于Cglib 实现了AOP技术。

在说到AOP的Java实现,可能会优先想到java的Proxy api,通过invoke方法拦截处理相应的代码逻辑,但是proxy 是面向接口的,被代理的class的所有方法调用都会通过反射调用invoke 方法,相对性能开销大。另外的还有Java 5提供的Instrument,比较适用于监控检查方面,但在处理灵活的代码逻辑方面并不合适。

ASM 框架对用户屏蔽了整个类字节码的长度,偏移量,能够更加灵活和方便得实现对字节码的解析和操作。其主要提供了两部分主要的API,Core Api 及Tree Api。本文先从Core Api的解析和生成字节码开始介绍。

CoreApi 在解析和生成这里,主要用的的类是ClassVisitor。ClassVisitor在2.0版本的时候是个接口,对于class文件操作都是访问ClassAdepter。这里主要基于4.0以后的API做介绍。

先看一下ClassVisitor代码:

 

public abstract class ClassVisitor {
public ClassVisitor(int api);
public ClassVisitor(int api, ClassVisitor cv);
public void visit(int version, int access, String name,
String signature, String superName, String[] interfaces);
public void visitSource(String source, String debug);
public void visitOuterClass(String owner, String name, String desc);
AnnotationVisitor visitAnnotation(String desc, boolean visible);
public void visitAttribute(Attribute attr);
public void visitInnerClass(String name, String outerName,
String innerName, int access);
public FieldVisitor visitField(int access, String name, String desc,
String signature, Object value);
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions);
void visitEnd();
}



ClassVisitor 的调用必须是遵循下面的调用顺序的:

 

 

  visit visitSource? visitOuterClass? ( visitAnnotation | visitAttribute )*
( visitInnerClass | visitField | visitMethod )*
visitEnd



 


围绕着ClassVisitor ,还有两个核心类:
后续的例子代码中可以看到,我们必须先调用visit方法,这就因为class是字节流的二进制文件,而我们解析和生成也是要遵循一定的顺序。ClassVisitor定义了我们需要操作的所有接口,并且ClassVisitor也可以接收一个ClassVisitor实例来构造,有点类似于一个事件的filter,可以套很多层的filter来一层层处理逻辑。

1、ClassReader 将class解析成byte 数组,然后会通过accept方法去按顺序调用绑定对象(继承了ClassVisitor的实例)的方法。可以视为一个事件的生产者。

2、ClassWriter 是ClassVisitor 的子类。直接可以通过toByteArray()方法以返回的byte数组形式构建编译后的class。可以视为一个事件的消费者。

下面来看下具体这三个Api的应用。

一、解析

因为Java编译后的class文件就是由特定格式的字节流组成的二进制文件。这里不展开说明class文件结构。那么我们可以通过ASM中的ClassReader 类来解析类的方法,属性,注解,父类和接口信息,内部类等等。下面这个例子主要示范下打印类的一些属性、方法信息(javap 工具类似功能)。

 

Task 类是我们需要解析的类。

 

package asm.core;
 
/**
 * Created by yunshen.ljy on 2015/6/8.
 */
public class Task {
 
    private int isTask = 0;
 
    private long tell = 0;
 
    public void isTask(){
        System.out.println("call isTask");
    }
    public void tellMe(){
        System.out.println("call tellMe");
    }
}



 


ClassPrintVisitor 类继承自ClassVisitor类来打印解析类的类名,父类名以及“is”开头的属性和方法。

 

package asm.core;
 
import org.objectweb.asm.*;
 
/**
 * Created by yunshen.ljy on 2015/6/8.
 */
public class ClassPrintVisitor extends ClassVisitor {
 
 
    public ClassPrintVisitor() {
        super(Opcodes.ASM4);
    }
 
    public void visit(int version, int access, String name,
                      String signature, String superName, String[] interfaces) {
        System.out.println(name + " extends " + superName + " {");
    }
 
    public FieldVisitor visitField(int access, String name, String desc,
                                   String signature, Object value) {
        if (name.startsWith("is")) {
            System.out.println(" field name: " + name + desc);
        }
        return null;
    }
 
    public MethodVisitor visitMethod(int access, String name,
                                     String desc, String signature, String[] exceptions) {
        if (name.startsWith("is")) {
            System.out.println(" start with is method: " + name + desc);
        }
        return null;
    }
 
    public void visitEnd() {
        System.out.println("}");
    }
}



 


下面是测试类ClassesPrintTest 。将一个ClassPrintVisitor 对象传给ClassReader。ClassReader作为一个解析事件的producer 并且由ClassPrintVisitor去消费(处理打印逻辑)。accept()方法就将Task 字节码进行解析,然后调用ClassPrintVisitor 的方法。

 

package asm.core;
 
import org.objectweb.asm.ClassReader;
 
import java.io.IOException;
 
/**
 * Created by yunshen.ljy on 2015/6/8.
 */
public class ClassesPrintTest {
 
    public static void main(String[] args) {
        try {
            ClassReader cr = new ClassReader("asm.core.Task");
            ClassPrintVisitor cp = new ClassPrintVisitor();
            cr.accept(cp, 0);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}



 

调用结果:

 

asm/core/Task extends java/lang/Object {
 field name: isTaskI
 start with is method: isTask()V
}



 

这里我们打印了is开头的属性名以及描述,方法名以及描述。这里的desc其实是class文件属性修饰或者方法参数、返回值的全限定名(fully qualified name)。这里isTask后面的I 代表的是int类型的描述。IsTask()方法后面的V表示返回值是void。详细的属性、方法描述可以参考《The Java Virtual Machine Specification》

 

二、生成

生成字节码,听起来有点暴力。Java编译后的class文件是以字节流的形式组成。所以也就是生成一个byte数组,就是之前说的以“特定格式”生成一个可被JVM加载、执行的字节流。下面这个例子是生成一个类ChildClass,继承自ParentInter 接口,这个是个空接口(没有具体变量和方法,只是为了示范代码)。我们用ASM 的ClassWriter 来实现。先看一下ChildClass 类的代码:

 

package asm.core;
 
import asm.core.ParentInter;
 
public abstract class ChildClass implements ParentInter {
    public static final int zero = 0;
 
    public abstract int compareTo(Object var1);
}



 


我们通过调用ClassVisitor的方法就能实现生成上述代码。

 

private static byte[] gen() {
        ClassWriter cw = new ClassWriter(0);
        cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC + Opcodes.ACC_ABSTRACT ,
                "asm/core/ChildClass", null, "java/lang/Object", new String[]{"asm/core/ParentInter"});
      
 cw.visitField(Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, "zero", "I", null, new Integer(0))
                .visitEnd();
     
  cw.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_ABSTRACT, "compareTo", "(Ljava/lang/Object;)I", null, null)
                .visitEnd();
        cw.visitEnd();
        return cw.toByteArray();
    }

 


生成一个能代表class文件的字节数组,我们需要先调用visit 方法生成一个class的头部信息。这里的几个参数稍作解释:第一个参数Opcode是ASM内部定义的操作数常量,这里的V1_5代表Java1.5,第二个参数代表类的修饰符,这里是一个抽象类。第三个是类名。第四个参数本例传null ,因为这里我们没有一个类型参数。后面两个是父类和接口。visitField()和visitMethod()方法分别生成我们的属性和方法,这里看到都会再调用visitEnd方法,是因为我们这里的field后续没有相应visitAnnotation 、visitAttribute等方法调用,method 后续也没有其他调用。最后调用cw.visitEnd()结束整个创建过程,toByteArray()返回了我们需要的代表这个类的byte 数组。
这里我们是new 一个ClassWriter。ClassWriter 继承自ClassVisitor。前面已经介绍过ClassVisitor。这里ClassWriter 的toByteArray() 返回的字节数组就能代表一个我们生成的class。

通过这个byte数组我们可以通过ClassLoader来加载我们的类,也可以用FileOutputStream来生成个class文件。

 

 

 

分享到:
评论
2 楼 lijingyao8206 2015-06-13  
javafound 写道
大神复出啊~~~

恩~~~之前也一直学一些东西,都有做笔记(写本子上),然后后来基本发现看不懂以前自己写的什么乱七八糟的了,所以还是电子版靠谱,不用猜缭乱的字到底是啥意思。打算复习复习,把笔记搬搬家 
1 楼 javafound 2015-06-12  
大神复出啊~~~

相关推荐

    asm5.1源码和jar

    源代码部分是学习和理解ASM工作原理的关键,通过阅读源代码,开发者可以深入到ASM的内部机制,了解如何解析和生成字节码。ASM的源码通常包含多个模块,如core、tree、analysis、visit等,每个模块都有其特定的功能。...

    asm-all-3.3

    1. **ASM Core**:这是ASM框架的基础,提供了解析和生成Java字节码的核心API。它包含了`ClassReader`和`ClassWriter`两个关键类,分别用于读取和写入类文件。`ClassReader`可以解析一个类的字节码,并触发用户自定义...

    asm-giude阅读笔记

    Java ASM是一个强大的库,专为Java字节码的分析、生成和转换而设计。它被广泛应用于动态代理、代码混淆、AOP(面向切面编程)等领域。ASM的核心优势在于其简单、模块化且功能强大的API,以及详尽的文档和Eclipse插件...

    asm-util-3.2.jar.zip

    ASM是一个开源的Java字节码操控和分析框架,它可以直接用来动态生成类或者增强已有类的功能。ASM库广泛应用于Java字节码级别的编程,例如在AOP(面向切面编程)、代码生成、代码分析等领域。 这个压缩包的核心是`...

    asm4-guide

    ASM提供了一套丰富的API,通过这些API可以读取、修改和生成Java字节码,这对于在运行时动态生成代码,例如在应用服务器中加载用户定义的类,或者执行字节码的后端优化等场景中非常有用。 描述中提到“动态加载的...

    02 - ASM组成部分 - 简书1

    7. **Opcodes**:这是一个枚举类,包含了所有的Java虚拟机操作码,用于生成字节码。 8. **Type**:用于处理Java类型的类,包括基本类型、对象类型和数组类型。 Core API的其他类,如asm-util.jar和asm-commons.jar...

    asm-tree-1.3.5.jar.zip

    而ASM Tree API是ASM的一个扩展,它将字节码表示为一棵抽象语法树(AST),使得对字节码的操作更为直观和方便。在处理复杂的类结构和代码逻辑时,这种树状结构特别有用,因为它允许开发者以结构化的方式遍历和修改...

    asm-util-1.4.1.jar.zip

    ASM是一个流行的Java字节码操控和分析框架,广泛用于动态代理、代码生成和静态代码分析等领域。ASM库的核心功能是能够直接操作和生成Java虚拟机(JVM)理解的字节码,这对于创建和修改类或方法的行为非常有用。 ...

    asm-analysis-1.5.1.jar.zip

    ASM库分为核心库(Core)和分析库(Analysis),核心库主要负责生成和解析字节码,而分析库则在此基础上增加了对字节码的静态分析功能。 描述中的"asm,analysis,1.5.1,jar.zip包下载,依赖包"关键词指出这个压缩包与...

    asm-tree-1.5.3.jar.zip

    ASM Tree库是Java开发者在处理字节码操作时经常会用到的一个工具库,它作为ASM框架的一部分,为开发者提供了一种方便的方式来解析和构建抽象语法树(Abstract Syntax Tree, AST)。ASM是一个Java字节码操控和分析...

    asm-1.4.1.jar.zip

    ASM是一个Java字节码操控和分析框架,它能够用来动态生成类或者增强已有类的功能。这个框架的核心在于它能够直接操作Java字节码,提供了一种低级别的手段来理解和修改类和方法。ASM库在Java开发中的主要用途包括代码...

    asm-tree-3.0_RC1.jar.zip

    **ASM库**: ASM是一个低级别的库,它可以直接操作和生成Java字节码。它主要用于动态代理、代码分析和转换,以及运行时代码修改。ASM提供了一种方式来深入理解Java类和方法的内部结构,这在创建编译器、反编译器、...

    06 - ASM之ClassVisitor - 简书1

    ASM是一个强大的Java字节码操控和分析框架,它允许动态生成类或者增强已有类的功能。在ASM中,`ClassVisitor`扮演着核心角色,它定义了一组方法来访问和修改类的信息。`ClassReader`、`ClassVisitor`和`ClassWriter`...

    java_cglib_lib.rar

    4. **asm-tree.jar**:ASM Tree API提供了将字节码解析为抽象语法树(AST)的工具,使得开发者可以更方便地遍历和修改类结构。CGlib在生成子类或处理方法时会用到这个库。 CGlib的使用步骤通常包括以下几个部分: ...

    asm-commons.rar_Java__Java_

    ASM Core提供了ClassReader和ClassWriter类,用于读取和生成字节码。 2. **ASM Commons**:这个模块是ASM的扩展,包含了一些通用工具和辅助类,使得使用ASM更加方便。例如,它提供了一些用于处理字段和方法的工具,...

    ssh整合jar包

    3. **ASM**: ASM是一个Java字节码操控和分析框架,用于动态生成类或者增强已有类的功能。在Hibernate中,ASM被用来生成和修改类的字节码,以实现透明的持久化。同时,Spring在实现AOP代理时,也可以选择使用ASM来...

    robolectric所有一来的jar包

    9. **asm-commons-5.0.1.jar**:这是ASM库的一个扩展,包含了用于解析和生成Java字节码的通用工具和类。与asm-5.0.1.jar配合使用,共同为Robolectric提供字节码级别的支持。 10. **robolectric-3.0.jar**:这是...

    lombok 实现原理(代码)

    它利用了Java的反射API,通过ASM库(一个流行的字节码操作库)在字节码层面插入所需的方法。 5. IDE集成 为了在IDE中得到更好的支持,Lombok提供了IDE插件,如Eclipse和IntelliJ IDEA的插件。这些插件在编写代码时...

    hibernate所包含的所有jar包

    4. **javassist.jar**:Hibernate使用javassist动态生成字节码,实现对实体类的增强,例如懒加载、级联操作等。 5. **jta.jar**(或者javax.transaction-api.jar):支持JTA(Java Transaction API),用于处理...

Global site tag (gtag.js) - Google Analytics