ASM字节码编程-01

本文介绍了ASM框架,一个用于Java字节码操控和分析的库。通过ASM,可以在类加载入JVM前修改其行为。文章展示了如何引入ASM库,并通过示例说明如何使用ASM生成一个简单的类,包括构造方法和计算方法。还提到了如何自定义局部变量名,并提供了Java类型与类型描述符的对应表。
摘要由CSDN通过智能技术生成

最近在学习jacoco,看源码的时候发现插桩是用的asm插入字节码,所以准备学习下这个东东。话不多说,搞起

ASM 是一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class 文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。

  •  项目引入asm库
 <properties>
        <!-- Dependencies versions -->
        <asm.version>9.1</asm.version>
    </properties>
<dependency>
            <groupId>org.ow2.asm</groupId>
            <artifactId>asm</artifactId>
            <version>${asm.version}</version>
        </dependency>
        <dependency>
            <groupId>org.ow2.asm</groupId>
            <artifactId>asm-commons</artifactId>
            <version>${asm.version}</version>
        </dependency>
        <dependency>
            <groupId>org.ow2.asm</groupId>
            <artifactId>asm-tree</artifactId>
            <version>${asm.version}</version>
        </dependency>
        <dependency>
            <groupId>org.ow2.asm</groupId>
            <artifactId>asm-analysis</artifactId>
            <version>${asm.version}</version>
        </dependency>
        <dependency>
            <groupId>org.ow2.asm</groupId>
            <artifactId>asm-util</artifactId>
            <version>${asm.version}</version>
        </dependency>
  •  写一个类,用asm的api 生成一个类,详情看注释
public class AsmTest {

    @Test
    public void test(){
        ClassWriter classWriter = new ClassWriter(0);
        {
            /**
             * 创建一个构造方法
             */
            //visitMethod(修饰符、全类名、签名、父类、实现的接口)
            MethodVisitor mv = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "(Z)V", null, null);
            // 加载隐含的this对象,这是每个JAVA方法都有的
            mv.visitVarInsn(Opcodes.ALOAD, 0);
            //INVOKESPECIAL用来调用当前类的实例化方法,私有方法以及父类的方法
            mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
            mv.visitInsn(Opcodes.RETURN);
            mv.visitMaxs(1, 1);
            mv.visitEnd();
        }
        {
            /**
             * 定义类的内容
             */
            // 定义对象头:版本号、修饰符、全类名、签名、父类、实现的接口
            classWriter.visit(Opcodes.V1_7,Opcodes.ACC_PUBLIC,"com/zzu/asm/AccSum",null,"java/lang/Object",null);
            // 添加方法;修饰符、方法名、参数(2个int类型的入参,返回值int)、签名、异常
            MethodVisitor mv = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "sum", "(II)I", null, null);
            mv.visitVarInsn(Opcodes.ILOAD,1);
            mv.visitVarInsn(Opcodes.ILOAD,2);
            mv.visitInsn(Opcodes.IADD); //2个int类型相加
            //返回int 类型
            mv.visitInsn(Opcodes.IRETURN);
            // 设置操作数栈的深度和局部变量的大小:2个数计算,加上this 总共3个变量
            mv.visitMaxs(2, 3);
            mv.visitEnd();
        }
        //类写完
        classWriter.visitEnd();
        byte[] data = classWriter.toByteArray();

        writeToFile("AccSum.class",data);
    }






    public static void writeToFile(String className,byte[] data) {
        File dataFile = new File(className);
        try {
            FileOutputStream outputStream = new FileOutputStream(dataFile);
            outputStream.write(data);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
  • 用jd-gui 查看生成的文件

暂时告一段路,发现一个问题:变量名自动生成的,int类型的是paramsInt1,boolean类型是paramBoolean, 如果想定制变量名改怎么做呢?

用到了 visitLocalVariable 创建局部变量

{
            /**
             *定义本地变量,可以用在定义方法里面
             */
            mv = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "sumA", "(II)I", null, null);
           //Starts the visit of the method's code
            mv.visitCode();
            Label l0 = new Label();
            mv.visitLabel(l0);
            mv.visitLineNumber(7,l0);
            mv.visitVarInsn(Opcodes.ILOAD,1);
            mv.visitVarInsn(Opcodes.ILOAD,2);
            mv.visitInsn(IADD);
            mv.visitInsn(IRETURN);
            Label l1 = new Label();
            mv.visitLabel(l1);
            mv.visitLocalVariable("numA", "I", null, l0, l1, 1);
            mv.visitLocalVariable("numB", "I", null, l0, l1, 2);
            mv.visitMaxs(2, 3);
            mv.visitEnd();
        }

用jd-gui 查看生成的文件

基础类型描述符

Java 类型

类型描述符

boolean

Z

char

C

byte

B

short

S

int

I

float

F

long

J

double

D

Object

Ljava/lang/Object;

int[]

[I

Object

[[Ljava/lang/Object;

  • 方法描述符

源文件中的方法声明

方法描述符

void m(int i, float f)

(IF)V

int m(Object o)

(Ljava/lang/Object;)I

int[] m(int i, String s)

(ILjava/lang/String;)[I

Object m(int[] i)

([I)Ljava/lang/Object;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值