Dubbo使用javassist生成动态类

在服务(本地和远程)暴露的时候会调用proxyFactory.getInvoker方法

具体位置:

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

会先调用AOP织入的类StubProxyFactoryWrapper#getInvoker

然后执行JavassistProxyFactory#getInvoker

JavassistProxyFactory#getInvoker如下

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方法里面

例如现在暴露的服务如下:

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中反编译后的结果如下

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中进行服务暴露

还有一篇 dubbo中使用动态代理

posted @ 2019-06-11 23:53  复合式→展开式  阅读(1919)  评论(0编辑  收藏  举报