文章

动态代理(三)之本地存根Stub

动态代理(三)之本地存根Stub

本文基于 Dubbo 2.6.1 版本,望知悉。

1. 概述

本文接 《精尽 Dubbo 源码分析 —— 动态代理(二)之 JDK》 一文,分享使用 Dubbo 本地存根( Stub )的特性。当然,从标题我们就可以看出,实现的原理是基于动态代理的机制。

《Dubbo 用户指南 —— 本地存根》 中,已经非常详尽的分享了本地存根的概念和使用,本文就不重复介绍啦。文档有一点点小小的错误,在 Spring 配置文件的配置方式应该是如下:

1
<dubbo:reference interface="com.alibaba.dubbo.demo.DemoService" stub="com.alibaba.dubbo.demo.consumer.DemoServiceStub">

是服务引用,而不是服务暴露。

2. StubProxyFactoryWrapper

com.alibaba.dubbo.rpc.proxy.wrapper.StubProxyFactoryWrapper ,实现 ProxyFactory 接口,存根代理工厂包装器实现类。

2.1 构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
 * ProxyFactory$Adaptive 对象
 */
private final ProxyFactory proxyFactory;
/**
 * Protocol$Adaptive 对象
 */
private Protocol protocol;

public StubProxyFactoryWrapper(ProxyFactory proxyFactory) {
    this.proxyFactory = proxyFactory;
}

public void setProtocol(Protocol protocol) {
    this.protocol = protocol;
}
  • proxyFactory 属性,ProxyFactory$Adaptive 对象。StubProxyFactoryWrapper 基于 Dubbo SPI Wrapper 机制,所以使用 ProxyFactory 创建代理的流程,实际变成如下:

StubProxyFactoryWrapper流程图

流程

  • protocol 属性,Protocol$Adaptive 对象。

2.2 getProxy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
@Override
@SuppressWarnings({"unchecked", "rawtypes"})
public <T> T getProxy(Invoker<T> invoker) throws RpcException {
    // 获得 Service Proxy 对象
    T proxy = proxyFactory.getProxy(invoker);
    if (GenericService.class != invoker.getInterface()) { // 非泛化引用
        // 获得 `stub` 配置项
        String stub = invoker.getUrl().getParameter(Constants.STUB_KEY, invoker.getUrl().getParameter(Constants.LOCAL_KEY));
        if (ConfigUtils.isNotEmpty(stub)) {
            Class<?> serviceType = invoker.getInterface();
            // `stub = true` 的情况,使用接口 + `Stub` 字符串。
            if (ConfigUtils.isDefault(stub)) {
                if (invoker.getUrl().hasParameter(Constants.STUB_KEY)) {
                    stub = serviceType.getName() + "Stub";
                } else {
                    stub = serviceType.getName() + "Local";
                }
            }
            try {
                // 加载 Stub 类
                Class<?> stubClass = ReflectUtils.forName(stub);
                if (!serviceType.isAssignableFrom(stubClass)) {
                    throw new IllegalStateException("The stub implementation class " + stubClass.getName() + " not implement interface " + serviceType.getName());
                }
                try {
                    // 创建 Stub 对象,使用带 Service Proxy 对象的构造方法
                    Constructor<?> constructor = ReflectUtils.findConstructor(stubClass, serviceType);
                    proxy = (T) constructor.newInstance(new Object[]{proxy});

                    // 【TODO 8033】参数回调
                    //export stub service
                    URL url = invoker.getUrl();
                    if (url.getParameter(Constants.STUB_EVENT_KEY, Constants.DEFAULT_STUB_EVENT)) {
                        url = url.addParameter(Constants.STUB_EVENT_METHODS_KEY, StringUtils.join(Wrapper.getWrapper(proxy.getClass()).getDeclaredMethodNames(), ","));
                        url = url.addParameter(Constants.IS_SERVER_KEY, Boolean.FALSE.toString());
                        try {
                            export(proxy, (Class) invoker.getInterface(), url);
                        } catch (Exception e) {
                            LOGGER.error("export a stub service error.", e);
                        }
                    }
                } catch (NoSuchMethodException e) {
                    throw new IllegalStateException("No such constructor \"public " + stubClass.getSimpleName() + "(" + serviceType.getName() + ")\" in stub implementation class " + stubClass.getName(), e);
                }
            } catch (Throwable t) {
                LOGGER.error("Failed to create stub implementation class " + stub + " in consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", cause: " + t.getMessage(), t);
                // ignore
            }
        }
    }
    return proxy;
}
  • 第一步:第 5 行:调用 proxyFactory#getProxy(invoker) 方法,获得 Service Proxy 对象。这个过程,就是我们在前两篇看到的内容。
  • 第 6 行:若是泛化引用,不支持使用本地存根。
  • 第 8 至 18 行:获得 stub 配置项。注意,local 配置项和 stub 配置项是等价的,目前使用 stub 而不使用 local。
  • 第 20 至 24 行:调用 ReflectUtils#forName(stub) 方法,加载 Stub 类。
  • 第二步:第 26 至 28 行:创建 Stub 对象,使用带 Service Proxy 对象作为参数的构造方法包装在内部。例如,public DemoServiceStub(DemoService demoService)。通过这样的方式,我们的 Stub 对象,就将 Proxy Service 对象包装在内部,可以实现各种 OOXX 啦。
  • 第 30 至 41 行:【TODO 8033】参数回调。先无视。
  • 第 51 行:返回最终的 proxy。

2.3 getInvoker

1
2
3
4
@Override
public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException {
    return proxyFactory.getInvoker(proxy, type, url);
}
  • 服务实现的 Service ,不支持 Stub 存根。所以,虽然有 stub 配置项,但是实际是没有效果的。

3. 校验 Stub 配置

#checkStubAndMock(interfaceClass) 方法中,有校验 stub 配置项的代码,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// `local` 配置项的校验,和 `stub` 一样。
if (ConfigUtils.isNotEmpty(local)) {
    Class<?> localClass = ConfigUtils.isDefault(local) ? ReflectUtils.forName(interfaceClass.getName() + "Local") : ReflectUtils.forName(local);
    if (!interfaceClass.isAssignableFrom(localClass)) {
        throw new IllegalStateException("The local implementation class " + localClass.getName() + " not implement interface " + interfaceClass.getName());
    }
    try {
        ReflectUtils.findConstructor(localClass, interfaceClass);
    } catch (NoSuchMethodException e) {
        throw new IllegalStateException("No such constructor \"public " + localClass.getSimpleName() + "(" + interfaceClass.getName() + ")\" in local implementation class " + localClass.getName());
    }
}
// `stub` 配置项的校验
if (ConfigUtils.isNotEmpty(stub)) {
    // `stub = true` 的情况,使用接口 + `Stub` 字符串。
    Class<?> localClass = ConfigUtils.isDefault(stub) ? ReflectUtils.forName(interfaceClass.getName() + "Stub") : ReflectUtils.forName(stub);
    // Stub 类,必须实现服务接口
    if (!interfaceClass.isAssignableFrom(localClass)) {
        throw new IllegalStateException("The local implementation class " + localClass.getName() + " not implement interface " + interfaceClass.getName());
    }
    // Stub 类,必须带有服务接口的构造方法
    try {
        ReflectUtils.findConstructor(localClass, interfaceClass);
    } catch (NoSuchMethodException e) {
        throw new IllegalStateException("No such constructor \"public " + localClass.getSimpleName() + "(" + interfaceClass.getName() + ")\" in local implementation class " + localClass.getName());
    }
}

666. 彩蛋

知识星球

知识星球

小文一篇。

本文由作者按照 CC BY 4.0 进行授权