原文地址:www.tianshouzhi.com/api/tutoria…java
Javassist是一個開源的分析、編輯和建立Java字節碼的類庫。是由東京工業大學的數學和計算機科學系的 Shigeru Chiba (千葉 滋)所建立的。它已加入了開放源代碼JBoss 應用服務器項目,經過使用Javassist對字節碼操做爲JBoss實現動態"AOP"框架。shell
經過Javassist,咱們能夠:api
假設咱們須要生成一個Student
類,代碼以下:服務器
public class Student {
private String name;
private Integer age;
public Student() {
}
public Student(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String display(String desc) {
return this.name + "," + this.age + "," + desc + ".";
}
}
複製代碼
javassist
建立代碼以下:框架
String clazzName = "Student";
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.makeClass(clazzName);
// member field,setter and getter
CtField nameField = new CtField(classPool.get(String.class.getName()), "name", ctClass);
nameField.setModifiers(Modifier.PRIVATE);
CtField ageField = new CtField(classPool.get(Integer.class.getName()), "age", ctClass);
ageField.setModifiers(Modifier.PRIVATE);
ctClass.addMethod(CtNewMethod.setter("setName", nameField));
ctClass.addMethod(CtNewMethod.setter("getName", nameField));
ctClass.addField(nameField);
ctClass.addMethod(CtNewMethod.setter("setAge", ageField));
ctClass.addMethod(CtNewMethod.setter("getAge", ageField));
ctClass.addField(ageField);
// constructor
CtConstructor constructor = new CtConstructor(
null,
ctClass);
constructor.setBody("{}");
ctClass.addConstructor(constructor);
constructor = new CtConstructor(
new CtClass[]{
classPool.get(String.class.getName()),
classPool.get(Integer.class.getName())},
ctClass);
constructor.setBody("{ $0.name = $1; $0.age = $2; }");
ctClass.addConstructor(constructor);
// display method
CtMethod displayCtMethod = new CtMethod(
classPool.get(String.class.getName()),
"display",
new CtClass[]{
classPool.get(String.class.getName())
},
ctClass
);
displayCtMethod.setModifiers(Modifier.PUBLIC);
String displayMethodBody = "{ return $0.name+\"(\"+$0.age +\"), \"+$1+\".\";}";
displayCtMethod.setBody(displayMethodBody);
ctClass.addMethod(displayCtMethod);
// generator class
Class<?> c = ctClass.toClass();
Object student = c.getConstructor(String.class, Integer.class)
.newInstance("niumi", 20);
Method method = student.getClass().getMethod("display", String.class);
String result = (String) method.invoke(student, "is a good boy");
System.out.println(result);
複製代碼
運行程序後輸出:ide
niumi(20), is a good boy.
複製代碼
想統計Student
的display
方法的耗時時間。最簡單的思路是使用Student
修改display
方法的源碼:this
public String display(String desc) {
//記錄方法調用的開始時間
long start = System.currentTimeMillis();
try {
Thread.sleep(2000l);
String result = this.name + "," + this.age + "," + desc + ".";
//方法結束時打印耗時
System.out.println("耗時:" + (System.currentTimeMillis() - start) + "ms");
return result;
} catch (InterruptedException e) {
return "";
}
}
複製代碼
javassist
的CtClass方法提供的insertBefore
和insertAfter
方法,容許咱們在一個方法開始和結束添加本身的代碼。spa
String className = "Student";
String methodName = "display";
ClassPool classPool = ClassPool.getDefault();
CtClass clazz = classPool.get(className);
CtMethod displayMethod = clazz.getDeclaredMethod(methodName);
displayMethod.insertBefore("long start=System.currentTimeMillis();");
displayMethod.insertAfter("System.out.println(\"耗時:\"+(System.currentTimeMillis()-start)+\"ms\");");
Student student = (Student) clazz.toClass().newInstance();
student.display("is a good boy");
複製代碼
運行程序會報以下錯誤:開放源代碼
由於javassist
插入的代碼片斷中,每次插入操做的代碼,稱之爲一個插入代碼塊,後面的插入塊不能使用前面的插入塊定義的局部變量,而言且也不能使用方法中的原有局部變量。而上述代碼中,咱們分表調用了insertBefore
和insertAfter
插入了兩個代碼塊,然後面的插入塊不能使用前面的插入塊定義的局部變量start
,所以報錯。code
若是代碼片斷都位於一個插入塊中,則局部變量是能夠引用的。所以考慮使用以下的方法實現: 將原有的display
方法名改成display$impl
,而後再定義一個display
方法,新的display
方法內部會調用display$impl
,在調用以前和調用以後分別加入上述的代碼片斷。
String className = "Student";
String methodName = "display";
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.get(className);
CtMethod method = ctClass.getDeclaredMethod(methodName);
String newName = methodName + "$impl";
method.setName(newName);
CtMethod newMethod = CtNewMethod.make("public String " + methodName + "(String desc){" +
"long start=System.currentTimeMillis();" +
"String data = " + newName + "(desc);" +
"System.out.println(\"耗時:\"+(System.currentTimeMillis() - start)+\"ms\");" +
"return data;" +
"}", ctClass);
ctClass.addMethod(newMethod);
Constructor<?> constructor = ctClass.toClass()
.getDeclaredConstructor(String.class,Integer.class);
Student student = (Student) constructor.newInstance("niumi",20);
student.display("is a good boy");
複製代碼
此外還有一種更加簡單的方式:
ProxyFactory factory=new ProxyFactory();
factory.setSuperclass(Student.class);
Class<ProxyObject> proxyClass = factory.createClass();
Student student = (Student) proxyClass.newInstance();
//設置攔截處理
((ProxyObject)student).setHandler(new MethodHandler() {
@Override
public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable {
long start=System.currentTimeMillis();
Object result = proceed.invoke(self, args);
System.out.println("耗時:"+(System.currentTimeMillis()-start)+"ms");
return result;
}
});
student.display("is a good boy");
複製代碼