ASMSupport教程2動態生成類

上一篇文章中我們介紹瞭如何生成接口,這次我們將編寫如何生成一個Class,首先我們先看下面的代碼:

 

public class CreateClassExample {
    private static String staticGlobalVariable = "I'm a static global variable at class";
    public int globalVariable;

    public CreateClassExample(int intVal) {
        this.globalVariable = intVal;
    }

    private void commonMethod() {
        System.out.println("staticGlobalVariable : " + staticGlobalVariable);
        System.out.println("globalVariable : " + this.globalVariable);
    }

    public static void main(String[] args) {
        new CreateClassExample(1024).commonMethod();
    }
}

 和前面的例子一樣我們創建一個帶有main方法的可執行的class

 

 

public class CreateClass extends AbstractExample {
    public static void main(String[] args) throws Exception{
        //asmsupport code
        generate(creator);
    }
}

 我們的生成class的代碼寫在上面的”//asmsupport code”處,具體代碼如下:

 

 

public class CreateClass extends AbstractExample {

    public static void main(String[] args) throws Exception, NoSuchMethodException {

        ClassCreator creator = new ClassCreator(Opcodes.V1_5, Opcodes.ACC_PUBLIC , "generated.create.CreateClassExample", null, null);

        creator.createGlobalVariable("staticGlobalVariable", Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC, AClass.STRING_ACLASS);

        creator.createStaticBlock(new CInitBody(){
            @Override
            public void generateBody() {
                assign(getMethodOwner().getGlobalVariable("staticGlobalVariable"), Value.value("I'm a static global variable at class"));
                runReturn();
            }
        });

        creator.createGlobalVariable("globalVariable", Opcodes.ACC_PUBLIC, AClass.INT_ACLASS);

        creator.createConstructor(new AClass[]{AClassFactory.getProductClass(int.class)}, new String[]{"intVal"}, new InitBody(){

            @Override
            public void generateBody(LocalVariable... argus) {

                invokeSuperConstructor();

                assign(getThis().getGlobalVariable("globalVariable"), argus[0]);
                runReturn();
            }

        }, Opcodes.ACC_PUBLIC);

        creator.createMethod("commonMethod", null, null, null, null, Opcodes.ACC_PRIVATE, new CommonMethodBody(){

            @Override
            public void generateBody(LocalVariable... argus) {
                invoke(systemOut, "println", append(Value.value("staticGlobalVariable : "), getMethodOwner().getGlobalVariable("staticGlobalVariable")));
                invoke(systemOut, "println", append(Value.value("globalVariable : "), getThis().getGlobalVariable("globalVariable")));
                runReturn();
            }

        });

        creator.createStaticMethod("main", new AClass[]{AClassFactory.getProductClass(String[].class)}, new String[]{"args"}, null, null,
                Opcodes.ACC_PUBLIC, new StaticMethodBody(){

            @Override
            public void generateBody(LocalVariable... argus) {
                invoke(invokeConstructor(getMethodOwner(), Value.value(1024)), "commonMethod");
                runReturn();
            }

        });
        generate(creator);
    }

}

 

 

下面我們對每一句代碼進行解釋:

 

ClassCreator creator = new ClassCreator(Opcodes.V1_5, Opcodes.ACC_PUBLIC , "generated.create.CreateClassExample", null, null);

 

 

這代碼的作用就是創建一個Class創建器,通過上面的代碼創建了一個”public class generated.create.CreateClassExample”,jdk版本是1.5的class。構造它需要5個參數,依次是:

  1. jdk version :JDK的版本。可以用過org.objectweb.asm.Opcodes獲取,比如:Opcodes.V1_5表示1.5版本的jdk
  2. class modifiers :表示創建的Class的修飾符。修飾符同樣也是通過org.objectweb.asm.Opcodes獲取,比如修飾符是”public abstract”則是 Opcodes.ACCPUBLIC + Opcodes.ACCABSTRACT
  3. the class full name : Class的全名。比如:asmsupport.oschina.TestClass
  4. super class : 當前創建的class的父類,如果爲null則是繼承自Object.class。參數類型是class
  5. interfaces : 實現了那些接口,可以是null,類型是Class[]

 

creator.createGlobalVariable("staticGlobalVariable", Opcodes.ACCPRIVATE + Opcodes.ACCSTATIC, AClass.STRING_ACLASS);

 這部分是創建一個靜態全局 變量。基本和上篇教程的CreateInterface.java中如何創建和賦值是一樣的,唯一不同的是這時候全局變量的修飾符,和ClassCreator的modifiers類似,例如: Opcodes.ACCPUBLIC + Opcodes.ACCSTATIC。

 

 

creator.createStaticBlock(new CInitBody(){
    @Override
    public void generateBody() {
        assign(getMethodOwner().getGlobalVariable("staticGlobalVariable"), Value.value("I'm a static global variable at class"));
        runReturn();
    }
});

 

 

這裏是創建一個靜態語句塊,在靜態語句塊中爲全局static變量賦值。和上一篇blog中的靜態變量賦值是一樣的。

 

 

creator.createGlobalVariable("globalVariable", Opcodes.ACC_PUBLIC,AClass.INT_ACLASS);

 創建個非靜態的全局變量,我們這裏僅僅值做一個全局變量的聲明。我們並不能在這裏給予它賦值。這裏得區別於我們平時編寫java代碼,其實如果你對java字節碼熟悉的話也能知道,在java字節碼中,全局變量的賦值是在static塊中或者構造方法中實現的,前者是給靜態的全局變量賦值。而後者是給非靜態的變量賦值。我們這裏也一樣。如果是static的全局變量我們將在createStaticBlock中賦值,否則我們在createConstructor中賦值。

 

creator.createConstructor(new AClass[]{AClassFactory.getProductClass(int.class)}, new String[]{"intVal"}, new InitBody(){
    @Override
    public void generateBody(LocalVariable... argus) {
        invokeSuperConstructor();
        assign(getThis().getGlobalVariable("globalVariable"), argus[0]);
        runReturn();
    }
});

 

創建一個構造方法,對於ClassCreator來說。如果沒有創建任何構造方法,也就是沒有調用createConstructor方法,將會自動創建一個無參的默認構造函數。我們這裏創建的構造方法有一個int類型參數,在構造函數中將該參數賦值給我們上面創建的globalVariable。類似生成如下的java代碼:

public CreateClassExample(String intVal){
    this.globalVariable = intVal;
}

 創建一個構造方法,對於ClassCreator來說。如果沒有創建任何構造方法,也就是沒有調用createConstructor方法,將會自動創建一個無參的默認構造函數。我們這裏創建的構造方法有一個int類型參數,在構造函數中將該參數賦值給我們上面創建的globalVariable。類似生成如下的java代碼:

public CreateClassExample(String intVal){
    this.globalVariable = intVal;
}

 

createConstructor方法有四個參數依次是:

  1. 構造方法的所有參數類型的數組
  2. 構造方法所有參數的名稱
  3. 構造參數的方法體
  4. 構造方法的修飾符

這裏當我們調用createConstructor所使用的方法體是InitBody。下面介紹InitBody裏面的內容。

首先我們看下我們在創建匿名類InitBody的時候所重寫的方法:

public void generateBody(LocalVariable... argus) {....}

 

這個方法中的內容就是我們創建的構造方法裏面需要執行的內容了,他有一個變元參數 argus。這裏的參數表示的是我們當前創建的構造方法的參數。這個數組中每個LocalVariable和createConstructor方法的第一個參數中的AClass數組一一對應的。例如這裏argus只有一個元素它的類型是int類型,名字是intVal。

invokeSuperConstructor();

 

這裏要注意的。 要顯示的調用invokeSuperConstructor,他的作用就是等同於我們編寫java代碼的時候調用super()一樣。只不過我們在用java創建個構造函數的時候有時候不需要調用super,是應爲父類的存在沒有參數的構造方法。但這並不代碼jvm沒有去調用super().其實編譯器在編譯java的時候已經將調用super的指令加上到了構造方法的字節碼中去了。這個方法存在一個變元參數arguments。他表示我們調用super()的時候需要傳遞的參數,由於我們創建的class沒有繼承任何類即只繼承了Object類,那麼我們就不需要傳遞參數直接調用invokeSuperConstructor();

assign(getThis().getGlobalVariable("globalVariable"), argus[0]);

 

由於我們創建的globalVariable是非static的。所以通過getThis()獲取globalVariable。getThis()對應於java代碼中就是this關鍵字。

creator.createMethod("commonMethod", null, null, null, null, Opcodes.ACC_PRIVATE, new CommonMethodBody(){

    @Override
    public void generateBody(LocalVariable... argus) {
        invoke(systemOut, "println", append(Value.value("staticGlobalVariable : "), getMethodOwner().getGlobalVariable("staticGlobalVariable")));
        invoke(systemOut, "println", append(Value.value("globalVariable : "), getThis().getGlobalVariable("globalVariable")));
        runReturn();
    }

});

 

創建一個private的commonMethod方法。在commonMethod中將打印出staticGlobalVariable和globalVariable的值。對應的java代碼如下:

private void commonMethod(){
    System.out.println("staticGlobalVariable : " + staticGlobalVariable);
    System.out.println("globalVariable : " + globalVariable);
}

creator.createStaticMethod("main", new AClass[]{AClassFactory.getProductClass(String[].class)}, new String[]{"args"}, null, null,
            Opcodes.ACC_PUBLIC, new StaticMethodBody(){

    @Override
    public void generateBody(LocalVariable... argus) {
        invoke(invokeConstructor(getMethodOwner(), Value.value(1024)), "commonMethod");
        runReturn();
    }
});

 這裏我們創建一個我們經常用到的靜態方法main方法,在這個方法中我們首先new一個我們正在創建的class然後調用它的commonMethod方法。對應的java代碼如下:

public static void main(String[] args){
    new CreateClassExample(1024).commonMethod();
}

 好了運行代碼可以看到控制檯輸出:

staticGlobalVariable : I’m a static global variable at class
globalVariable : 1024

更多ASMSupport教程

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章