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<Hobby> 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 <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
    // 这里就是生成 Wrapper 代理对象的核心一行代码
    final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
    // 包装一个 Invoker 对象
    return new AbstractProxyInvoker<T>(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 对象进行方法调用了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本系统的研发具有重大的意义,在安全性方面,用户使用浏览器访问网站时,采用注册和密码等相关的保护措施,提高系统的可靠性,维护用户的个人信息和财产的安全。在方便性方面,促进了校园失物招领网站的信息化建设,极大的方便了相关的工作人员对校园失物招领网站信息进行管理。 本系统主要通过使用Java语言编码设计系统功能,MySQL数据库管理数据,AJAX技术设计简洁的、友好的网址页面,然后在IDEA开发平台中,编写相关的Java代码文件,接着通过连接语言完成与数据库的搭建工作,再通过平台提供的Tomcat插件完成信息的交互,最后在浏览器中打开系统网址便可使用本系统。本系统的使用角色可以被分为用户和管理员,用户具有注册、查看信息、留言信息等功能,管理员具有修改用户信息,发布寻物启事等功能。 管理员可以选择任一浏览器打开网址,输入信息无误后,以管理员的身份行使相关的管理权限。管理员可以通过选择失物招领管理,管理相关的失物招领信息记录,比如进行查看失物招领信息标题,修改失物招领信息来源等操作。管理员可以通过选择公告管理,管理相关的公告信息记录,比如进行查看公告详情,删除错误的公告信息,发布公告等操作。管理员可以通过选择公告类型管理,管理相关的公告类型信息,比如查看所有公告类型,删除无用公告类型,修改公告类型,添加公告类型等操作。寻物启事管理页面,此页面提供给管理员的功能有:新增寻物启事,修改寻物启事,删除寻物启事。物品类型管理页面,此页面提供给管理员的功能有:新增物品类型,修改物品类型,删除物品类型。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值