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 对象进行方法调用了