我们知道Java是静态语言,而python、ruby是动态语言,Java程序一旦写好很难在运行时更改类的行为,而python、ruby可以。
不过基于bytecode层面上我们可以做一些手脚,来使Java程序多一些灵活性和Magic,ASM就是这样一个应用广泛的开源库。
ASMisaJavabytecodemanipulationframework.Itcanbeusedtodynamicallygeneratestubclassesorotherproxyclasses,
directlyinbinaryform,ortodynamicallymodifyclassesatloadtime,i.e.,justbeforetheyareloadedintotheJavaVirtualMachine.
ASM完成了BCEL和SERP同样的功能,但ASM
只有30多k,而后两者分别是350k和150k。apache真是越来越过气了。
让我们来看一个ASM的简单例子Helloworld.java,它生成一个Example类和一个main方法,main方法打印"Helloworld!"语句:
Java代码
1.importjava.io.FileOutputStream;
2.importjava.io.PrintStream;
3.importorg.objectweb.asm.ClassWriter;
4.importorg.objectweb.asm.MethodVisitor;
5.importorg.objectweb.asm.Opcodes;
6.importorg.objectweb.asm.Type;
7.importorg.objectweb.asm.commons.GeneratorAdapter;
8.importorg.objectweb.asm.commons.Method;
9.
10.publicclassHelloworldextendsClassLoaderimplementsOpcodes{
11.
12.public staticvoid main(finalStringargs[])throwsException{
13.
14.//createsaClassWriterfortheExamplepublicclass,
15.//whichinheritsfromObject
16.
17.ClassWriter cw = newClassWriter(0);
18.cw.visit(V1_1,ACC_PUBLIC,"Example",null,"java/lang/Object",null);
19.MethodVisitormw=cw.visitMethod(ACC_PUBLIC,"<init>","()V",null,
20.null);
21.mw.visitVarInsn(ALOAD,0);
22.mw.visitMethodInsn(INVOKESPECIAL,"java/lang/Object","<init>","()V");
23.mw.visitInsn(RETURN);
24.mw.visitMaxs(1,1);
25.mw.visitEnd();
26.mw=cw.visitMethod(ACC_PUBLIC+ACC_STATIC,"main",
27."([Ljava/lang/String;)V",null,null);
28.mw.visitFieldInsn(GETSTATIC,"java/lang/System","out",
29."Ljava/io/PrintStream;");
30.mw.visitLdcInsn("Helloworld!");
31.mw.visitMethodInsn(INVOKEVIRTUAL,"java/io/PrintStream","println",
32."(Ljava/lang/String;)V");
33.mw.visitInsn(RETURN);
34.mw.visitMaxs(2,2);
35.mw.visitEnd();
36.byte[]code=cw.toByteArray();
37.FileOutputStreamfos=newFileOutputStream("Example.class");
38.fos.write(code);
39.fos.close();
40.Helloworldloader=newHelloworld();
41.ClassexampleClass=loader
42..defineClass("Example",code,0,code.length);
43.exampleClass.getMethods()[0].invoke(null,newObject[]{null});
44.//------------------------------------------------------------------------
45.//SameexamplewithaGeneratorAdapter(moreconvenientbutslower)
46.//------------------------------------------------------------------------
47.
48.cw=newClassWriter(ClassWriter.COMPUTE_MAXS);
49.cw.visit(V1_1,ACC_PUBLIC,"Example",null,"java/lang/Object",null);
50.Methodm=Method.getMethod("void<init>()");
51.GeneratorAdaptermg=newGeneratorAdapter(ACC_PUBLIC,m,null,null,
52.cw);
53.mg.loadThis();
54.mg.invokeConstructor(Type.getType(Object.class),m);
55.mg.returnValue();
56.mg.endMethod();
57.m=Method.getMethod("voidmain(String[])");
58.mg=newGeneratorAdapter(ACC_PUBLIC+ACC_STATIC,m,null,null,cw);
59.mg.getStatic(Type.getType(System.class),"out",Type
60..getType(PrintStream.class));
61.mg.push("Helloworld!");
62.mg.invokeVirtual(Type.getType(PrintStream.class),Method
63..getMethod("voidprintln(String)"));
64.mg.returnValue();
65.mg.endMethod();
66.cw.visitEnd();
67.code=cw.toByteArray();
68.loader=newHelloworld();
69.exampleClass=loader.defineClass("Example",code,0,code.length);
70.exampleClass.getMethods()[0].invoke(null,newObject[]{null});
71.}
72.}
我们看到上面的例子分别使用ASM的MethodVisitor和GeneratorAdapter两种方式来动态生成Example类并调用打印语句。