1 問題的提出
相信對於每一個java程序員來說,早已經習慣了寫一個java文件 —> 編譯成class文件 —> 加載到JVM生成一個實例對象的開發流程。
但是有沒有想過其實沒有Java文件,也沒有編譯好的class文件,我們照樣可以向JVM中添加一個類實例呢? —》 javassist就可以完成這種騷操作。
而且讀過dubbo源碼的人肯定都知道,在dubbo框架裏很多地方用到了這種姿勢!!!
2 javassist動態生成實例對象
注意:
當你引了dubbo的dubbo-common包後,它會自動引入javassist包,如下:
寫一個Javassist動態生成實例對象的簡單demo —》 很簡單,相信每個人都可以很容易的就看懂,代碼如下:
/**
* javassist動態生成類示例
*/
@Test
public void createClassObjByJavassist() throws NotFoundException, CannotCompileException,
IllegalAccessException, InstantiationException, NoSuchMethodException,
InvocationTargetException {
// ClassPool:Class對象的容器
ClassPool pool = ClassPool.getDefault();
// 通過ClassPool生成一個public類
CtClass ctClass = pool.makeClass("com.enjoy.service.DemoImpl");
// 添加屬性 private String name
CtField nameFild = new CtField(pool.getCtClass("java.lang.String"), "name", ctClass);
nameFild.setModifiers(Modifier.PRIVATE);
ctClass.addField(nameFild);
// 添加屬性 private int age
CtField ageField = new CtField(pool.getCtClass("int"), "age", ctClass);
ageField.setModifiers(Modifier.PRIVATE);
ctClass.addField(ageField);
// 爲屬性name和age添加getXXX和setXXX方法
ctClass.addMethod(CtNewMethod.getter("getName", nameFild));
ctClass.addMethod(CtNewMethod.setter("setName", nameFild));
ctClass.addMethod(CtNewMethod.getter("getAge", ageField));
ctClass.addMethod(CtNewMethod.setter("setAge", ageField));
// 添加方法 void sayHello(String name) {...}
CtMethod ctMethod = new CtMethod(CtClass.voidType, "sayHello", new CtClass[]{}, ctClass);
// 方法設置爲PUBLIC
ctMethod.setModifiers(Modifier.PUBLIC);
// 方法體
ctMethod.setBody("{\nSystem.out.println(\"hello \" + getName() + \" !!!\");\n}");
ctClass.addMethod(ctMethod);
//生成class類
Class<?> clazz = ctClass.toClass();
//創建對象
Object obj = clazz.newInstance();
//反射 執行方法sayHello
obj.getClass().getMethod("setName", new Class[]{String.class})
.invoke(obj, new Object[]{"nrsc"});
obj.getClass().getMethod("sayHello", new Class[]{})
.invoke(obj, new Object[]{});
}
測試結果如下:
3 JavassistCompiler(dubbo對Javassist的封裝)動態生成實例對象
dubbo對Javassist進行封裝後搞了個JavassistCompiler對象,利用該對象可以將傳入的一個字符串,直接轉成java的實例對象,其使用姿勢如下:
/**
* 通過JavassistCompiler動態生成類示例
*/
@Test
public void createClassByJavassistCompiler()
throws IllegalAccessException, InstantiationException, NoSuchMethodException,
InvocationTargetException {
JavassistCompiler compiler = new JavassistCompiler();
//(1)第一個參數爲一個類組成的字符串
//(2)第二個參數爲一個加載器
//直接通過compiler方法就可以獲取字符串對應的類的class類型
Class<?> clazz = compiler.compile(
"public class DemoImpl implements DemoService { " +
"public String sayHello(String name) {" +
"System.out.println(\"hello \" + name); " +
"return \"Hello, \" + name ;" +
"}}",
this.getClass().getClassLoader());
//通過class類型創建實例對象
Object obj = clazz.newInstance();
//反射 執行方法sayHello
obj.getClass().getMethod("sayHello", new Class[]{String.class}).invoke(obj, "yoyo");
}
測試結果如下: