【dubbo源碼解析】--- 通過javassist/JavassistCompiler動態生成一個實例對象

本文對應源碼地址:https://github.com/nieandsun/dubbo-study


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");
}

測試結果如下:
在這裏插入圖片描述

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