類增強,也叫字節碼增強,用於在運行時動態修改字節碼,以便增加額外的方法、字段以及方法體內容等。Play中很多插件都包含用於在運行時更改應用實現類的增強器,比如爲控制器類增加額外的方法。這便是Play不可思議的地方,也是非常核心的概念,掌握底層實現不可或缺的知識。
內置的play.CorePlugin使用play.classloading.enhancers包提供的類增強器,爲應用實現類動態增加代碼,主要有以下幾種:
- ContinuationEnhancer爲控制器類增加continuations支持。
- ControllersEnhancer爲控制器中的action方法增加線程安全功能,併爲方法調用提供HTTP重定向支持。
- LocalvariablesNamesEnhancer用於跟蹤控制器中的本地變量
- MailerEnhancer構建play.mvc.Mailer子類
- PropertiesEnhancer將所有應用類都裝入有效的JavaBean,所有應用類相關的屬性都注入到JavaBean字段中。
- SigEnhancer爲每個類的簽名生成唯一的hash值,從而實現自動重載。
除此以外,play.db.jpa.JPAPlugin動態增強play.db.jpa.JPABase的子類,實現更爲方便的JPA查詢方法。這些通常都會應用到繼承於play.db.jpa.Model的實體類中。上面提到的JPA查詢方法都定義在play.db.jpa.GenericModel類中。
如果需要自定義類增強器,只需要在插件的enhance(ApplicationClass)方法中使用play.classloading.enhancers.Enhancer類即可。
Play底層使用Javassist庫來實現類增強,下面舉一個例子來說明如何在運行時動態爲class增加其它功能:
package com.example;
public class TestClazz {
public void testMethod(){
//do noting;
}
}
類增強的寫法!這種方式,直接在字節碼中插入函數或方法,所以速度也非常快!package com.example;
import java.lang.reflect.Method;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.Modifier;
public class MyTest {
public static void main(String[] args) {
try {
CtClass ctClass=ClassPool.getDefault().get("com.example.TestClazz");
CtMethod testMethod=ctClass.getDeclaredMethod("testMethod");
testMethod.setBody("{ System.out.println(\"Test Method\"); }");
CtMethod newMethod=new CtMethod(CtClass.voidType,"sayHello",new CtClass[]{},ctClass);
newMethod.setModifiers(Modifier.PUBLIC);
StringBuffer body=new StringBuffer();
body.append("{\n System.out.println(\"hello\");");
body.append("\n return ;");
body.append("\n}");
newMethod.setBody(body.toString());
ctClass.addMethod(newMethod);
ctClass.writeFile("bin");
TestClazz testClazz=new TestClazz();
testClazz.testMethod();
Method method=testClazz.getClass().getMethod("sayHello", new Class[]{});
method.invoke(testClazz, new Object[]{});
} catch (Exception e) {
e.printStackTrace();
}
}
}