Dubbo Wrapper机制

​Wrapper,很多人从单词层面来解读,很容易理解成是 Java 包装类,或者是装饰器设计模式,其实都不是,它是 Dubbo 中的一种动态生成的代理类

举一个代码例子

package com.hstong.bssaccount.server.config.controller.wrapper;

import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.Adaptive;
import org.apache.dubbo.common.extension.SPI;


@SPI("boy")
public interface Hobby {

    void sing();

    @Adaptive
    void dance(URL url);
}
package com.hstong.bssaccount.server.config.controller.wrapper;

import org.apache.dubbo.common.URL;

public class StudentHobby implements Hobby {

    @Override
    public void sing() {
        System.out.println("这是student的 sing hobby");
    }

    @Override
    public void dance(URL url) {
        System.out.println("这是student的 dance hobby");
    }
}
package com.hstong.bssaccount.server.config.controller.wrapper;

import org.apache.dubbo.common.URL;

public class TeacherHobby implements Hobby {

    @Override
    public void sing() {
        System.out.println("这是teacher的 sing hobby");
    }

    @Override
    public void dance(URL url) {
        System.out.println("这是teacher 的 dance hobby");
    }

}
package com.hstong.bssaccount.server.config.controller.wrapper;

import org.apache.dubbo.common.URL;

public class HobbyWrapper implements Hobby {

    private Hobby hobby;

    public HobbyWrapper(Hobby hobby){
        this.hobby = hobby;
    }

    @Override
    public void sing() {
        System.out.println("增强----开始");
        hobby.sing();
        System.out.println("增强----结束");
    }

    @Override
    public void dance(URL url) {
        System.out.println("增强----开始");
        hobby.dance(url);
        System.out.println("增强----结束");
    }
}
    public static void main(String[] args) {
        ExtensionLoader loader = ExtensionLoader.getExtensionLoader(Hobby.class);
        Hobby hobby = loader.getAdaptiveExtension();
        URL url = URL.valueOf("xxx://localhost:8080/test");
        hobby.dance(url);
    }

增强----开始
这是student的 dance hobby
增强----结束

说到动态代理我们不难想到最常见的jdk动态代理和cglib动态代理,但Dubbo不选用这两种动态代理的原理有两个

1.JDK动态代理是通过反射创建对象并反射调用对象的方法,而 Dubbo 本身是一款追求高性能的调用框架,反射层面的各种耗时开销是不能容忍的,因此这是 JDK 代理的一个不足

2.Cglib 的这种方式,就像代理类的内部动态生成了一堆的 if…else 语句来调用被代理类的方法,避免了手工写各种 if…else 的硬编码逻辑,省去了不少硬编码的活。但是这么一来,如何生成动态代理类的逻辑就至关重要了,而且万一我们以后有自主定制的诉求,想修改这段生成代理类的这段逻辑,反而受 Cglib 库的牵制

我们来看看源码层面 Dubbo Wrapper机制是怎么生成代理类的


@Override
public  Invoker getInvoker(T proxy, Class type, URL url) {
    // 这里就是生成 Wrapper 代理对象的核心一行代码
    final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
    // 包装一个 Invoker 对象
    return new AbstractProxyInvoker(proxy, type, url) {
        @Override
        protected Object doInvoke(T proxy, String methodName,
                                  Class[] parameterTypes,
                                  Object[] arguments) throws Throwable {
            // 使用 wrapper 代理对象调用自己的 invokeMethod 方法
            // 以此来避免反射调用引起的性能开销
            return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
        }
    };
}

我们把生成的 wrapper 代理类 class 文件反编译为 Java 代码,看看生成的内容到底长什么样的


///
// Wrapper.getWrapper(demoFacade.getClass()) 
// 这句代码生成出来的 wrapper 代理对象,对应类的代码结构
///
package com.hmilyylimh.cloud.wrapper.demo;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import org.apache.dubbo.common.bytecode.NoSuchMethodException;
import org.apache.dubbo.common.bytecode.NoSuchPropertyException;
import org.apache.dubbo.common.bytecode.Wrapper;
import org.apache.dubbo.common.bytecode.ClassGenerator.DC;
// Dubbo 框架生成代理类的类名为 DemoFacadeImplDubboWrap0,
// 然后也继承了一个 Wrapper 对象,需要一个 invokeMethod 方法来统一调用
public class DemoFacadeImplDubboWrap0 extends Wrapper implements DC {
    public static String[] pns;
    public static Map pts;
    public static String[] mns;
    public static String[] dmns;
    public static Class[] mts0;
    public static Class[] mts1;
    public String[] getPropertyNames() { return pns; }
    public boolean hasProperty(String var1) { return pts.containsKey(var1); }
    public Class getPropertyType(String var1) { return (Class)pts.get(var1); }
    public String[] getMethodNames() { return mns; }
    public String[] getDeclaredMethodNames() { return dmns; }
    public void setPropertyValue(Object var1, String var2, Object var3) {
        try {
            DemoFacadeImpl var4 = (DemoFacadeImpl)var1;
        } catch (Throwable var6) {
            throw new IllegalArgumentException(var6);
        }
        throw new NoSuchPropertyException("Not found property \"" + var2 + "\" field or setter method in class com.hmilyylimh.cloud.wrapper.demo.DemoFacadeImpl.");
    }
    public Object getPropertyValue(Object var1, String var2) {
        try {
            DemoFacadeImpl var3 = (DemoFacadeImpl)var1;
        } catch (Throwable var5) {
            throw new IllegalArgumentException(var5);
        }
        throw new NoSuchPropertyException("Not found property \"" + var2 + "\" field or getter method in class com.hmilyylimh.cloud.wrapper.demo.DemoFacadeImpl.");
    }
    // !!!!!!!!!!!!!!!!!!!!!!!!!!!
    // 重点看这里,这才是调用的关键代码
    // 这里也动态生成了 if...else 代码
    // 然后通过强转调用源对象(被代理对象)的方法
    public Object invokeMethod(Object var1, String var2, Class[] var3, Object[] var4) throws InvocationTargetException {
        DemoFacadeImpl var5;
        try {
            var5 = (DemoFacadeImpl)var1;
        } catch (Throwable var8) {
            throw new IllegalArgumentException(var8);
        }
        try {
            if ("sayHello".equals(var2) && var3.length == 1) {
                return var5.sayHello((String)var4[0]);
            }
            if ("say".equals(var2) && var3.length == 0) {
                return var5.say();
            }
        } catch (Throwable var9) {
            throw new InvocationTargetException(var9);
        }
        throw new NoSuchMethodException("Not found method \"" + var2 + "\" in class com.hmilyylimh.cloud.wrapper.demo.DemoFacadeImpl.");
    }
    public DemoFacadeImplDubboWrap0() {
    }
}

可以看到其实生成的代理类又回到了最初朴实无华的if else代码,只不过dubbo实现了这个代码的生成模板,让我们再次对比一下调用耗时情况

正常调用耗时为:8 毫秒
反射调用耗时为:2019 毫秒
Wrapper调用耗时为:12 毫秒

总结:

综合两个代理的不足,Dubbo Wrapper 机制打造了一个迷你型的 Cglib 代理工具,而生成Wrapper动态代理需要以下三个步骤:

1)首先,想办法设计出一套代码模板,这套代码模板具备指定业务场景的通用性,这样才方便进行统一代理

2)然后,通过市场上的字节码工具,最终按照代码模板的要求生成出一套动态的代码
3)最后,将动态的代码通过 JDK 编译或者通过字节码工具,最终想办法生成 Class 对象,就可以拿着 Class 对象进行方法调用了

你可能感兴趣的:(dubbo)