Java 字節碼 工具 javassist

javassist是基於源碼級別的API比基於字節碼的ASM簡單。
基於javassist開發,不需要了解字節碼的一些知識,而且其封裝的一些工具類可以簡單實現一些高級功能。比如HotSwaper。

用來前後攔截方法的使用方式:
用法:判斷方法是否爲空(空方法或抽象方法):isEmpty
用法:判斷方法是否是構造方法:isConstructor
用法:判斷方法是否是靜態塊構造:isStaticInitializer
用法:獲取方法的第一行的行號:getLineNumber(1)或者getLineNumber(0)  (傳入的參數表示源碼中相對於首行的位置)
用法:開頭插入代碼:
ctMethod.insertBefore("com.profiler.core.JpProcessor.methodBegin($$);");
用法:方法返回前插入代碼:
ctMethod.insertAfter("com.profiler.core.JpProcessor.methodBegin(null);");
其它用法:insertParameter、addCatch、insertAt 等

public byte[] modifyBytecode(String className, byte[] originalBytecode, boolean profileArgs) {
        try {
            CtClass ctClass = ClassPool.getDefault().makeClass(new ByteArrayInputStream(originalBytecode));
            CtMethod[] declaredMethods = ctClass.getDeclaredMethods();
            for (CtMethod ctMethod : declaredMethods) {
                try {
                    if (ctMethod.isEmpty()) continue;// abstract or method body is {}
                    MethodInfo methodInfo = ctMethod.getMethodInfo();
                    if (methodInfo.isConstructor()) continue;// name:<init>
                    if(methodInfo.isStaticInitializer()) continue;// <clinit>
                    // methodInfo.getAccessFlags(); // ACC_ABSTRACT/ACC_PUBLIC/ACC_FINAL etc.
                    if (methodInfo.isMethod()) {
                        System.out.println(methodInfo.getName());
                        int lineNumber = ctMethod.getMethodInfo().getLineNumber(0);
                        System.out.println("lineNumber:" + lineNumber);
                        ctMethod.insertBefore("com.profiler.core.JpProcessor.methodBegin($$);");
                        ctMethod.insertAfter("com.profiler.core.JpProcessor.methodBegin(null);");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return originalBytecode;// NOP
    }



出處:http://www.cnblogs.com/sunfie/p/5154246.html
1、讀取和輸出字節碼
複製代碼
 1 ClassPool pool = ClassPool.getDefault();
 2 //會從classpath中查詢該類
 3 CtClass cc = pool.get("test.Rectangle");
 4 //設置.Rectangle的父類
 5 cc.setSuperclass(pool.get("test.Point"));
 6 //輸出.Rectangle.class文件到該目錄中
 7 cc.writeFile("c://");
 8 //輸出成二進制格式
 9 //byte[] b=cc.toBytecode();
10 //輸出並加載class 類,默認加載到當前線程的ClassLoader中,也可以選擇輸出的ClassLoader。
11 //Class clazz=cc.toClass();
複製代碼
這裏可以看出,Javassist的加載是依靠ClassPool類,輸出方式支持三種。
2、新增Class
1 ClassPool pool = ClassPool.getDefault();
2 CtClass cc = pool.makeClass("Point");
3 //新增方法
4 cc.addMethod(m);
5 //新增Field
6 cc.addField(f);
從上面可以看出,對Class的修改主要是依賴於CtClass類。API也比較清楚和簡單。
3、凍結Class
    當CtClass 調用writeFile()、toClass()、toBytecode() 這些方法的時候,Javassist會凍結CtClass Object,對CtClass object的修改將不允許。這個主要是爲了警告開發者該類已經被加載,而JVM是不允許重新加載該類的。如果要突破該限制,方法如下:
1 CtClasss cc = ...;
2     :
3 cc.writeFile();
4 cc.defrost();
5 cc.setSuperclass(...);    // OK since the class is not frozen.
    當 ClassPool.doPruning=true的時候,Javassist 在CtClass object被凍結時,會釋放存儲在ClassPool對應的數據。這樣做可以減少javassist的內存消耗。默認情況ClassPool.doPruning=false。例如 
1 CtClasss cc = ...;
2 cc.stopPruning(true);
3     :
4 cc.writeFile();                             // convert to a class file.
5 // cc沒有被釋放
提示:當調試時,可以調用debugWriteFile(),該方法不會導致CtClass被釋放。
4、Class 搜索路徑
    從上面可以看出Class 的載入是依靠ClassPool,而ClassPool.getDefault() 方法的搜索Classpath 只是搜索JVM的同路徑下的class。當一個程序運行在JBoss或者Tomcat下,ClassPool Object 可能找到用戶的classes。Javassist 提供了四種動態加載classpath的方法。如下
複製代碼
 1 //默認加載方式如pool.insertClassPath(new ClassClassPath(this.getClass()));
 2 ClassPool pool = ClassPool.getDefault();
 3 //從file加載classpath
 4 pool.insertClassPath("/usr/local/javalib")
 5 //從URL中加載
 6 ClassPath cp = new URLClassPath("www.javassist.org", 80, "/java/", "org.javassist.");
 7 pool.insertClassPath(cp);
 8 //從byte[] 中加載
 9 byte[] b = a byte array;
10 String name = class name;
11 cp.insertClassPath(new ByteArrayClassPath(name, b));
12 //可以從輸入流中加載class
13 InputStream ins = an input stream for reading a class file;
14 CtClass cc = cp.makeClass(ins);
複製代碼
5、ClassPool
5.1 減少內存溢出
     ClassPool是一個CtClass objects的裝載容器,當加載了CtClass object後,是不會被ClassPool釋放的(默認情況下),這個是因爲CtClass object 有可能在下個階段會被用到,當加載過多的CtClass object的時候,會造成OutOfMemory的異常。爲了避免這個異常,javassist提供幾種方法,一種是在上面提到的 ClassPool.doPruning這個參數,還有一種方法是調用CtClass.detach()方法,可以把CtClass object 從ClassPool中移除。例如:
1 CtClass cc = ... ;
2 cc.writeFile();
3 cc.detach();
    另外一種方法是不用默認的ClassPool即不用 ClassPool.getDefault()這個方式來生成,這樣當ClassPool沒被引用的時候,JVM的垃圾收集會收集該類。例如
1 //ClassPool(true) 會默認加載Jvm的ClassPath
2 ClassPool cp = new ClassPool(true);
3 // if needed, append an extra search path by appendClassPath()
5.2  級聯ClassPools
     javassist支持級聯的ClassPool,即類似於繼承。例如:
1 ClassPool parent = ClassPool.getDefault();
2 ClassPool child = new ClassPool(parent);
3 child.insertClassPath("./classes");
5.3 修改已有Class的name以創建一個新的Class
    當調用setName方法時,會直接修改已有的Class的類名,如果再次使用舊的類名,則會重新在classpath路徑下加載。例如:
1 ClassPool pool = ClassPool.getDefault();
2 CtClass cc = pool.get("Point");
3 cc.setName("Pair");
4 //重新在classpath加載
5 CtClass cc1 = pool.get("Point"); 
    對於一個被凍結(Frozen)的CtClass object ,是不可以修改class name的,如果需要修改,則可以重新加載,例如:
1 ClassPool pool = ClassPool.getDefault();
2 CtClass cc = pool.get("Point");
3 cc.writeFile(); // has frozened
4 //cc.setName("Pair");    wrong since writeFile() has been called.
5 CtClass cc2 = pool.getAndRename("Point", "Pair"); 
6、Class loader
    上面也提到,javassist同個Class是不能在同個ClassLoader中加載兩次的,所以在輸出CtClass的時候需要注意下,例如:
複製代碼
 1 // 當Hello未加載的時候,下面是可以運行的。
 2 ClassPool cp = ClassPool.getDefault();
 3 CtClass cc = cp.get("Hello");
 4 Class c = cc.toClass();
 5 //下面這種情況,由於Hello2已加載,所以會出錯
 6 Hello2 h=new Hello2();
 7 CtClass cc2 = cp.get("Hello2");
 8 Class c2 = cc.toClass();//這裏會拋出java.lang.LinkageError 異常
 9 //解決加載問題,可以指定一個未加載的ClassLoader
10 Class c3 = cc.toClass(new MyClassLoader());
複製代碼
6.1 使用javassist.Loader
    從上面可以看到,如果在同一個ClassLoader加載兩次Class拋出異常,爲了方便javassist也提供一個Classloader供使用,例如
1  ClassPool pool = ClassPool.getDefault();
2  Loader cl = new Loader(pool);
3  CtClass ct = pool.get("test.Rectangle");
4  ct.setSuperclass(pool.get("test.Point"));
5  Class c = cl.loadClass("test.Rectangle");
6  Object rect = c.newInstance();        :
   爲了方便監聽Javassist自帶的ClassLoader的生命週期,javassist也提供了一個listener,可以監聽ClassLoader的生命週期,例如:
複製代碼
 1 //Translator 爲監聽器
 2 public class MyTranslator implements Translator {
 3     void start(ClassPool pool)
 4         throws NotFoundException, CannotCompileException {}
 5     void onLoad(ClassPool pool, String classname)
 6         throws NotFoundException, CannotCompileException
 7     {
 8         CtClass cc = pool.get(classname);
 9         cc.setModifiers(Modifier.PUBLIC);
10     }
11 }
12 //示例
13 public class Main2 {
14   public static void main(String[] args) throws Throwable {
15      Translator t = new MyTranslator();
16      ClassPool pool = ClassPool.getDefault();
17      Loader cl = new Loader();
18      cl.addTranslator(pool, t);
19      cl.run("MyApp", args);
20   }
21 }
22 //輸出
23 % java Main2 arg1 arg2...
複製代碼
6.2 修改系統Class
    由JVM規範可知,system classloader 是比其他classloader 是優先加載的,而system classloader 主要是加載系統Class,所以要修改系統Class,如果默認參數運行程序是不可能修改的,如果需要修改也有一些辦法,即在運行時加入-Xbootclasspath/p: 參數的意義可以參考其他文件。下面修改String的例子如下:
複製代碼
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("java.lang.String");
CtField f = new CtField(CtClass.intType, "hiddenValue", cc);
f.setModifiers(Modifier.PUBLIC);
cc.addField(f);
cc.writeFile(".");
//運行腳本
% java -Xbootclasspath/p:. MyApp arg1 arg2...
複製代碼
6.3 動態重載Class
    如果JVM運行時開啓JPDA(Java Platform Debugger Architecture),則Class是運行被動態重新載入的。具體方式可以參考java.lang.Instrument。javassist也提供了一個運行期重載Class的方法,具體可以看API 中的javassist.tools.HotSwapper。
7、Introspection和定製
    javassist封裝了很多很方便的方法以供使用,大部分使用只需要用這些API即可,如果不能滿足,Javassist也提供了一個低層的API(具體參考javassist.bytecode 包)來修改原始的Class。
7.1 插入source 文本在方法體前或者後
     CtMethod 和CtConstructor 提供了 insertBefore()、insertAfter()和 addCatch()方法,它們可以插入一個souce文本到存在的方法的相應的位置。javassist 包含了一個簡單的編譯器解析這souce文本成二進制插入到相應的方法體裏。javassist 還支持插入一個代碼段到指定的行數,前提是該行數需要在class 文件裏含有。插入的source 可以關聯fields 和methods,也可以關聯方法的參數。但是關聯方法參數的時,需要在程序編譯時加上 -g 選項(該選項可以把本地變量的聲明保存在class 文件中,默認是不加這個參數的。)。因爲默認一般不加這個參數,所以Javassist也提供了一些特殊的變量來代表方法參數:$1,$2,$args...要注意的是,插入的source文本中不能引用方法本地變量的聲明,但是可以允許聲明一個新的方法本地變量,除非在程序編譯時加入-g選項。方法的特殊變量說明:
$0, $1, $2, ... this and actual parameters
$args An array of parameters. The type of $args is Object[].
$$ All actual parameters.For example, m($$) is equivalent to m($1,$2,...)
$cflow(...) cflow variable
$r The result type. It is used in a cast expression.
$w The wrapper type. It is used in a cast expression.
$_ The resulting value
$sig An array of java.lang.Class objects representing the formal parameter types
$type A java.lang.Class object representing the formal result type.
$class A java.lang.Class object representing the class currently edited.
 
7.1.1 $0, $1, $2, ...
   $0代碼的是this,$1代表方法參數的第一個參數、$2代表方法參數的第二個參數,以此類推,$N代表是方法參數的第N個。例如:
1 //實際方法
2 void move(int dx, int dy) 
3 //javassist
4 CtMethod m = cc.getDeclaredMethod("move");
5 //打印dx,和dy
6 m.insertBefore("{ System.out.println($1); System.out.println($2); }");
注意:如果javassist改變了$1的值,那實際參數值也會改變。
7.1.2 $args
    $args 指的是方法所有參數的數組,類似Object[],如果參數中含有基本類型,則會轉成其包裝類型。需要注意的時候,$args[0]對應的是$1,而不是$0,$0!=$args[0],$0=this。
7.1.3 $$
    $$是所有方法參數的簡寫,主要用在方法調用上。例如:
1 //原方法
2 move(String a,String b)
3 move($$) 相當於move($1,$2)
4 如果新增一個方法,方法含有move的所有參數,則可以這些寫:
5 exMove($$, context) 相當於 exMove($1, $2, context)
7.1.4 $cflow
 $cflow意思爲控制流(control flow),是一個只讀的變量,值爲一個方法調用的深度。例如:
複製代碼
 1 //原方法
 2 int fact(int n) {
 3     if (n <= 1)
 4         return n;
 5     else
 6         return n * fact(n - 1);
 7 }
 8 //javassist調用
 9 CtMethod cm = ...;
10 //這裏代表使用了cflow
11 cm.useCflow("fact");
12 //這裏用了cflow,說明當深度爲0的時候,就是開始當第一次調用fact的方法的時候,打印方法的第一個參數
13 cm.insertBefore("if ($cflow(fact) == 0)"
14               + "    System.out.println(\"fact \" + $1);");
複製代碼
7.1.5 $r
   指的是方法返回值的類型,主要用在類型的轉型上。例如:
Object result = ... ;
$_ = ($r)result;
如果返回值爲基本類型的包裝類型,則該值會自動轉成基本類型,如返回值爲Integer,則$r爲int。如果返回值爲void,則該值爲null。
7.1.6 $w
$w代表一個包裝類型。主要用在轉型上。比如:Integer i = ($w)5; 如果該類型不是基本類型,則會忽略。
7.1.7 $_
$_代表的是方法的返回值。
7.1.8 $sig
$sig指的是方法參數的類型(Class)數組,數組的順序爲參數的順序。
7.1.9 $class
$class 指的是this的類型(Class)。也就是$0的類型。
7.1.10 addCatch()
   addCatch() 指的是在方法中加入try catch 塊,需要注意的是,必須在插入的代碼中,加入return 值。$e代表異常值。比如:
1 CtMethod m = ...;
2 CtClass etype = ClassPool.getDefault().get("java.io.IOException");
3 m.addCatch("{ System.out.println($e); throw $e; }", etype);
實際代碼如下:
複製代碼
1 try {
2     the original method body
3 }
4 catch (java.io.IOException e) {
5     System.out.println(e);
6     throw e;
7 }
複製代碼
8、修改方法體
CtMethod 和CtConstructor 提供了 setBody() 的方法,可以替換方法或者構造函數裏的所有內容。
支持的變量有:
$0, $1, $2, ... this and actual parameters
$args An array of parameters. The type of $args is Object[].
$$ All actual parameters.For example, m($$) is equivalent to m($1,$2,...)
$cflow(...) cflow variable
$r The result type. It is used in a cast expression.
$w The wrapper type. It is used in a cast expression.
$sig An array of java.lang.Class objects representing the formal parameter types
$type A java.lang.Class object representing the formal result type.
$class A java.lang.Class object representing the class currently edited.
注意 $_變量不支持。
8.1 替換方法中存在的source
javassist 允許修改方法裏的其中一個表達式,javassist.expr.ExprEditor 這個class 可以替換該表達式。例如:
複製代碼
 1 CtMethod cm = ... ;
 2 cm.instrument(
 3     new ExprEditor() {
 4         public void edit(MethodCall m)
 5                       throws CannotCompileException
 6         {
 7             if (m.getClassName().equals("Point")
 8                           && m.getMethodName().equals("move"))
 9                 m.replace("{ $1 = 0; $_ = $proceed($$); }");
10         }
11     });
複製代碼
    注意: that the substituted code is not an expression but a statement or a block. It cannot be or contain a try-catch statement.
方法instrument() 可以用來搜索方法體裏的內容。比如調用一個方法,field訪問,對象創建等。如果你想在某個表達式前後插入方法,則修改的souce如下:
{ before-statements;
  $_ = $proceed($$);
  after-statements; } 
8.2 javassist.expr.MethodCall
MethodCall代表的是一個方法的調用。用replace()方法可以對調用的方法進行替換。
$0 The target object of the method call.
This is not equivalent to this, which represents the caller-side this object.
$0 is null if the method is static.
$1, $2, ... The parameters of the method call.
$_ The resulting value of the method call.
$r The result type of the method call.
$class A java.lang.Class object representing the class declaring the method.
$sig An array of java.lang.Class objects representing the formal parameter types
$type A java.lang.Class object representing the formal result type.
$proceed The name of the method originally called in the expression.
注意:$w, $args 和 $$也是允許的。$0不是this,是隻調用方法的Object。$proceed指的是一個特殊的語法,而不是一個String。
8.3 javassist.expr.ConstructorCall
ConstructorCall 指的是一個構造函數,比如:this()、super()的調用。ConstructorCall.replace()是用來用替換一個塊當調用構造方法的時候。
$0 The target object of the constructor call. This is equivalent to this.
$1, $2, ... The parameters of the constructor call.
$class A java.lang.Class object representing the class declaring the constructor.
$sig An array of java.lang.Class objects representing the formal parameter types.
$proceed The name of the constructor originally called in the expression.
$w, $args 和 $$  也是允許的。
8.4 javassist.expr.FieldAccess
FieldAccess代表的是Field的訪問類。
$0 The object containing the field accessed by the expression. This is not equivalent to this.
this represents the object that the method including the expression is invoked on.
$0 is null if the field is static.
$1 The value that would be stored in the field if the expression is write access.
Otherwise, $1 is not available.
$_ The resulting value of the field access if the expression is read access.
Otherwise, the value stored in $_ is discarded.
$r The type of the field if the expression is read access.
Otherwise, $r is void.
$class A java.lang.Class object representing the class declaring the field.
$type A java.lang.Class object representing the field type.
$proceed The name of a virtual method executing the original field access. .
$w, $args 和 $$  也是允許的。 
8.5 javassist.expr.NewExpr
NewExpr代表的是一個Object 的操作(但不包括數組的創建)。
$0 null
$1, $2, ... The parameters to the constructor.
$_ The resulting value of the object creation.
A newly created object must be stored in this variable.
$r The type of the created object.
$sig An array of java.lang.Class objects representing the formal parameter types
$type A java.lang.Class object representing the class of the created object.
$proceed The name of a virtual method executing the original object creation. .
$w, $args 和 $$  也是允許的。
8.6 javassist.expr.NewArray
NewArray 代表的是數組的創建。
$0 null
$1, $2, ... The size of each dimension.
$_ The resulting value of the object creation. 
A newly created array must be stored in this variable.
$r The type of the created object.
$type A java.lang.Class object representing the class of the created array .
$proceed The name of a virtual method executing the original array creation. .
$w, $args 和 $$  也是允許的。
例如:
String[][] s = new String[3][4];
 $1 和 $2 的值爲 3 和 4, $3 得不到的.
String[][] s = new String[3][];
 $1 的值是 3 ,但 $2 得不到的.
8.7 javassist.expr.Instanceof
Instanceof 代表的是Instanceof 表達式。
$0 null
$1 The value on the left hand side of the original instanceof operator.
$_ The resulting value of the expression. The type of $_ is boolean.
$r The type on the right hand side of the instanceof operator.
$type A java.lang.Class object representing the type on the right hand side of the instanceof operator.
$proceed The name of a virtual method executing the original instanceof expression.
It takes one parameter (the type is java.lang.Object) and returns true
if the parameter value is an instance of the type on the right hand side of
the original instanceof operator. Otherwise, it returns false.
$w, $args 和 $$  也是允許的。 
8.8 javassist.expr.Cast
Cast 代表的是一個轉型表達式。
$0 null
$1 The value the type of which is explicitly cast.
$_ The resulting value of the expression. The type of $_ is the same as the type
after the explicit casting, that is, the type surrounded by ( ).
$r the type after the explicit casting, or the type surrounded by ( ).
$type A java.lang.Class object representing the same type as $r.
$proceed The name of a virtual method executing the original type casting.
It takes one parameter of the type java.lang.Object and returns it after
the explicit type casting specified by the original expression.
$w, $args 和 $$  也是允許的。 
8.9 javassist.expr.Handler
Handler 代表的是一個try catch 聲明。
$1 The exception object caught by the catch clause.
$r the type of the exception caught by the catch clause. It is used in a cast expression.
$w The wrapper type. It is used in a cast expression.
$type A java.lang.Class object representing
the type of the exception caught by the catch clause.
9 新增一個方法或者field
Javassist 允許開發者創建一個新的方法或者構造方法。新增一個方法,例如:
複製代碼
 1 CtClass point = ClassPool.getDefault().get("Point");
 2 CtMethod m = CtNewMethod.make(
 3                  "public int xmove(int dx) { x += dx; }",
 4                  point);
 5 point.addMethod(m);
 6  
 7 在方法中調用其他方法,例如:
 8 CtClass point = ClassPool.getDefault().get("Point");
 9 CtMethod m = CtNewMethod.make(
10                  "public int ymove(int dy) { $proceed(0, dy); }",
11                  point, "this", "move");
12 其效果如下:
13 public int ymove(int dy) { this.move(0, dy); }
複製代碼
下面是javassist提供另一種新增一個方法(未看明白):
Javassist provides another way to add a new method. You can first create an abstract method and later give it a method body:
複製代碼
1 CtClass cc = ... ;
2 CtMethod m = new CtMethod(CtClass.intType, "move",
3                           new CtClass[] { CtClass.intType }, cc);
4 cc.addMethod(m);
5 m.setBody("{ x += $1; }");
6 cc.setModifiers(cc.getModifiers() & ~Modifier.ABSTRACT);
7 Since Javassist makes a class abstract if an abstract method is added to the class, you have to explicitly change the class back to a non-abstract one after calling setBody().
複製代碼
9.1 遞歸方法
複製代碼
1 CtClass cc = ... ;
2 CtMethod m = CtNewMethod.make("public abstract int m(int i);", cc);
3 CtMethod n = CtNewMethod.make("public abstract int n(int i);", cc);
4 cc.addMethod(m);
5 cc.addMethod(n);
6 m.setBody("{ return ($1 <= 0) ? 1 : (n($1 - 1) * $1); }");
7 n.setBody("{ return m($1); }");
8 cc.setModifiers(cc.getModifiers() & ~Modifier.ABSTRACT);
複製代碼
9.2 新增field
如下:
複製代碼
1 CtClass point = ClassPool.getDefault().get("Point");
2 CtField f = new CtField(CtClass.intType, "z", point);
3 point.addField(f);
4 //point.addField(f, "0");    // initial value is 0.
5 或者:
6 CtClass point = ClassPool.getDefault().get("Point");
7 CtField f = CtField.make("public int z = 0;", point);
8 point.addField(f);
複製代碼
9.3 移除方法或者field
1 調用removeField()或者removeMethod()。
10 註解
獲取註解信息:
複製代碼
 1 //註解
 2 public @interface Author {
 3     String name();
 4     int year();
 5 }
 6 //javassist代碼
 7 CtClass cc = ClassPool.getDefault().get("Point");
 8 Object[] all = cc.getAnnotations();
 9 Author a = (Author)all[0];
10 String name = a.name();
11 int year = a.year();
12 System.out.println("name: " + name + ", year: " + year);
複製代碼
11  javassist.runtime 
12 import
引用包:
1 ClassPool pool = ClassPool.getDefault();
2 pool.importPackage("java.awt");
3 CtClass cc = pool.makeClass("Test");
4 CtField f = CtField.make("public Point p;", cc);
5 cc.addField(f);
13 限制
(1)不支持java5.0的新增語法。不支持註解修改,但可以通過底層的javassist類來解決,具體參考:javassist.bytecode.annotation
(2)不支持數組的初始化,如String[]{"1","2"},除非只有數組的容量爲1
(3)不支持內部類和匿名類
(4)不支持continue和btreak 表達式。
(5)對於繼承關係,有些不支持。例如
class A {} 
class B extends A {} 
class C extends B {} 
 
class X { 
    void foo(A a) { .. } 
    void foo(B b) { .. } 
}
如果調用  x.foo(new C()),可能會調用foo(A) 。
(6)推薦開發者用#分隔一個class name和static method或者 static field。例如:
javassist.CtClass.intType.getName()推薦用javassist.CtClass#intType.getName()
14.完整實例
14.1 創建類實例
複製代碼
 1 package com.swust.javassist;
 2 
 3 import javassist.ClassPool;
 4 import javassist.CtClass;
 5 import javassist.CtConstructor;
 6 import javassist.CtField;
 7 import javassist.CtMethod;
 8 
 9 public class Example1 {
10     public static void main(String[] args) throws Exception {  
11         ClassPool pool = ClassPool.getDefault();  
12         CtClass cc = pool.makeClass("bean.User");  
13           
14         //創建屬性  
15         CtField field01 = CtField.make("private int id;",cc);  
16         CtField field02 = CtField.make("private String name;", cc);  
17         cc.addField(field01);  
18         cc.addField(field02);  
19   
20         //創建方法  
21         CtMethod method01 = CtMethod.make("public String getName(){return name;}", cc);  
22         CtMethod method02 = CtMethod.make("public void setName(String name){this.name = name;}", cc);  
23         cc.addMethod(method01);  
24         cc.addMethod(method02);  
25           
26         //添加有參構造器  
27         CtConstructor constructor = new CtConstructor(new CtClass[]{CtClass.intType,pool.get("java.lang.String")},cc);  
28         constructor.setBody("{this.id=id;this.name=name;}");  
29         cc.addConstructor(constructor);  
30         //無參構造器  
31         CtConstructor cons = new CtConstructor(null,cc);  
32         cons.setBody("{}");  
33         cc.addConstructor(cons);  
34           
35         cc.writeFile("E:/workspace/TestCompiler/src");  
36     }  
37 }
複製代碼

14.2 訪問類實例變量

複製代碼
  1 package com.swust.javassist;
  2 
  3 import java.lang.reflect.Field;
  4 import java.lang.reflect.Method;
  5 import java.util.Arrays;
  6 
  7 import javassist.ClassPool;
  8 import javassist.CtClass;
  9 import javassist.CtConstructor;
 10 import javassist.CtField;
 11 import javassist.CtMethod;
 12 import javassist.CtNewMethod;
 13 import javassist.Modifier;
 14 
 15 public class Example2 {
 16         //獲取類的簡單信息  
 17         public static void test01() throws Exception{ 
 18             ClassPool pool = ClassPool.getDefault();  
 19             CtClass cc = pool.get("com.swust.beans.Person");  
 20             //得到字節碼  
 21             byte[] bytes = cc.toBytecode();  
 22             System.out.println(Arrays.toString(bytes));  
 23             System.out.println(cc.getName());//獲取類名  
 24             System.out.println(cc.getSimpleName());//獲取簡要類名  
 25             System.out.println(cc.getSuperclass());//獲取父類  
 26             System.out.println(cc.getInterfaces());//獲取接口  
 27             System.out.println(cc.getMethods());//獲取  
 28         }  
 29         //新生成一個方法  
 30         public static void test02() throws Exception{  
 31             ClassPool pool = ClassPool.getDefault();  
 32             CtClass cc = pool.get("com.swust.beans.Person");  
 33             //第一種  
 34             //CtMethod cm = CtMethod.make("public String getName(){return name;}", cc);  
 35             //第二種  
 36             //參數:返回值類型,方法名,參數,對象  
 37             CtMethod cm = new CtMethod(CtClass.intType,"add",new CtClass[]{CtClass.intType,CtClass.intType},cc);  
 38             cm.setModifiers(Modifier.PUBLIC);//訪問範圍  
 39             cm.setBody("{return $1+$2;}");  
 40             //cc.removeMethod(m) 刪除一個方法  
 41             cc.addMethod(cm);  
 42             //通過反射調用方法  
 43             Class clazz = cc.toClass();  
 44             Object obj = clazz.newInstance();//通過調用無參構造器,生成新的對象  
 45             Method m = clazz.getDeclaredMethod("add", int.class,int.class);  
 46             Object result = m.invoke(obj, 2,3);  
 47             System.out.println(result);  
 48         }  
 49           
 50         //修改已有的方法  
 51         public static void test03() throws Exception{  
 52             ClassPool pool  = ClassPool.getDefault();  
 53             CtClass cc = pool.get("bean.User");  
 54               
 55             CtMethod cm = cc.getDeclaredMethod("hello",new CtClass[]{pool.get("java.lang.String")});  
 56             cm.insertBefore("System.out.println(\"調用前\");");//調用前  
 57             cm.insertAt(29, "System.out.println(\"29\");");//行號  
 58             cm.insertAfter("System.out.println(\"調用後\");");//調用後  
 59               
 60             //通過反射調用方法  
 61             Class clazz = cc.toClass();  
 62             Object obj = clazz.newInstance();  
 63             Method m = clazz.getDeclaredMethod("hello", String.class);  
 64             Object result = m.invoke(obj, "張三");  
 65             System.out.println(result);       
 66         }  
 67           
 68         //修改已有屬性  
 69         public static void test04() throws Exception{  
 70             ClassPool pool  = ClassPool.getDefault();  
 71             CtClass cc = pool.get("bean.User");  
 72               
 73             //屬性  
 74             CtField cf = new CtField(CtClass.intType,"age",cc);  
 75             cf.setModifiers(Modifier.PRIVATE);  
 76             cc.addField(cf);  
 77             //增加響應的get set方法  
 78             cc.addMethod(CtNewMethod.getter("getAge",cf));  
 79             cc.addMethod(CtNewMethod.setter("setAge",cf));  
 80               
 81             //訪問屬性  
 82             Class clazz = cc.toClass();  
 83             Object obj = clazz.newInstance();         
 84             Field field = clazz.getDeclaredField("age");  
 85             System.out.println(field);  
 86             Method m = clazz.getDeclaredMethod("setAge", int.class);  
 87             m.invoke(obj, 16);  
 88             Method m2 = clazz.getDeclaredMethod("getAge", null);  
 89             Object resutl = m2.invoke(obj,null);          
 90             System.out.println(resutl);  
 91         }  
 92           
 93         //操作構造方法  
 94         public static void test05() throws Exception{  
 95             ClassPool pool = ClassPool.getDefault();  
 96             CtClass cc = pool.get("com.swust.beans.Person");  
 97               
 98             CtConstructor[] cons = cc.getConstructors();  
 99             for(CtConstructor con:cons){  
100                 System.out.println(con);  
101             }  
102         }  
103         public static void main(String[] args) throws Exception {  
104             test01();  
105             //test02();  
106             //test03();  
107             //test04();  
108             test05();  
109     }
110 }
複製代碼

調用方法1獲取類的基本信息,結果如下:

複製代碼
 1 完整類名爲:com.swust.beans.Person
 2 類名爲:Person
 3 父類名稱爲:java.lang.Object
 4 *****************************
 5 *****************************
 6 屬性方法爲:wait
 7 屬性方法爲:wait
 8 屬性方法爲:setName
 9 屬性方法爲:notifyAll
10 屬性方法爲:wait
11 屬性方法爲:toString
12 屬性方法爲:getName
13 屬性方法爲:setAge
14 屬性方法爲:equals
15 屬性方法爲:main
16 屬性方法爲:getAge
17 屬性方法爲:getClass
18 屬性方法爲:clone
19 屬性方法爲:finalize
20 屬性方法爲:hashCode
21 屬性方法爲:notify
複製代碼

調用方法2添加新方法:

1 方法執行結果爲:5
1 這是在原有方法體執行之前增加的內容
2 張三
3 這是在原有方法體執行之後增加的內容
4 null

調用方法4修改已有屬性:

1 增添的屬性爲:private int com.swust.beans.Person.age
2 getAge方法執行後的結果爲:16
3 增添的屬性爲:private int com.swust.beans.Person.height
4 getHeight方法執行後的結果爲:176

調用方法5操作構造函數:

1 javassist.CtConstructor@180cb01[public Person ()V]


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章