Java字節(jié)碼操作之ASM創(chuàng)建class

學(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ū)留言!

下一篇:Java字節(jié)碼操作之ASM修改class

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載咐熙,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者弱恒。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市棋恼,隨后出現(xiàn)的幾起案子返弹,更是在濱河造成了極大的恐慌,老刑警劉巖爪飘,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件义起,死亡現(xiàn)場離奇詭異,居然都是意外死亡师崎,警方通過查閱死者的電腦和手機默终,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來犁罩,“玉大人齐蔽,你說我怎么就攤上這事〈补溃” “怎么了肴熏?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長顷窒。 經(jīng)常有香客問我,道長源哩,這世上最難降的妖魔是什么鞋吉? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮励烦,結(jié)果婚禮上谓着,老公的妹妹穿的比我還像新娘。我一直安慰自己坛掠,他們只是感情好赊锚,可當(dāng)我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著屉栓,像睡著了一般舷蒲。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上友多,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天牲平,我揣著相機與錄音,去河邊找鬼域滥。 笑死纵柿,一個胖子當(dāng)著我的面吹牛蜈抓,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播昂儒,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼沟使,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了渊跋?” 一聲冷哼從身側(cè)響起腊嗡,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎刹枉,沒想到半個月后叽唱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡微宝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年棺亭,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蟋软。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡镶摘,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出岳守,到底是詐尸還是另有隱情凄敢,我是刑警寧澤,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布湿痢,位于F島的核電站涝缝,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏譬重。R本人自食惡果不足惜拒逮,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望臀规。 院中可真熱鬧滩援,春花似錦、人聲如沸塔嬉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽谨究。三九已至恩袱,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間胶哲,已是汗流浹背憎蛤。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人俩檬。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓萎胰,卻偏偏與公主長得像,于是被迫代替她去往敵國和親棚辽。 傳聞我的和親對象是個殘疾皇子技竟,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,612評論 2 350

推薦閱讀更多精彩內(nèi)容