ASMSupport教程3:动态生成接口

准备


在阅读主要内容之前我们将首先介绍下ASMSupport的一个特有类jw.asmsupport.clazz.AClass。

ASMSupport中很多地方我们都会用到一个类jw.asmsupport.clazz.AClass,这是ASMSupport对Class的一个特有封装。我们可以看到它的层级结构是这样的:

  jw.asmsupport.clazz.AClass  
      ----jw.asmsupport.clazz.ArrayClass
      ----NewMemberClass
          ----jw.asmsupport.clazz.ProductClass
          ----jw.asmsupport.clazz.SemiClass

AClass有两个直接的子类: 1.ArrayClass,2.NewMemberClass

ArrayClass表示的是一个数组类型Class,NewMemberClass则表示可以对其修改的class或者我们新创建的class。

而NewMemberClass又有两个子类:

  • ProductClass:这个表示的是在我们程序在运行是内存中已经存在的Class封裝
  • SemiClass:而这个则是表示我们动态需要创建或正在创建的Class的封装。

那么如何去获取这些AClass呢。对于ArrayClass和ProductClass我们可以通过AClassFactory的如下静态方法获得:

<!-- lang: java -->
public static AClass getProductClass(Class<?> cls):  这个方法我们将传递一个Class类型的参数,然后将会返回AClass,这个方法的参数可以是数组类型的比如String[].class,如果是数组类型的Class将返回一个ArrayClass,否则返回一个ProductClass。这个方法也是使用最多的方法.  

接下来是三个重载的getArrayClass方法,都是获取ArrayClass的。

<!-- lang: java -->
public static ArrayClass getArrayClass(Class<?> arrayCls):  这个方法和getProductClass方法类似,只不过这里的参数必须是一个数组类型Class,否则会抛出异常,返回的是ArrayClass  

<!-- lang: java -->
public static ArrayClass getArrayClass(Class<?> cls, int dim):  这个方法有两个参数第一个是数组类型基本类型cls,第二个是数组的维度。这里的cls可以是一个数组类型的Class,也可以是一个非数组类型。比如:    
1.getArrayClass(String.class, 2);将会得到一个String[][].class的ArrayClass,等同于调用getProductClass(String[][].class);  
2.getArrayClass(String[].class, 2);将会得到一个String[][][].class的ArrayClass,等同于调用getProductClass(String[][][].class);  

<!-- lang: java -->
public static ArrayClass getArrayClass(AClass cls, int dim):这个方法和上面的getArrayClass(Class<?> cls, int dim)类似,但是第一个参数是一个AClass类型的,并且不能是一个数组类型,也就是说cls不能是ArrayClass类型。比如:getArrayClass(AClass.STRING_ACLASS, 1);将获得String[].class的ArrayClass,等同于调用getProductClass(String[].class);  

当然一些常用的AClass可以直接通过AClass的常量获得比如AClass.STRING_ACLASS获得的是String类型的AClass.我们预先设置了如下的AClass:

/** java.lang.Boolean of AClass */
public static final AClass BOOLEAN_WRAP_ACLASS = AClassFactory.getProductClass(Boolean.class);

/** java.lang.Byte of AClass */
public static final AClass BYTE_WRAP_ACLASS = AClassFactory.getProductClass(Byte.class);

/** java.lang.Short of AClass */
public static final AClass SHORT_WRAP_ACLASS = AClassFactory.getProductClass(Short.class);

/** java.lang.Character of AClass */
public static final AClass CHARACTER_WRAP_ACLASS = AClassFactory.getProductClass(Character.class);

/** java.lang.Integer of AClass */
public static final AClass INTEGER_WRAP_ACLASS = AClassFactory.getProductClass(Integer.class);

/** java.lang.Long of AClass */
public static final AClass LONG_WRAP_ACLASS = AClassFactory.getProductClass(Long.class);

/** java.lang.Float of AClass */
public static final AClass FLOAT_WRAP_ACLASS = AClassFactory.getProductClass(Float.class);

/** java.lang.Double of AClass */
public static final AClass DOUBLE_WRAP_ACLASS = AClassFactory.getProductClass(Double.class);

/** boolean of AClass */
public static final AClass BOOLEAN_ACLASS = AClassFactory.getProductClass(boolean.class);

/** byte of AClass */
public static final AClass BYTE_ACLASS = AClassFactory.getProductClass(byte.class);

/** short of AClass */
public static final AClass SHORT_ACLASS = AClassFactory.getProductClass(short.class);

/** char of AClass */
public static final AClass CHAR_ACLASS = AClassFactory.getProductClass(char.class);

/** int of AClass */
public static final AClass INT_ACLASS = AClassFactory.getProductClass(int.class);

/** long of AClass */
public static final AClass LONG_ACLASS = AClassFactory.getProductClass(long.class);

/** float of AClass */
public static final AClass FLOAT_ACLASS = AClassFactory.getProductClass(float.class);

/** double of AClass */
public static final AClass DOUBLE_ACLASS = AClassFactory.getProductClass(double.class);

/** java.lang.Object of AClass */
public static final AClass OBJECT_ACLASS = AClassFactory.getProductClass(Object.class);

/** java.lang.Cloneable of AClass */
public static final AClass CLONEABLE_ACLASS = AClassFactory.getProductClass(Cloneable.class);

/** java.lang.Serializable of AClass */
public static final AClass SERIALIZABLE_ACLASS = AClassFactory.getProductClass(Serializable.class);

/** java.lang.String of AClass */
public static final AClass STRING_ACLASS = AClassFactory.getProductClass(String.class);

/** java.util.Iterator of AClass */
public static final AClass ITERATOR_ACLASS = AClassFactory.getProductClass(Iterator.class);

/** java.lang.Exception of AClass */
public static final AClass EXCEPTION_ACLASS = AClassFactory.getProductClass(Exception.class);

/** java.lang.Class of AClass */
public static final AClass CLASS_ACLASS = AClassFactory.getProductClass(Class.class);

/** java.lang.Throwable of AClass */
public static final AClass THROWABLE_ACLASS = AClassFactory.getProductClass(Throwable.class);

/** java.lang.Void of AClass */
public static final AClass VOID_ACLASS = AClassFactory.getProductClass(void.class);

如何获得SemiClass呢。由于SemiClass表示正在创建的Class,所以我们只能通過ProgramBlock的**getMethodOwner()**获得。通过这个方法我们将会得到我们正在创建或者正在修改的Class。也就是说当我们正在修改一个Class的时候获得的是一个ProductClass。正在创建Class的时候获得的将是SemiClass.

总结:

  • AClassFactory.getProductClass(Class<?> cls)获取ProductClass或者ArrayClass
  • AClassFactory.getArrayClass(参数)获取ArrayClass
  • ProgramBlock.getMethodOwner()获取ProductClass或者SemiClass

说了这么多下面该进入正题了!

创建接口

在前面的文章中我们创建了个AbstractExample,这里我们依旧创建一个Class继承自它: 具体代码如下:

<!-- lang: java -->
package example.create;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

import jw.asmsupport.block.method.cinit.CInitBody;
import jw.asmsupport.clazz.AClass;
import jw.asmsupport.creator.InterfaceCreator;
import jw.asmsupport.definition.value.Value;

import org.objectweb.asm.Opcodes;

import example.AbstractExample;

public class CreateInterface extends AbstractExample {

    public static void main(String[] args) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {

        InterfaceCreator interfaceCreator = new InterfaceCreator(Opcodes.V1_6, "generated.create.CreateInterfaceExample", null);
	
        interfaceCreator.createMethod("test", new AClass[]{AClass.STRING_ACLASS, AClass.INT_ACLASS}, AClass.BOOLEAN_ACLASS, null);
	
        interfaceCreator.createGlobalVariable("globalValue", AClass.STRING_ACLASS);

        interfaceCreator.createStaticBlock(new CInitBody(){

            @Override
            public void generateBody() {
                
                	assign(getMethodOwner().getGlobalVariable("globalValue"), Value.value("I'm a global variable at Interface"));

                       invoke(systemOut, "println", Value.value("I'm in static block at interface"));
                       	runReturn();
                        }
		
            });
        Class inter = generate(interfaceCreator);
        Field globalValue = inter.getField("globalValue");
        System.out.println(globalValue + " : value is " + globalValue.get(inter));
        System.out.println(inter);
        }
    
    }

下面我们来解释下上面的代码: 首先这段代码的作用就是:
1.创建名为"example.generated.CreateInterfaceExample"的一个接口。
2.在这个接口里声明一个名为"test"方法。
3.在接口中创建一个globalValue的全局变量。
4创建一个static语句块。在这个语句块中给globalValue赋值并且答应一段话。

下面我们看下逐行解释:


<!-- lang: java -->
InterfaceCreator interfaceCreator = new InterfaceCreator(Opcodes.V1_6, "generated.create.CreateInterfaceExample", null); 

创建接口我们采用 InterfaceCreator类,该类的构造方法包含了三个参数:
1.接口的JDK版本,可以用过org.objectweb.asm.Opcodes获取,比如:Opcodes.V1_5表示1.5版本的jdk。
2.接口的全路径名
3.接口所继承哪些接口,是一个Class的数组。


<!-- lang: java -->
interfaceCreator.createMethod("test", new AClass[]{AClass.STRING_ACLASS, AClass.INT_ACLASS}, AClass.BOOLEAN_ACLASS, null);

通过createMethod放发我们可以向接口中创建方法


<!-- lang: java -->
interfaceCreator.createGlobalVariable("globalValue", AClass.STRING_ACLASS);

通过createGlobalVariable创建局部变量,当然这个变量的修饰符是public static final的,需要注意的一点是,我们不能像java代码里一样在声明变量的同时给变量赋值,因为这个变量是static的,所以我们只能在下面的代码createStaticBlock中对其赋值。事实上,如果你在编写java代码的时候在接口中申明了一个变量,java的编译器其实也会将你代码中的赋值部分解释出来,再将这一部分赋值的字节码放到static块中。对于非static类型的全局变量如何申明和赋值将在以后文章中有详细的解释。这段代码对于的生成的java代码如下:
public static final String globalValue;


<!-- lang: java -->
interfaceCreator.createStaticBlock(new CInitBody(){

通过createStaticBlock创建static块。 对应的java代码如下:

static{  
    globalValue = "I'm in static block at interface";//这一段在通过jd-gui反编译后将不会显示在static块中,将会显示为在声明变量的时候赋值操作(public static final String globalValue = "I'm a global variable at Interface";)  
    System.out.println("I'm in static block at interface");  
}

这里区别于java代码。在用java代码编写接口的时候,是不允许编写static程序块的。但是在这里你可以创建它,它的作用和Class中的static程序块是相同的,都是在Class被装载的时候执行,且执行一次。 你可以在这个程序块中编写任何代码。

在下面的CInitBody重写的方法generateBody代码中,这里首先将"I'm a global variable at Interface"字符串赋值给我们上面申明的变量。然后在打印"I'm in static block at interface". 这里我们发现有一个runReturn()方法。这个方法是生成return语句。这里为什么要这么写。首先要明确个概念,对于JVM来说,或者说在字节码的层面上来讲,静态语句块其实是特殊一个静态方法,它的名字叫做"<cinit>",返回类型是void,我们用java代码来描述就是static void <cinit>(),所以我们需要执行return操作。当然在我们编写java代码的时候,对于void返回类型的方法我们是不需要显式的写return的,但是在使用asmsupport的时候,我们需要显式的执行一次runReturn().事实上在你编写java代码的时候,对于void的方法,如果你不写return,java编译器会自动在你所写方法的最后自动加上一条return指令。

注意:createStaticBlock我们只能调用一次,因为static块实际上就是一个方法,根据方法的重载很容易理解,我们不可能创建多个static void <cinit>()方法。虽然我们在编写java代码的时候可以写多个static块在同一个类中,但是java编译器到最后会将所有写在static块中的代码归并到<cinit>方法当中去。


<!-- lang: java -->
assign(getMethodOwner().getGlobalVariable("globalValue"), Value.value("I'm a global variable at Interface"));

这句话是将"I'm a global variable at Interface"赋值给上面我们创建的globalValue全局变量对应的java代码可以理解为:
example.generated.CreateInterfaceExample.globalValue="I'm a global variable at Interface";
这里要记住一点,所有静态变量的获取都是需要通过AClass,这个AClass是你所需要获取变量的宿主Class。getMethodOwner()是获取当前方法的宿主class,就是你当前创建或者正在修改的Class。比如我们最常用到的System.out对因的asmsupport代码就是:
AClassFactory.getProductClass(System.class).getGlobalVariable("out");
具体如何使用全局变量在以后的文章中有详细的解释。


<!-- lang: java -->
invoke(systemOut, "println", Value.value("I'm in static block at interface"));

这段代码是调用println方法,具体如何调用方法在以后的文章中有详细的解释。

运行代码


执行上面的类,我将就可以得到我们的Class文件了。通过jd-gui反编译可以看到我们的代码内容如下:

<!-- lang: java -->
package generated.create;

import java.io.PrintStream;

public abstract interface CreateInterfaceExample
{
    public static final String globalValue = "I'm a global variable at Interface";
    
    static
    {
        System.out.println("I'm in static block at interface");
    }

     public abstract boolean test(String paramString, int paramInt);
}

资源

ASMSupport源码地址:https://amssupport.googlecode.com/svn/trunk/

ASMSupport实例地址:http://amssupport.googlecode.com/svn/trunk/asmsupport/src/test/java/example/

本文示例地址: AbstractExample: http://amssupport.googlecode.com/svn/trunk/asmsupport/src/test/java/example/AbstractExample.java CreateInterface: http://amssupport.googlecode.com/svn/trunk/asmsupport/src/test/java/example/create/CreateInterface.java

以上代码均可通过svn下载

最新asmsupport下载地址:https://amssupport.googlecode.com/files/asmsupport-0.2-alpha-2013-03-25.jar

原文地址:http://my.oschina.net/wensiqun/blog/117708

转载于:https://my.oschina.net/wensiqun/blog/117708

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值