【JAVA】字节码框架asm生成类的简单应用

JAVA字节码框架ASM生成类的简单应用

一、ASM框架的简介

ASM 是一个通用的 Java 字节码操作和分析框架。一般用来动态生成类或者增强既有类的功能。也就是既可以创建class文件,也可以修改class文件。
ASM官网:https://asm.ow2.io/index.html

在这里插入图片描述

简单描述就是一个操作java字节码的框架
众所周知,java的执行过程是将java文件通过javac编译成为class字节码文件,但是字节码文件又是一个晦涩难懂的二进制文件,虽然有相应的规范,但是理解起来还是非常的繁琐,不容易去记忆。
在这里插入图片描述>(图片来自网络,侵权必删)

二、代码实现

前言

本篇博客仅是简单通过ASM框架生成一个类,并且使用这个类输出HelloWorld!,仅作新手了解认识ASM框架的启蒙,并不做具体内容的讲解。

HelloWorldByAsm.java

我们先放整个代码,然后再对代码内容进行分析
首先在maven里面导入依赖

    <dependency>
      <groupId>org.ow2.asm</groupId>
      <artifactId>asm</artifactId>
      <version>9.2</version>
    </dependency>
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

import java.lang.reflect.Method;

public class HelloWorldByAsm {
    public static void main(String[] args) throws Exception {
        // 生成二进制字节码
        byte[] bytes = MyClassLoader.generate();
        // 使用自定义的ClassLoader
        MyClassLoader cl = new MyClassLoader();
        // 加载我们生成的 HelloWorld 类
        Class<?> clazz = cl.defineClass("com.kevinwalker.asm.HelloWorld", bytes);
        // 反射获取 main 方法
        Method main = clazz.getMethod("main", String[].class);
        // 调用 main 方法
        main.invoke(null, new Object[]{new String[]{}});
    }
}

/**
 * 自定义ClassLoader以支持加载字节数组形式的字节码
 */
class MyClassLoader extends ClassLoader {
    public Class<?> defineClass(String name, byte[] b) {
        // ClassLoader是个抽象类,而ClassLoader.defineClass 方法是protected的
        // 所以我们需要定义一个子类将这个方法暴露出来
        return super.defineClass(name, b, 0, b.length);
    }

    static byte[] generate() {
        ClassWriter cw = new ClassWriter(0);
        // 定义对象头:版本号、修饰符、全类名、签名、父类、实现的接口
        cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "com/kevinwalker/asm/HelloWorld",
                null, "java/lang/Object", null);
        // 添加方法:修饰符、方法名、描述符、签名、抛出的异常
        MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "main",
                "([Ljava/lang/String;)V", null, null);
        // 执行指令:获取静态属性
        mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
        // 加载常量 load constant
        mv.visitLdcInsn("HelloWorld!");
        // 调用方法
        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
        // 返回
        mv.visitInsn(Opcodes.RETURN);
        // 设置栈大小和局部变量表大小
        mv.visitMaxs(2, 1);
        // 方法结束
        mv.visitEnd();
        // 类完成
        cw.visitEnd();
        // 生成字节数组
        return cw.toByteArray();
    }
}

下面我们直接运行该类,得到如下结果:
在这里插入图片描述
可以看到输出HelloWorld!了。

HelloWorldByAsm类

如果仔细阅读过代码可以看到main函数部门并没有写输出的语句,而实际的情况是通过我们定义的MyClassLoader类去重写defineClass方法从而通过bytes去实例出来我们所要生成的HelloWorld类,然后通过反射去调用HelloWorld类的main函数,整个过程中我们并没有去写这个HelloWorld类,而是通过generate方法生成了这个类的bytes。
在这里插入图片描述

generate方法

在这里插入图片描述
第一步:我们告诉ASM我们想要开始编写一个新类的方式。我们让ClassWriter为我们生成这些类的字节数据。
第二步:在visit方法内,一定要引入Java8,因为需要jre>=8才能运行,后面设置访问修饰符,类名地址等参数。
第三步:通过MethodVisitor来描述main方法,并且通过([Ljava/lang/String;)V描述参数及返回值。
第四步:获取静态属性out。(也就是System.out)
第五步:加载常量’HelloWorld!‘。
第六步:调用静态属性out的println方法输出’HelloWorld!’。
第七步:返回,设置栈大小和局部变量大小。
第八步:返回cw.toByteArray(),ClassWriter帮我们生成的类的字节参数。

最后补充

再次说明,本教程仅作为对ASM简单应用的介绍,并不是一篇完整的教程,笔者尽可能的 通过简洁的语言和代码来展示ASM的强大能力,当然ASM远不止本篇介绍的生成类的功能。希望本篇教程可以给你带来对asm简单的体验和启蒙。下面附上部分资料:

关于第三步的([Ljava/lang/String;)V想了解的可以参考:https://blog.csdn.net/longaiyunlay/article/details/80049440
再补充一个ASM框架的javadoc:https://asm.ow2.io/javadoc/index.html

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值