本文基于 Dubbo 2.6.1 版本,望知悉。
1. 概述
本文接 《精尽 Dubbo 源码分析 —— 动态代理(二)之 JDK》 一文,分享使用 Dubbo 本地存根( Stub )的特性。😁 当然,从标题我们就可以看出,实现的原理是基于动态代理的机制。
在 《Dubbo 用户指南 —— 本地存根》 中,已经非常详尽的分享了本地存根的概念和使用,本文就不重复介绍啦。😈 文档有一点点小小的错误,在 Spring 配置文件的配置方式应该是如下:
<dubbo:reference interface="com.alibaba.dubbo.demo.DemoService" stub="com.alibaba.dubbo.demo.consumer.DemoServiceStub">
|
- 是服务引用
<dubbo:reference />
,而不是服务暴露 <dubbo:service />
2. StubProxyFactoryWrapper
com.alibaba.dubbo.rpc.proxy.wrapper.StubProxyFactoryWrapper
,实现 ProxyFactory 接口,存根代理工厂包装器实现类。
2.1 构造方法
private final ProxyFactory proxyFactory;
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 创建代理的流程,实际变成如下:
流程
protocol
属性,Protocol$Adaptive 对象。
2.2 getProxy
1: @Override 2: @SuppressWarnings({"unchecked", "rawtypes"}) 3: public <T> T getProxy(Invoker<T> invoker) throws RpcException { 4: 5: T proxy = proxyFactory.getProxy(invoker); 6: if (GenericService.class != invoker.getInterface()) { 7: 8: String stub = invoker.getUrl().getParameter(Constants.STUB_KEY, invoker.getUrl().getParameter(Constants.LOCAL_KEY)); 9: if (ConfigUtils.isNotEmpty(stub)) { 10: Class<?> serviceType = invoker.getInterface(); 11: 12: if (ConfigUtils.isDefault(stub)) { 13: if (invoker.getUrl().hasParameter(Constants.STUB_KEY)) { 14: stub = serviceType.getName() + "Stub"; 15: } else { 16: stub = serviceType.getName() + "Local"; 17: } 18: } 19: try { 20: 21: Class<?> stubClass = ReflectUtils.forName(stub); 22: if (!serviceType.isAssignableFrom(stubClass)) { 23: throw new IllegalStateException("The stub implementation class " + stubClass.getName() + " not implement interface " + serviceType.getName()); 24: } 25: try { 26: 27: Constructor<?> constructor = ReflectUtils.findConstructor(stubClass, serviceType); 28: proxy = (T) constructor.newInstance(new Object[]{proxy}); 29: 30: 31: 32: URL url = invoker.getUrl(); 33: if (url.getParameter(Constants.STUB_EVENT_KEY, Constants.DEFAULT_STUB_EVENT)) { 34: url = url.addParameter(Constants.STUB_EVENT_METHODS_KEY, StringUtils.join(Wrapper.getWrapper(proxy.getClass()).getDeclaredMethodNames(), ",")); 35: url = url.addParameter(Constants.IS_SERVER_KEY, Boolean.FALSE.toString()); 36: try { 37: export(proxy, (Class) invoker.getInterface(), url); 38: } catch (Exception e) { 39: LOGGER.error("export a stub service error.", e); 40: } 41: } 42: } catch (NoSuchMethodException e) { 43: throw new IllegalStateException("No such constructor \"public " + stubClass.getSimpleName() + "(" + serviceType.getName() + ")\" in stub implementation class " + stubClass.getName(), e); 44: } 45: } catch (Throwable t) { 46: LOGGER.error("Failed to create stub implementation class " + stub + " in consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", cause: " + t.getMessage(), t); 47: 48: } 49: } 50: } 51: return proxy; 52: }
|
- 【第一步】第 5 行:调用
proxyFactory#getProxy(invoker)
方法,获得 Service Proxy 对象。这个过程,就是我们在 《精尽 Dubbo 源码解析 —— 动态代理》 前两篇看到的内容。
- 第 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
@Override public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException { return proxyFactory.getInvoker(proxy, type, url); }
|
- 服务实现的 Service ,不支持 Stub 存根。所以,虽然
<dubbo:service />
有 stub
配置项,但是实际是没有效果的。
3. 校验 Stub 配置
在 #checkStubAndMock(interfaceClass)
方法中,有校验 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()); } }
if (ConfigUtils.isNotEmpty(stub)) { Class<?> localClass = ConfigUtils.isDefault(stub) ? ReflectUtils.forName(interfaceClass.getName() + "Stub") : ReflectUtils.forName(stub); 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()); } }
|
666. 彩蛋
知识星球
🙂 小文一篇。