Java学习之javassist
javassist可以實現動態編程,即動態生成class文件,或者操作class文件,下面就詳細介紹。
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 方法執行結果為:51 這是在原有方法體執行之前增加的內容
2 張三
3 這是在原有方法體執行之后增加的內容
4 null
調用方法4修改已有屬性:
1 增添的屬性為:private int com.s
wust.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]from:?https://www.cnblogs.com/sunfie/p/5154246.html?
總結
以上是生活随笔為你收集整理的Java学习之javassist的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java-- String源码分析
- 下一篇: Java 网络IO编程总结(BIO、NI