三、ASM 我們知道Java是靜態語言,而python、ruby是動態語言,Java程式一旦寫好很難在運行時更改類的行為,而python、ruby可以。 不過基於bytecode層面上我們可以做一些手腳,來使Java程式多一些靈活性和Magic,ASM就是這樣一個應用廣泛的開源庫。 ASM is a Java bytecode manipulation framework. It can be used to dynamically generate stub classes or other proxy classes, directly in binary form, or to dynamically modify classes at load time, i.e., just before they are loaded into the Java Virtual Machine. ASM完成了BCEL和SERP同樣的功能,但ASM 只有30多k,而後兩者分別是350k和150k。apache真是越來越過氣了。 讓我們來看一個ASM的簡單例子Helloworld.java,它產生一個Example類和一個main方法,main方法列印"Hello world!"語句: 代碼
- import java.io.FileOutputStream;
- import java.io.PrintStream;
-
- import org.objectweb.asm.ClassWriter;
- import org.objectweb.asm.MethodVisitor;
- import org.objectweb.asm.Opcodes;
- import org.objectweb.asm.Type;
- import org.objectweb.asm.commons.GeneratorAdapter;
- import org.objectweb.asm.commons.Method;
-
- public class Helloworld extends ClassLoader implements Opcodes {
-
- public static void main(final String args[]) throws Exception {
-
- // creates a ClassWriter for the Example public class,
- // which inherits from Object
-
- ClassWriter cw = new ClassWriter(0);
- cw.visit(V1_1, ACC_PUBLIC, "Example", null, "java/lang/Object", null);
- MethodVisitor mw = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null,
- null);
- mw.visitVarInsn(ALOAD, 0);
- mw.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
- mw.visitInsn(RETURN);
- mw.visitMaxs(1, 1);
- mw.visitEnd();
- mw = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main",
- "([Ljava/lang/String;)V", null, null);
- mw.visitFieldInsn(GETSTATIC, "java/lang/System", "out",
- "Ljava/io/PrintStream;");
- mw.visitLdcInsn("Hello world!");
- mw.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println",
- "(Ljava/lang/String;)V");
- mw.visitInsn(RETURN);
- mw.visitMaxs(2, 2);
- mw.visitEnd();
- byte[] code = cw.toByteArray();
- FileOutputStream fos = new FileOutputStream("Example.class");
- fos.write(code);
- fos.close();
- Helloworld loader = new Helloworld();
- Class exampleClass = loader
- .defineClass("Example", code, 0, code.length);
- exampleClass.getMethods()[0].invoke(null, new Object[] { null });
-
- // ------------------------------------------------------------------------
- // Same example with a GeneratorAdapter (more convenient but slower)
- // ------------------------------------------------------------------------
-
- cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
- cw.visit(V1_1, ACC_PUBLIC, "Example", null, "java/lang/Object", null);
- Method m = Method.getMethod("void <init> ()");
- GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC, m, null, null,
- cw);
- mg.loadThis();
- mg.invokeConstructor(Type.getType(Object.class), m);
- mg.returnValue();
- mg.endMethod();
- m = Method.getMethod("void main (String[])");
- mg = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC, m, null, null, cw);
- mg.getStatic(Type.getType(System.class), "out", Type
- .getType(PrintStream.class));
- mg.push("Hello world!");
- mg.invokeVirtual(Type.getType(PrintStream.class), Method
- .getMethod("void println (String)"));
- mg.returnValue();
- mg.endMethod();
- cw.visitEnd();
- code = cw.toByteArray();
- loader = new Helloworld();
- exampleClass = loader.defineClass("Example", code, 0, code.length);
- exampleClass.getMethods()[0].invoke(null, new Object[] { null });
- }
- }
我們看到上面的例子分別使用ASM的MethodVisitor和GeneratorAdapter兩種方式來動態產生Example類並調用列印語句。
四、cglib cglib is a powerful, high performance and quality Code Generation Library, It is used to extend JAVA classes and implements interfaces at runtime. cglib是Code Generation Library的縮寫。 cglib依賴於ASM庫。 Hibernate主要是利用cglib產生pojo的子類並override get方法來實現lazy loading機制,Spring則是利用cglib來實現動態代理。 而JDK的動態代理機制要求有介面才行,這樣就強制我們的pojo實現某個介面。 這裡還是提供一個cglib的入門級的樣本: MyClass.java: 代碼
- public class MyClass {
-
- public void print() {
- System.out.println("I'm in MyClass.print!");
- }
-
- }
Main.java: 代碼
- import java.lang.reflect.Method;
- import net.sf.cglib.proxy.Enhancer;
- import net.sf.cglib.proxy.MethodInterceptor;
- import net.sf.cglib.proxy.MethodProxy;
-
- public class Main {
-
- public static void main(String[] args) {
-
- Enhancer enhancer = new Enhancer();
- enhancer.setSuperclass(MyClass.class);
- enhancer.setCallback(new MethodInterceptorImpl());
- MyClass my = (MyClass) enhancer.create();
- my.print();
- }
-
- private static class MethodInterceptorImpl implements MethodInterceptor {
- public Object intercept(Object obj, Method method, Object[] args,
- MethodProxy proxy) throws Throwable {
- // log something
- System.out.println(method + " intercepted!");
-
- proxy.invokeSuper(obj, args);
- return null;
- }
- }
- }
列印結果為: 代碼
- public void MyClass.print() intercepted!
- I'm in MyClass.print!
這個樣本就基本上實現了日誌AOP的功能 |
|