Dubbo使用javassist生成動態類

Dubbo使用javassist生成動態類

在服務(本地和遠程)暴露的時候會調用proxyFactory.getInvoker方法java

具體位置:app

  • 本地暴露:ServiceConfig#exportLocal line:538
  • 遠程暴露: ServiceConfig#doExportUrlsFor1Protocol line:512

會先調用AOP織入的類StubProxyFactoryWrapper#getInvokeride

而後執行JavassistProxyFactory#getInvoker函數

JavassistProxyFactory#getInvoker以下this

public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
    // getWrapper會生成代理類
    final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
    return new AbstractProxyInvoker<T>(proxy, type, url) {
        @Override
        protected Object doInvoke(T proxy, String methodName,
                                  Class<?>[] parameterTypes,
                                  Object[] arguments) throws Throwable {
            return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
        }
    };
}

而後進入Wrapper#getWrapper--> Wrapper#makeWrapper, 具體代碼就在這個makeWrapper方法裏面url

例如如今暴露的服務以下:debug

public interface TestService {

    String getData(String var1);

    List<String> getList();
}

那麼生成的代理類以下:代理

public class Wrapper0 extends com.alibaba.dubbo.common.bytecode.Wrapper {

    /**
     * 屬性名, 屬性類型
     */
    public static java.util.Map pts = new HashMap<String, Class<?>>();
    public static String[] pns = new String[0];

    /**
     * 全部的方法名
     */
    public static String[] mns = {"getData"};
    /**
     * 本類中的全部方法名
     */
    public static String[] dmns = {"getData"};

    /**
     * 一個方法中全部的參數類型  mts[n]屬性的個數和方法的個數形同
     */
    public static Class[] mts0 = {String.class};

    public static Class[] mts1 = {List.class};

    @Override
    public String[] getPropertyNames() {
        return pns;
    }

    @Override
    public boolean hasProperty(String n) {
        return pts.containsKey(n);
    }

    @Override
    public Class getPropertyType(String n) {
        return (Class) pts.get(n);
    }

    @Override
    public String[] getMethodNames() {
        return mns;
    }

    @Override
    public String[] getDeclaredMethodNames() {
        return dmns;
    }

    @Override
    public void setPropertyValue(Object o, String n, Object v) {
        per.qiao.service.TestService w;
        try {
            w = ((per.qiao.service.TestService) o);
        } catch (Throwable e) {
            throw new IllegalArgumentException(e);
        }
        throw new com.alibaba.dubbo.common.bytecode.NoSuchPropertyException("Not found property \"" + n + "\" filed or setter method in class per.qiao.service.TestService.");
    }

    @Override
    public Object getPropertyValue(Object o, String n) {
        per.qiao.service.TestService w;
        try {
            w = ((per.qiao.service.TestService) o);
        } catch (Throwable e) {
            throw new IllegalArgumentException(e);
        }
        if (n.equals("list")) {
            return w.getList();
        }
        throw new com.alibaba.dubbo.common.bytecode.NoSuchPropertyException("Not found property \"" + n + "\" filed or setter method in class per.qiao.service.TestService.");
    }
    
    /**
     *  在調用接口時,就時調用的這個方法
        @param o 接口實例
        @param n 方法名
        @param p 參數類型
        @param v 參數
     */
    @Override
    public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws java.lang.reflect.InvocationTargetException {
        per.qiao.service.TestService w;
        try {
            w = ((per.qiao.service.TestService) o);
        } catch (Throwable e) {
            throw new IllegalArgumentException(e);
        }
        try {
            //這個try範圍內就是你所須要暴露的全部方法
            if ("getData".equals(n) && p.length == 1) {
                return w.getData((java.lang.String) v[0]);
            }
            if ("getList".equals(n) && p.length == 0) {
                return w.getList();
            }
        } catch (Throwable e) {
            throw new java.lang.reflect.InvocationTargetException(e);
        }
        throw new com.alibaba.dubbo.common.bytecode.NoSuchMethodException("Not found method \"" + n + "\" in class per.qiao.service.TestService.");
    }
}

javassist生成動態代理類的示例

public class CompilerByJavassist {

    public static void main(String[] args) throws Exception {
        // ClassPool:CtClass對象的容器
        ClassPool pool = ClassPool.getDefault();

        // 經過ClassPool生成一個public新類Emp.java
        CtClass ctClass = pool.makeClass("per.qiao.javassist.Qiao");

        // 添加屬性 private String name
        CtField nameFild = new CtField(pool.getCtClass("java.lang.String"), "name", ctClass);
        nameFild.setModifiers(Modifier.PRIVATE);
        ctClass.addField(nameFild);

        // 其次添加熟悉privtae 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));

        // 添加構造函數
        CtConstructor ctConstructor = new CtConstructor(new CtClass[] {}, ctClass);
        // 爲構造函數設置函數體
        StringBuffer buffer = new StringBuffer();
        buffer.append("{\n").append("name=\"qiaogege\";\n").append("age=25;\n}");
        ctConstructor.setBody(buffer.toString());
        // 把構造函數添加到新的類中
        ctClass.addConstructor(ctConstructor);


        // 添加自定義方法  public void printInfo {...}
        CtMethod ctMethod = new CtMethod(CtClass.voidType, "printInfo", new CtClass[] {}, ctClass);
        // 爲自定義方法設置修飾符
        ctMethod.setModifiers(Modifier.PUBLIC);
        // 爲自定義方法設置函數體
        StringBuffer buffer2 = new StringBuffer();
        buffer2.append("{\nSystem.out.println(\"begin!\");\n")
                .append("System.out.println(name);\n")
                .append("System.out.println(age);\n")
                .append("System.out.println(\"over!\");\n").append("}");
        ctMethod.setBody(buffer2.toString());
        ctClass.addMethod(ctMethod);


        //最好生成一個class
        Class<?> clazz = ctClass.toClass();
        Object obj = clazz.newInstance();
        //ctClass.debugWriteFile("E://Qiao.class");

        //反射 執行方法
        obj.getClass().getMethod("printInfo", new Class[] {})
                .invoke(obj, new Object[] {});

        ctClass.debugWriteFile("E://Emp.class");
        // 把生成的class文件寫入文件
        byte[] byteArr = ctClass.toBytecode();
        FileOutputStream fos = new FileOutputStream(new File("E://Qiao.class"));
        fos.write(byteArr);
        fos.close();
    }

}

生成的Class文件放入IDEA中反編譯後的結果以下code

public class Qiao {
    private String name = "qiaogege";
    private int age = 25;

    public String getName() {
        return this.name;
    }

    public void setName(String var1) {
        this.name = var1;
    }

    public int getAge() {
        return this.age;
    }

    public void setAge(int var1) {
        this.age = var1;
    }

    public Qiao() {
    }

    public void printInfo() {
        System.out.println("begin!");
        System.out.println(this.name);
        System.out.println(this.age);
        System.out.println("over!");
    }
}

小結:對象

1. Dubbo經過javassist動態生成一個代理類對象,該對象不一樣於普通的javassist生成的對象,而是隻記錄了暴露接口中的方法的相關參數,生成一個Wrapper類型的對象,並保存在WRAPPER_MAP中,經過invokeMethod方法來執行相應的方法
2. 再將生成的Wrapper對象包裝在AbstractProxyInvoker中進行服務暴露
相關文章
相關標籤/搜索