學(xué)習(xí)條件:學(xué)習(xí)Java ASM之前你必須對JVM指令有一定的了解鲜侥,理解棧楨筛璧、操作數(shù)棧朝刊、局部變量表的概念,必須能看必要的JVM,所以建議先學(xué)習(xí)JVM指令!
一其障、ASM可以做什么矿瘦?
簡單的說Java ASM是一個非常底層的字節(jié)碼操作框架,它可以創(chuàng)建一個全新的Java class文件也可以修改現(xiàn)有的class文件對象蝎抽,是Java開發(fā)中非常核心的技術(shù)點政钟。spring、Mybatis樟结、Hibernate等現(xiàn)在Java中的框架底層都是用動態(tài)代理實現(xiàn)的核心功能养交,而動態(tài)代理的核心技術(shù)點就是ASM,所以說沒有ASM也就沒有這些框架瓢宦!
二碎连、如何使用ASM創(chuàng)建一個class文件對象呢?
目標(biāo):創(chuàng)建如下所示的一個class
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.zjw;
import java.util.ArrayList;
public class HelloAsm {
private String name;
private long age;
public static long num;
public static final String ADMIN_NAME = "趙進偉";
public HelloAsm() {
}
public int method(String var1) {
System.out.println("method ing");
ArrayList var2 = new ArrayList();
StringBuilder var3 = new StringBuilder();
var3.append("Hello Java Asm StringBuilder!");
long var4 = System.nanoTime();
return 10 + 11;
}
public static void staticMethod(String var0) {
System.out.println("Hello Java Asm!");
}
}
1驮履、創(chuàng)建ClassWriter對象鱼辙,定義一個你所創(chuàng)建class的全限定名、父類玫镐、權(quán)限等
//定義一個叫做HelloAsm的類
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
cw.visit(V1_8, ACC_PUBLIC, "com/zjw/HelloAsm", null, "java/lang/Object", null);
2倒戏、生成HelloAsm默認(rèn)的構(gòu)造方法
//生成默認(rèn)的構(gòu)造方法
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
//生成構(gòu)造方法的字節(jié)碼指令
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
3、生成成員變量
//生成成員變量
FieldVisitor fv = cw.visitField(ACC_PRIVATE, "name", "Ljava/lang/String;", null, null);
fv.visitEnd();
fv = cw.visitField(ACC_PRIVATE, "age", "J", null, null);
fv.visitEnd();
4恐似、生成靜成員變量
//生成靜成員變量
fv = cw.visitField(ACC_PUBLIC + ACC_STATIC, "num", "J", null, 101L);
fv.visitEnd();
5杜跷、生成常量
//生成常量
fv = cw.visitField(ACC_PUBLIC + ACC_STATIC + ACC_FINAL, "ADMIN_NAME", "Ljava/lang/String;", null, "趙進偉");
fv.visitEnd();
6、生成成員方法
//生成成員方法
mv = cw.visitMethod(ACC_PUBLIC, "method", "(Ljava/lang/String;)I", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
7矫夷、調(diào)用靜態(tài)方法
mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mv.visitLdcInsn("method ing");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
8葛闷、創(chuàng)建局部變量
LocalVariablesSorter lvs = new LocalVariablesSorter(ACC_PUBLIC, "(Ljava/lang/String;)I", mv);
//創(chuàng)建ArrayList對象
//new ArrayList,分配內(nèi)存但不做初始化操作
mv.visitTypeInsn(NEW, "java/util/ArrayList");
//復(fù)制棧里元素(對象地址),再次壓入
mv.visitInsn(DUP);
//彈出一個對象所在地址双藕,進行初始化操作淑趾,構(gòu)造函數(shù)默認(rèn)為空,此時棧大小為1
mv.visitMethodInsn(INVOKESPECIAL, "java/util/ArrayList", "<init>", "()V", false);
int time = lvs.newLocal(Type.getType("java/util/List"));
mv.visitVarInsn(ASTORE, time);
mv.visitVarInsn(ALOAD, time);
//創(chuàng)建StringBuilder對象
mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);
time = lvs.newLocal(Type.getType("java/lang/StringBuilder"));
mv.visitVarInsn(ASTORE, time);
mv.visitVarInsn(ALOAD, time);
9忧陪、調(diào)用對象方法
//調(diào)用StringBuilder的append方法
mv.visitLdcInsn("Hello Java Asm StringBuilder!");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
10治笨、加法運算
//調(diào)用靜態(tài)方法
mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "nanoTime", "()J", false);
time = lvs.newLocal(Type.LONG_TYPE);
mv.visitVarInsn(LSTORE, time);
mv.visitLdcInsn(10);
mv.visitLdcInsn(11);
//加運算10+11
mv.visitInsn(IADD);
mv.visitInsn(IRETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
11、生成靜態(tài)方法
//生成靜態(tài)方法
mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "staticMethod", "(Ljava/lang/String;)V", null, null);
//生成靜態(tài)方法中的字節(jié)碼指令
mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mv.visitLdcInsn("Hello Java Asm!");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
12赤嚼、獲取生成的class文件對應(yīng)的二進制byte數(shù)組
// 獲取生成的class文件對應(yīng)的二進制
byte[] code = cw.toByteArray();
13旷赖、將二進制寫到本地磁盤上
//將二進制寫到本地磁盤上
FileOutputStream fos = null;
try {
fos = new FileOutputStream("HelloAsm.class");
fos.write(code);
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
14、直接將二進制流加載到內(nèi)存中
//直接將二進制流加載到內(nèi)存中 name可以傳入null
/*注意:defineClass是ClassLoader的protected方法<本類更卒、子類等孵、同包類中才可以調(diào)用>,所以自己想辦法吧*/
Class<?> clazz=defineClass("com.zjw.HelloAsm", code, 0, code.length);
到此ASM創(chuàng)建一個class就完成了蹂空!
作者整理資料也是查閱了N篇文章俯萌,對你有幫助請點個贊吧果录!
需要作者項目源碼歡迎在評論區(qū)留言!