# ASM字节码增强技术

## ASM是什么

ASM是一个Java字节码操控框架。它可以被用来动态生成类或者增强既有类的功能。ASM可以直接产生二进制的class文件，也可以在类被加载到Java虚拟机之前改变类行为。ASM的应用场景有AOP（cglib基于ASM）、热部署、修改其它jar包中的类等。

核心API：

* ClassReader：用于读取已经编译好的.class文件；
* ClassWriter：用于重新构建编译后的类，如修改类名、属性及方法等，或者生成一个新的类；
* ClassVistor：ASM内部采用访问者模式根据字节码从上到下依次处理，对于字节码文件中不同的区域有不同的Visitor。

```
访问类文件时，会回调ClassVistor的visit方法；
访问类注解时，会回调ClassVistor的visitAnnotation方法；
访问类成员时，会回调ClassVistor的visitField方法；
访问类方法时，会回调ClassVistor的visitMethod方法；
.......

当访问不同区域时会回调相应方法，该方法会返回一个对应的字节码操作对象。通过修改这个对象就可以修改class文件相应结构对应的内容。最后将这个对象的字节码内容覆盖原先的.class文件就可以实现字节码的切入。
```

## 示例代码

创建一个新的类，并调用其方法。

```
引入asm-all-5.2.jar，这里使用的版本是5.2
```

```java
package com.sankuai.test.bytecode;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import static org.objectweb.asm.Opcodes.*;

import java.io.IOException;
import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.Method;

public class ByteCode {

    public static void main(String[] args) throws Exception {
        byte[] data = genMyClass2();

        // 加载类
        MyClassLoader myClassLoader = new MyClassLoader();
        Class<?> cls = myClassLoader.defineClass("MyClass2", data);

        // 获取foo方法
        Method foo = cls.getMethod("foo", int.class);

        // 创建MyClass2类的对象
        Object obj = cls.newInstance();

        // 调用foo方法，输出：8
        System.out.println(foo.invoke(obj, 5));
    }

    static class MyClassLoader extends ClassLoader {
        public Class<?> defineClass(String name, byte[] b) {
            return super.defineClass(name, b, 0, b.length);
        }
    }

    /** 用asm创建下面这个类的字节码：
     public class MyClass2{
        public int foo(int a){
            return a + 3;
        }
     }
     */
    public static byte[] genMyClass2() throws IOException {
        // 创建一个ClassWriter
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);

        // 创建MyClass2类
        cw.visit(V1_8, ACC_PUBLIC, "MyClass2", null, "java/lang/Object", null);

        // 创建缺省构造方法
        genDefaultConstructor(cw);

        // 创建foo方法
        genFooMethod(cw);

        // 结束类
        cw.visitEnd();

        byte[] data = cw.toByteArray();
        File file = new File("MyClass2.class");
        FileOutputStream fos = new FileOutputStream(file);
        fos.write(data);
        fos.close();

        return data;

    }

    //创建缺省构造方法
    private static void genDefaultConstructor(ClassWriter cw){
        MethodVisitor constructor = cw.visitMethod(ACC_PUBLIC, "<init>",
                "()V", null, null);

        constructor.visitCode();
        constructor.visitVarInsn(ALOAD, 0);
        constructor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
        constructor.visitInsn(RETURN);
        constructor.visitMaxs(1,1);
        constructor.visitEnd();
    }

    //创建foo方法
    private static void genFooMethod(ClassWriter cw){
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "foo",
                "(I)I",  //括号中的是参数类型，括号后面的是返回值类型
                null, null);

        //添加参数a
        mv.visitParameter("a", ACC_PUBLIC);

        mv.visitVarInsn(ILOAD, 1);      //iload_1
        mv.visitInsn(ICONST_3);         //iconst_3
        mv.visitInsn(IADD);             //iadd
        mv.visitInsn(IRETURN);          //ireturn

        //设置操作数栈最大的帧数，以及最大的本地变量数
        mv.visitMaxs(2,2);

        //结束方法
        mv.visitEnd();
    }
}
```

扩展阅读：

> 字节码增强之ASM、JavaAssist、Agent、Instrumentation: <https://blog.csdn.net/hosaos/article/details/102931887>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://jun-wang.gitbook.io/learnjava/ji-shu-xue-xi/jvm-xue-xi/asm-zi-jie-ma-zeng-qiang-ji-shu.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
