<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    dream.in.java

    能以不變應(yīng)萬(wàn)變是聰明人做事的準(zhǔn)則。萬(wàn)事從小事做起,積累小成功,問(wèn)鼎大成功,是成功者的秘訣。

    深入了解Java ClassLoader、Bytecode 、ASM、cglib

    Java代碼 復(fù)制代碼
    1. import java.io.ByteArrayOutputStream;   
    2. import java.io.File;   
    3. import java.io.FileInputStream;   
    4. import java.io.IOException;   
    5.   
    6. public class FileClassLoader extends ClassLoader {   
    7.   public Class findClass(String name) {   
    8.     byte[] data = loadClassData(name);   
    9.     return defineClass(name, data, 0, data.length);   
    10.   }   
    11.      
    12.   private byte[] loadClassData(String name) {   
    13.     FileInputStream fis = null;   
    14.     byte[] data = null;   
    15.     try {   
    16.       fis = new FileInputStream(new File("D:\\project\\test\\" + name + ".class"));   
    17.       ByteArrayOutputStream baos = new ByteArrayOutputStream();   
    18.       int ch = 0;   
    19.       while ((ch = fis.read()) != -1) {   
    20.         baos.write(ch);   
    21.       }   
    22.       data = baos.toByteArray();   
    23.     } catch (IOException e) {   
    24.       e.printStackTrace();   
    25.     }   
    26.     return data;   
    27.   }   
    28. }  

    MyApp.java:
    Java代碼 復(fù)制代碼
    1. public class MyApp {   
    2.   public static void main(String[] args) throws Exception {   
    3.     FileClassLoader loader = new FileClassLoader();   
    4.     Class objClass = loader.findClass("MyApp");   
    5.     Object obj = objClass.newInstance();   
    6.     System.out.println(objClass.getName());   
    7.     System.out.println(objClass.getClassLoader());   
    8.     System.out.println(obj);   
    9.   }   
    10. }  

    編譯并運(yùn)行MyApp類,結(jié)果為:
    Java代碼 復(fù)制代碼
    1. MyApp   
    2. FileClassLoader@757aef  
    3. MyApp@9cab16  




    二、Bytecode

    1,什么是Bytecode
    C/C++編譯器把源代碼編譯成匯編代碼,Java編譯器把Java源代碼編譯成字節(jié)碼bytecode。
    Java跨平臺(tái)其實(shí)就是基于相同的bytecode規(guī)范做不同平臺(tái)的虛擬機(jī),我們的Java程序編譯成bytecode后就可以在不同平臺(tái)跑了。
    .net框架有IL(intermediate language),匯編是C/C++程序的中間表達(dá)方式,而bytecode可以說(shuō)是Java平臺(tái)的中間語(yǔ)言。
    了解Java字節(jié)碼知識(shí)對(duì)debugging、performance tuning以及做一些高級(jí)語(yǔ)言擴(kuò)展或框架很有幫助。

    2,使用javap生成Bytecode
    JDK自帶的javap.exe文件可以反匯編Bytecode,讓我們看個(gè)例子:
    Test.java:
    Java代碼 復(fù)制代碼
    1. public class Test {   
    2.   public static void main(String[] args) {   
    3.     int i = 10000;   
    4.     System.out.println("Hello Bytecode! Number = " + i);   
    5.   }   
    6. }  

    編譯后的Test.class:
    Java代碼 復(fù)制代碼
    1. 漱壕   1 +   
    2.            
    3.       
    4.      
    5.      
    6.      
    7.      <init> ()V Code LineNumberTable main ([Ljava/lang/String;)V    
    8. SourceFile   Test.java       
    9.     ! " java/lang/StringBuilder Hello Bytecode! Number =   # $  # %  & ' (  ) * Test java/lang/Object java/lang/System out Ljava/io/PrintStream; append -(Ljava/lang/String;)Ljava/lang/StringBuilder; (I)Ljava/lang/StringBuilder; toString ()Ljava/lang/String; java/io/PrintStream println (Ljava/lang/String;)V !    
    10.               
    11.           *                      >     '<  Y                              

    使用javap -c Test > Test.bytecode生成的Test.bytecode:
    Java代碼 復(fù)制代碼
    1. Compiled from "Test.java"  
    2. public class Test extends java.lang.Object{   
    3. public Test();   
    4.   Code:   
    5.    0:  aload_0   
    6.    1:  invokespecial  #1//Method java/lang/Object."<init>":()V   
    7.    4:  return  
    8.   
    9. public static void main(java.lang.String[]);   
    10.   Code:   
    11.    0:  sipush  10000  
    12.    3:  istore_1   
    13.    4:  getstatic  #2//Field java/lang/System.out:Ljava/io/PrintStream;   
    14.    7:  new  #3//class java/lang/StringBuilder   
    15.    10:  dup   
    16.    11:  invokespecial  #4//Method java/lang/StringBuilder."<init>":()V   
    17.    14:  ldc  #5//String Hello Bytecode! Number =    
    18.    16:  invokevirtual  #6//Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;   
    19.    19:  iload_1   
    20.    20:  invokevirtual  #7//Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;   
    21.    23:  invokevirtual  #8//Method java/lang/StringBuilder.toString:()Ljava/lang/String;   
    22.    26:  invokevirtual  #9//Method java/io/PrintStream.println:(Ljava/lang/String;)V   
    23.    29:  return  
    24.   
    25. }  

    JVM就是一個(gè)基于stack的機(jī)器,每個(gè)thread擁有一個(gè)存儲(chǔ)著一些frames的JVM stack,每次調(diào)用一個(gè)方法時(shí)生成一個(gè)frame。
    一個(gè)frame包括一個(gè)local variables數(shù)組(本地變量表),一個(gè)Operand LIFO stack和運(yùn)行時(shí)常量池的一個(gè)引用。

    我們來(lái)簡(jiǎn)單分析一下生成的字節(jié)碼指令:
    aload和iload指令的“a”前綴和“i”分別表示對(duì)象引用和int類型,其他還有“b”表示byte,“c”表示char,“d”表示double等等
    我們這里的aload_0表示將把local variable table中index 0的值push到Operand stack,iload_1類似
    invokespecial表示初始化對(duì)象,return表示返回
    sipush表示把10000這個(gè)int值push到Operand stack
    getstatic表示取靜態(tài)域
    invokevirtual表示調(diào)用一些實(shí)例方法
    這些指令又稱為opcode,Java一直以來(lái)只有約202個(gè)Opcode,具體請(qǐng)參考Java Bytecode規(guī)范。

    我們看到Test.class文件不全是二進(jìn)制的指令,有些是我們可以識(shí)別的字符,這是因?yàn)橛行┌㈩惷统A孔址疀](méi)有編譯成二進(jìn)制Bytecode指令。

    3,體驗(yàn)字節(jié)碼增強(qiáng)的魔力
    我們J2EE常用的Hibernate、Spring都用到了動(dòng)態(tài)字節(jié)碼修改來(lái)改變類的行為。
    讓我們通過(guò)看看ASM的org.objectweb.asm.MethodWriter類的部分方法來(lái)理解ASM是如何修改字節(jié)碼的:
    Java代碼 復(fù)制代碼
    1. class MethodWriter implements MethodVisitor {   
    2.   
    3.     private ByteVector code = new ByteVector();   
    4.   
    5.     public void visitIntInsn(final int opcode, final int operand) {   
    6.         // Label currentBlock = this.currentBlock;   
    7.         if (currentBlock != null) {   
    8.             if (compute == FRAMES) {   
    9.                 currentBlock.frame.execute(opcode, operand, nullnull);   
    10.             } else if (opcode != Opcodes.NEWARRAY) {   
    11.                 // updates current and max stack sizes only for NEWARRAY   
    12.                 // (stack size variation = 0 for BIPUSH or SIPUSH)   
    13.                 int size = stackSize + 1;   
    14.                 if (size > maxStackSize) {   
    15.                     maxStackSize = size;   
    16.                 }   
    17.                 stackSize = size;   
    18.             }   
    19.         }   
    20.         // adds the instruction to the bytecode of the method   
    21.         if (opcode == Opcodes.SIPUSH) {   
    22.             code.put12(opcode, operand);   
    23.         } else { // BIPUSH or NEWARRAY   
    24.             code.put11(opcode, operand);   
    25.         }   
    26.     }   
    27.   
    28.     public void visitMethodInsn(   
    29.         final int opcode,   
    30.         final String owner,   
    31.         final String name,   
    32.         final String desc)   
    33.     {   
    34.         boolean itf = opcode == Opcodes.INVOKEINTERFACE;   
    35.         Item i = cw.newMethodItem(owner, name, desc, itf);   
    36.         int argSize = i.intVal;   
    37.         // Label currentBlock = this.currentBlock;   
    38.         if (currentBlock != null) {   
    39.             if (compute == FRAMES) {   
    40.                 currentBlock.frame.execute(opcode, 0, cw, i);   
    41.             } else {   
    42.                 /*  
    43.                  * computes the stack size variation. In order not to recompute  
    44.                  * several times this variation for the same Item, we use the  
    45.                  * intVal field of this item to store this variation, once it  
    46.                  * has been computed. More precisely this intVal field stores  
    47.                  * the sizes of the arguments and of the return value  
    48.                  * corresponding to desc.  
    49.                  */  
    50.                 if (argSize == 0) {   
    51.                     // the above sizes have not been computed yet,   
    52.                     // so we compute them...   
    53.                     argSize = getArgumentsAndReturnSizes(desc);   
    54.                     // ... and we save them in order   
    55.                     // not to recompute them in the future   
    56.                     i.intVal = argSize;   
    57.                 }   
    58.                 int size;   
    59.                 if (opcode == Opcodes.INVOKESTATIC) {   
    60.                     size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1;   
    61.                 } else {   
    62.                     size = stackSize - (argSize >> 2) + (argSize & 0x03);   
    63.                 }   
    64.                 // updates current and max stack sizes   
    65.                 if (size > maxStackSize) {   
    66.                     maxStackSize = size;   
    67.                 }   
    68.                 stackSize = size;   
    69.             }   
    70.         }   
    71.         // adds the instruction to the bytecode of the method   
    72.         if (itf) {   
    73.             if (argSize == 0) {   
    74.                 argSize = getArgumentsAndReturnSizes(desc);   
    75.                 i.intVal = argSize;   
    76.             }   
    77.             code.put12(Opcodes.INVOKEINTERFACE, i.index).put11(argSize >> 20);   
    78.         } else {   
    79.             code.put12(opcode, i.index);   
    80.         }   
    81.     }   
    82. }  

    通過(guò)注釋我們可以大概理解visitIntInsn和visitMethodInsn方法的意思。
    比如visitIntInsn先計(jì)算stack的size,然后根據(jù)opcode來(lái)判斷是SIPUSH指令還是BIPUSH or NEWARRAY指令,并相應(yīng)的調(diào)用字節(jié)碼修改相關(guān)的方法。


    三、ASM
    我們知道Java是靜態(tài)語(yǔ)言,而python、ruby是動(dòng)態(tài)語(yǔ)言,Java程序一旦寫好很難在運(yùn)行時(shí)更改類的行為,而python、ruby可以。
    不過(guò)基于bytecode層面上我們可以做一些手腳,來(lái)使Java程序多一些靈活性和Magic,ASM就是這樣一個(gè)應(yīng)用廣泛的開(kāi)源庫(kù)。

    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完成了BCELSERP同樣的功能,但ASM
    只有30多k,而后兩者分別是350k和150k。apache真是越來(lái)越過(guò)氣了。

    讓我們來(lái)看一個(gè)ASM的簡(jiǎn)單例子Helloworld.java,它生成一個(gè)Example類和一個(gè)main方法,main方法打印"Hello world!"語(yǔ)句:
    Java代碼 復(fù)制代碼
    1. import java.io.FileOutputStream;   
    2. import java.io.PrintStream;   
    3.   
    4. import org.objectweb.asm.ClassWriter;   
    5. import org.objectweb.asm.MethodVisitor;   
    6. import org.objectweb.asm.Opcodes;   
    7. import org.objectweb.asm.Type;   
    8. import org.objectweb.asm.commons.GeneratorAdapter;   
    9. import org.objectweb.asm.commons.Method;   
    10.   
    11. public class Helloworld extends ClassLoader implements Opcodes {   
    12.   
    13.   public static void main(final String args[]) throws Exception {   
    14.   
    15.     // creates a ClassWriter for the Example public class,   
    16.     // which inherits from Object   
    17.   
    18.     ClassWriter cw = new ClassWriter(0);   
    19.     cw.visit(V1_1, ACC_PUBLIC, "Example"null"java/lang/Object"null);   
    20.     MethodVisitor mw = cw.visitMethod(ACC_PUBLIC, "<init>""()V"null,   
    21.         null);   
    22.     mw.visitVarInsn(ALOAD, 0);   
    23.     mw.visitMethodInsn(INVOKESPECIAL, "java/lang/Object""<init>""()V");   
    24.     mw.visitInsn(RETURN);   
    25.     mw.visitMaxs(11);   
    26.     mw.visitEnd();   
    27.     mw = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main",   
    28.         "([Ljava/lang/String;)V"nullnull);   
    29.     mw.visitFieldInsn(GETSTATIC, "java/lang/System""out",   
    30.         "Ljava/io/PrintStream;");   
    31.     mw.visitLdcInsn("Hello world!");   
    32.     mw.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream""println",   
    33.         "(Ljava/lang/String;)V");   
    34.     mw.visitInsn(RETURN);   
    35.     mw.visitMaxs(22);   
    36.     mw.visitEnd();   
    37.     byte[] code = cw.toByteArray();   
    38.     FileOutputStream fos = new FileOutputStream("Example.class");   
    39.     fos.write(code);   
    40.     fos.close();   
    41.     Helloworld loader = new Helloworld();   
    42.     Class exampleClass = loader   
    43.         .defineClass("Example", code, 0, code.length);   
    44.     exampleClass.getMethods()[0].invoke(nullnew Object[] { null });   
    45.   
    46.     // ------------------------------------------------------------------------   
    47.     // Same example with a GeneratorAdapter (more convenient but slower)   
    48.     // ------------------------------------------------------------------------   
    49.   
    50.     cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);   
    51.     cw.visit(V1_1, ACC_PUBLIC, "Example"null"java/lang/Object"null);   
    52.     Method m = Method.getMethod("void <init> ()");   
    53.     GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC, m, nullnull,   
    54.         cw);   
    55.     mg.loadThis();   
    56.     mg.invokeConstructor(Type.getType(Object.class), m);   
    57.     mg.returnValue();   
    58.     mg.endMethod();   
    59.     m = Method.getMethod("void main (String[])");   
    60.     mg = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC, m, nullnull, cw);   
    61.     mg.getStatic(Type.getType(System.class), "out", Type   
    62.         .getType(PrintStream.class));   
    63.     mg.push("Hello world!");   
    64.     mg.invokeVirtual(Type.getType(PrintStream.class), Method   
    65.         .getMethod("void println (String)"));   
    66.     mg.returnValue();   
    67.     mg.endMethod();   
    68.     cw.visitEnd();   
    69.     code = cw.toByteArray();   
    70.     loader = new Helloworld();   
    71.     exampleClass = loader.defineClass("Example", code, 0, code.length);   
    72.     exampleClass.getMethods()[0].invoke(nullnew Object[] { null });   
    73.   }   
    74. }  

    我們看到上面的例子分別使用ASM的MethodVisitor和GeneratorAdapter兩種方式來(lái)動(dòng)態(tài)生成Example類并調(diào)用打印語(yǔ)句。

    四、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庫(kù)。
    Hibernate主要是利用cglib生成pojo的子類并override get方法來(lái)實(shí)現(xiàn)lazy loading機(jī)制,Spring則是利用cglib來(lái)實(shí)現(xiàn)動(dòng)態(tài)代理。
    而JDK的動(dòng)態(tài)代理機(jī)制要求有接口才行,這樣就強(qiáng)制我們的pojo實(shí)現(xiàn)某個(gè)接口。

    這里還是提供一個(gè)cglib的入門級(jí)的示例:
    MyClass.java:
    Java代碼 復(fù)制代碼
    1. public class MyClass {   
    2.   
    3.   public void print() {   
    4.     System.out.println("I'm in MyClass.print!");   
    5.   }   
    6.   
    7. }  

    Main.java:
    Java代碼 復(fù)制代碼
    1. import java.lang.reflect.Method;   
    2. import net.sf.cglib.proxy.Enhancer;   
    3. import net.sf.cglib.proxy.MethodInterceptor;   
    4. import net.sf.cglib.proxy.MethodProxy;   
    5.   
    6. public class Main {   
    7.   
    8.   public static void main(String[] args) {   
    9.   
    10.     Enhancer enhancer = new Enhancer();   
    11.     enhancer.setSuperclass(MyClass.class);   
    12.     enhancer.setCallback(new MethodInterceptorImpl());   
    13.     MyClass my = (MyClass) enhancer.create();   
    14.     my.print();   
    15.   }   
    16.   
    17.   private static class MethodInterceptorImpl implements MethodInterceptor {   
    18.     public Object intercept(Object obj, Method method, Object[] args,   
    19.         MethodProxy proxy) throws Throwable {   
    20.       // log something   
    21.       System.out.println(method + " intercepted!");   
    22.   
    23.       proxy.invokeSuper(obj, args);   
    24.       return null;   
    25.     }   
    26.   }   
    27. }  

    打印結(jié)果為:
    Java代碼 復(fù)制代碼
    1. public void MyClass.print() intercepted!   
    2. I'm in MyClass.print!  

    這個(gè)示例就基本上實(shí)現(xiàn)了日志AOP的功能,很簡(jiǎn)單吧。

    參考資料
    CLR和JRE的運(yùn)行機(jī)制的初步總結(jié)
    Java虛擬機(jī)
    了解Java ClassLoader
    Java Virtual Machine Specification
    Java bytecode
    解讀字節(jié)碼文件
    Java Bytecode Specification and Verification
    ASM User Guide
    Hello, ASM
    cglig指南
    Java下的框架編程--cglib的應(yīng)用
    AOP = Proxy Pattern + Method Reflection + Aspect DSL + 自動(dòng)代碼生成
    深入淺出Spring AOP

    Traceback:http://www.javaeye.com/topic/98178

    posted on 2009-04-14 09:22 YXY 閱讀(319) 評(píng)論(0)  編輯  收藏


    只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 亚洲乳大丰满中文字幕| 久久国内免费视频| 亚洲偷自拍拍综合网| 一区二区三区精品高清视频免费在线播放 | a级毛片视频免费观看| 精品亚洲视频在线观看| 人成午夜免费大片在线观看| 日本a级片免费看| 日韩欧美亚洲中文乱码| 国产免费怕怕免费视频观看| 精品亚洲成a人在线观看| 四虎影视免费永久在线观看| 天天综合亚洲色在线精品| 亚洲成a人无码av波多野按摩| 成人国产网站v片免费观看| 亚洲性猛交XXXX| 色猫咪免费人成网站在线观看| 亚洲综合视频在线观看| 一个人免费观看视频www| 亚洲男人的天堂网站| 亚洲А∨精品天堂在线| 99久久婷婷免费国产综合精品| 久久亚洲一区二区| 日韩一区二区a片免费观看| 亚洲AV香蕉一区区二区三区| 亚洲成人一区二区| 免费无码一区二区三区| 伊人久久五月丁香综合中文亚洲| 国产区卡一卡二卡三乱码免费| 久久精品无码免费不卡| 一个人免费观看在线视频www| 亚洲av日韩av永久在线观看| 综合久久久久久中文字幕亚洲国产国产综合一区首 | 亚洲第一黄色网址| 一区二区三区无码视频免费福利| 亚洲无人区视频大全| 国产区卡一卡二卡三乱码免费| 久草视频在线免费看| 永久亚洲成a人片777777| 最刺激黄a大片免费网站| 亚洲精品国产首次亮相|