今日学习分享之操作增强java字节码工具asm

前言

对于我们JAVA开发人员来说能了解java编译的.class字节码文件,我们对于熟悉jvm就更进一层了,话不多说今天分享一下操作字节码工具 asm

环境

* jdk16
* 开发工具idea
* asm 9.7 (官网地址:https://asm.ow2.io/javadoc/index.html)

开始开发

今天分享一个方法执行时间案例来进行初步学习

包引入

pom文件引入asm

        <dependency>
          <groupId>org.ow2.asm</groupId>
          <artifactId>asm</artifactId>
          <version>9.7</version>
        </dependency>

asm主要组件

asm主要组件为ClassReader、ClassWriter、ClassVisitor、MethodVIsitor

ASMifier工具

ASMifier工具能解析字节码文件并呈现出结构化,ASMifier能帮助开发者快速开发字节码是一个非常好的工具

方法运行耗时案例

新建一个测试类

新建一个测试类TestFunction 并新建一个方法eat,具体代码如下:

package org.asm;

public class TestFunction {

    public void eat() throws InterruptedException {
        System.out.println("我开始吃饭啦====>");
        Thread.sleep(100);
        System.out.println("我吃完饭啦====>");
    }
}

新建一个AsmMethodVisitor 类并继承MethodVisitor ,然后重写visitCode和visitInsn方法

package org.asm;

import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

public class AsmMethodVisitor extends MethodVisitor {
    protected AsmMethodVisitor(MethodVisitor methodVisitor) {
        super(Opcodes.ASM9,methodVisitor);
    }

    @Override
    public void visitCode() {
        mv.visitCode();
    }

    @Override
    public void visitInsn(int opcode) {
        if (this.mv != null) {
            this.mv.visitInsn(opcode);
        }
    }

}

新建一个main方法用来调用eat方法,并且读取TestFunction字节码文件

package org.asm;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;

import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

public class Main {
    public static void main(String[] args) throws IOException, InterruptedException {
        String fileName = "E:\\study\\untitled\\target\\classes\\org\\asm\\TestFunction.class";
        byte[] bytecode= Files.readAllBytes(Paths.get(fileName));
        //创建ClassReader实例
        ClassReader classReader=new ClassReader(bytecode);
        ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
        AsmClassVisitor classVisitor=new AsmClassVisitor(classWriter);
        //使用ClassReader的accept方法,将MyClassVisitor传递给ClassReader进行字节码解析
        classReader.accept(classVisitor, ClassReader.SKIP_DEBUG);

        // 将字节码保存到文件
        FileOutputStream fos = new FileOutputStream(fileName);
        fos.write(classWriter.toByteArray());
        fos.close();
        TestFunction modifiedClass = new TestFunction();
        modifiedClass.eat();
    }
}

运行main方法效果如下:
在这里插入图片描述
使用ASMifier工具能解析字节码文件如下:
在这里插入图片描述
此时我们想要计算方法耗费时间则只需要添加如下代码:

package org.asm;

import java.util.Currency;

public class FuncTimeLogger {
    public static long a1=0l;

    public static void getStartTime() {
        a1 = System.currentTimeMillis();
    }

    public static void timeCost() {
        System.out.println("方法花费时间为:" + (System.currentTimeMillis() - a1));
    }

}
package org.asm;

public class TestFunction {

    public void eat() throws InterruptedException {
        FuncTimeLogger.getStartTime();
        System.out.println("我开始吃饭啦====>");
        Thread.sleep(100);
        System.out.println("我吃完饭啦====>");
        FuncTimeLogger.timeCost();
    }
}

运行结果如下:
在这里插入图片描述
再次使用ASMifier工具能解析并比对上一次执行,发现多了两行
在这里插入图片描述
此时我们使用asm工具进行字节码文件修改代码如下:

package org.asm;

import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

public class AsmMethodVisitor extends MethodVisitor {
    protected AsmMethodVisitor(MethodVisitor methodVisitor) {
        super(Opcodes.ASM9,methodVisitor);
    }

    @Override
    public void visitCode() {

        mv.visitCode();
        mv.visitMethodInsn(Opcodes.INVOKESTATIC, "org/asm/FuncTimeLogger", "getStartTime", "()V");
    }

    @Override
    public void visitInsn(int opcode) {
        mv.visitMethodInsn(Opcodes.INVOKESTATIC, "org/asm/FuncTimeLogger", "timeCost", "()V");

        if (this.mv != null) {
            this.mv.visitInsn(opcode);
        }
    }

}

执行方法去除调用耗时的方法

package org.asm;

public class TestFunction {

    public void eat() throws InterruptedException {
        System.out.println("我开始吃饭啦====>");
        Thread.sleep(100);
        System.out.println("我吃完饭啦====>");
    }
}

此时我们运行一下项目看一下效果:
在这里插入图片描述
查看字节码文件发现字节码文件已被修改:
在这里插入图片描述

结语:这是一个简单的案例给大家学习一下熟悉asm,asm字节码增强有很多应用场景,大家可以更深入的学习
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值