除非去写框架否则不太可能用到asm ASM is a Java bytecode manipulation framework. It can be used to dynamically generate stub classes or other proxy classes, directly in binary form, or to dynamically modify classes at load time, i.e., just before they are loaded into the Java Virtual Machine. java的汇编语言—->字节码文件.class 首先看官方网站、官方文档……(挑重要的看) 读最原始的那份文档,再去找别人写的资料 官网:asm官网
字节码文件的结构(10个部分)
- 《java虚拟机规范》(oracle官网,纸质书没那么快)(可中英对照)
- 做jvm的要看
- 阿里的龙井虚拟机(公开,可以用)
- 华为应该有
- IDEA中将光标放在.java文件中,View下的show bytecode查看字节码文件
- jclasslib viewer插件===>idea
asm可以按AST的规则读字节码也可以动态生成字节码
- 一个class文件是由不同的Node构成的
-
读字节码来解析class文件
ClassVisitor类
- ClassReader是一个visitor
- 给出class文件,可以反编译出来
- 作为一个visitor需要visit到每一个需要visit的部分(节点)(不同的部分)
- 相当于从内部进攻,visitor作为参数传进accept方法,让visitor在内部读取或者处理需要visit的那个类
T1.java
```java package com.mashibing.dp.ASM;
/**
- 光标必须位于类体内,View-Show ByteCode */
public class T1 { int i = 0; public void m() { int j=1; }
}
<a name="cor39"></a>
#### ClassPrinter类
```java
package com.mashibing.dp.ASM;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import java.io.IOException;
import static org.objectweb.asm.Opcodes.ASM4;
public class ClassPrinter extends ClassVisitor {
public ClassPrinter() {
super(ASM4);
}
// 只需要实现需要遍历的节点就行了
// 这里只实现了四个
// 访问version、access等最前面的内容(见参数)的时候
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
//继承信息
System.out.println(name + " extends " + superName + "{" );
}
// 访问属性的时候
@Override
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
System.out.println(" " + name);
return null;
}
// 访问方法的时候
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
System.out.println(" " + name + "()");
return null;
}
// 访问结束了的时候
@Override
public void visitEnd() {
System.out.println("}");
}
public static void main(String[] args) throws IOException {
ClassPrinter cp = new ClassPrinter();
// ClassReader也是一个visitor
ClassReader cr = new ClassReader("java.lang.Runnable");
// 读取T1.java生成的字节码文件(详见gitlab项目结构)
//ClassReader cr = new ClassReader(
// ClassPrinter.class.getClassLoader().getResourceAsStream("com/mashibing/dp/ASM/T1.class"));
cr.accept(cp, 0);
}
}
直接生成class文件而不需要源码
- ClassWriter生成字节码文件,不需要源代码!
- 不是动态生成代码而是生成字节码!
MyClassLoader类(自定义类加载器)
package com.mashibing.dp.ASM;
class MyClassLoader extends ClassLoader {
// 重写defineClass方法
// 参数1为类的名字
// 参数2为类的字节数组(类定义的字节数组)
public Class defineClass(String name, byte[] b) {
// 参数3、4分别为开始和结束的位置?
return defineClass(name, b, 0, b.length);
}
}
ClassWriteTest类
package com.mashibing.dp.ASM;
import org.objectweb.asm.ClassWriter;
import static org.objectweb.asm.Opcodes.*;
public class ClassWriteTest {
public static void main(String[] args) {
// 表示生成一个干净的writer
// ClassWriter是一个visitor
ClassWriter cw = new ClassWriter(0);
// cw为访问者,下面的第一个null为父接口
cw.visit(V1_5, ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE,
"pkg/Comparable", null, "java/lang/Object",
null);
// 写属性,返回值是FieldVisitor
// visitEnd()表示写入结束了
cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "LESS", "I",
null, -1).visitEnd();
cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "EQUAL", "I",
null, 0).visitEnd();
cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "GREATER", "I",
null, 1).visitEnd();
// 括号中的Ljava/lang/Object;是参数类型;后面的I是返回值类型
// 括号里的是参数类型
cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "compareTo",
"(Ljava/lang/Object;)I", null, null).visitEnd();
cw.visitEnd();
// 转换为字节数组
byte[] b = cw.toByteArray();
// 自定义的类加载器
MyClassLoader myClassLoader = new MyClassLoader();
// 第一个参数为包
Class c = myClassLoader.defineClass("pkg.Comparable", b);
// 加载进来的是类类型===>反射
System.out.println(c.getMethods()[0].getName());
}
}
asm生成动态代理
asm生成动态代理===>Transforming classes
- ClassWriter是一个visitor,ClassReader也是一个visitor
- 都是visitor,visit来visit去会形成一个链条===>chainOfResponsibility
- 传播的链条(中间是自定义的)
- 中间自定义的visit了reader之后再传给writer
- 中间可以自己定制(详见代码)
public class Tank { public void move(){ System.out.println(“Tank Moving ClaClaCla …”); } }
<a name="qQ7oy"></a>
#### TimeProxy类(将其中的before方法织入move)
```java
package com.mashibing.dp.ASM;
public class TimeProxy {
public static void before() {
System.out.println("before ...");
}
}
ClassTransformerTest类
package com.mashibing.dp.ASM;
import org.objectweb.asm.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import static org.objectweb.asm.Opcodes.*;
public class ClassTransformerTest {
public static void main(String[] args) throws Exception {
ClassReader cr = new ClassReader(
ClassPrinter.class.getClassLoader().getResourceAsStream("com/mashibing/dp/ASM/Tank.class"));
ClassWriter cw = new ClassWriter(0);
// 自己定义一个ClassVisitor并将writer传了进去===>形成了一个链条
ClassVisitor cv = new ClassVisitor(ASM4, cw) {
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
// reader自动给参数赋值?
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
//return mv;
// visitor套visitor
return new MethodVisitor(ASM4, mv) {
// 访问方法刚开始;还有访问方法其他时刻……
// 等等等,很多地方,很多visitor
@Override
public void visitCode() {
// instruction===>指令(用法说明; 操作指南; 指示; 命令; 吩咐; (计算机的)指令)
// 加了一条自己定义(?)的二进制,相当于java的汇编
// 静态调用,调用了com/mashibing/dp/ASM/TimeProxy的before方法
// 后两个参数固定:第三个参数是传参和返回值,第四个是?
visitMethodInsn(INVOKESTATIC, "com/mashibing/dp/ASM/TimeProxy","before", "()V", false);
super.visitCode();
}
};
}
};
// 链条===>cr接受了cv,cv里面传进来了cw
// 相当于classWriter visit了 classReader,将读的东西写进新的字节码了
cr.accept(cv, 0);
byte[] b2 = cw.toByteArray();
MyClassLoader cl = new MyClassLoader();
//Class c = cl.loadClass("com.mashibing.dp.ASM.Tank");
cl.loadClass("com.mashibing.dp.ASM.TimeProxy");
Class c2 = cl.defineClass("com.mashibing.dp.ASM.Tank", b2);
c2.getConstructor().newInstance();
String path = (String)System.getProperties().get("user.dir");
File f = new File(path + "/com/mashibing/dp/ASM/");
f.mkdirs();
FileOutputStream fos = new FileOutputStream(new File(path + "/com/mashibing/dp/ASM/Tank_0.class"));
fos.write(b2);
fos.flush();
fos.close();
}
}
- 上述代码存在的问题:没有区分是什么方法,应该只有move加brfore,结果所有方法都加了before,可以用方法名字或者别的方法来区分
随想
- C语言中可以直接写汇编语言—->关键字asm{ }
- AsSeMbly language汇编语言
int k = 0; k = k++; 为什么结果为0?--->看字节码