准备
在阅读主要内容之前我们将首先介绍下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又有两个子类:
那么如何去获取这些AClass呢。对于ArrayClass和ProductClass我们可以通过AClassFactory的如下静态方法获得:
一
public static AClass getProductClass(Class<?> cls): 这个方法我们将传递一个Class类型的参数,然后将会返回AClass,这个方法的参数可以是数组类型的比如String[].class,如果是数组类型的Class将返回一个ArrayClass,否则返回一个ProductClass。这个方法也是使用最多的方法.
接下来是三个重载的getArrayClass方法,都是获取ArrayClass的。
二
public static ArrayClass getArrayClass(Class<?> arrayCls): 这个方法和getProductClass方法类似,只不过这里的参数必须是一个数组类型Class,否则会抛出异常,返回的是ArrayClass
三
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);
四
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.
总结:
说了这么多下面该进入正题了!
在前面的文章中我们创建了个AbstractExample,这里我们依旧创建一个Class继承自它:
具体代码如下:
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赋值并且答应一段话。
下面我们看下逐行解释:
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的数组。
interfaceCreator.createMethod("test", new AClass[]{AClass.STRING_ACLASS, AClass.INT_ACLASS}, AClass.BOOLEAN_ACLASS, null);
通过createMethod放发我们可以向接口中创建方法
interfaceCreator.createGlobalVariable("globalValue", AClass.STRING_ACLASS);
通过createGlobalVariable创建局部变量,当然这个变量的修饰符是public static final的,需要注意的一点是,我们不能像java代码里一样在声明变量的同时给变量赋值,因为这个变量是static的,所以我们只能在下面的代码createStaticBlock中对其赋值。事实上,如果你在编写java代码的时候在接口中申明了一个变量,java的编译器其实也会将你代码中的赋值部分解释出来,再将这一部分赋值的字节码放到static块中。对于非static类型的全局变量如何申明和赋值将在以后文章中有详细的解释。这段代码对于的生成的java代码如下:
public static final String globalValue;
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来说,或者说在字节码的层面上来讲,静态语句块其实是特殊一个静态方法,它的名字叫做” “,返回类型是void,我们用java代码来描述就是static void (),所以我们需要执行return操作。当然在我们编写java代码的时候,对于void返回类型的方法我们是不需要显式的写return的,但是在使用asmsupport的时候,我们需要显式的执行一次runReturn().事实上在你编写java代码的时候,对于void的方法,如果你不写return,java编译器会自动在你所写方法的最后自动加上一条return指令。
注意:createStaticBlock我们只能调用一次,因为static块实际上就是一个方法,根据方法的重载很容易理解,我们不可能创建多个static void ()方法。虽然我们在编写java代码的时候可以写多个static块在同一个类中,但是java编译器到最后会将所有写在static块中的代码归并到 方法当中去。
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”);
具体如何使用全局变量在以后的文章中有详细的解释。
invoke(systemOut, "println", Value.value("I'm in static block at interface"));
这段代码是调用println方法,具体如何调用方法在以后的文章中有详细的解释。
运行代码
执行上面的类,我将就可以得到我们的Class文件了。通过jd-gui反编译可以看到我们的代码内容如下:
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