06.消费者consumer侧 reference bean生成逻辑
client侧demo
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/com/code260/ss/dubbo/demov/client/conf/registercenter/zookeeper/applicationContext.xml")
public class HelloClientTest
{
@Autowired
private HelloService helloService;
@Test
public void testSayHello()
{
this.helloService.sayHello("Simon");
}
}
<dubbo:reference id="helloService" interface="com.code260.ss.dubbo.demov.facade.service.HelloService" timeout="3000000"/>
我们通过dubbo:reference配置了一个HelloService的实现bean,这样我们在使用时就能通过Autowired直接使用helloService字段了,那么dubbo:reference是如何实现的?
先描述下整体流程:
graph TD
A[自定义spring配置标签-格式结构-]-->B[DubboNamespaceHandler对接spring自定义namespace注册标签解析器]
B-->C[定义DubboBeanDefinitionParser解析处理那些标签]
C-->D[ReferenceBean getobject 对接spring的FactoryBean接口]
D-->E[ReferenceConfig createProxy]
E-->F[组装配置上下文url-含注册中心信息-具体的协议实现refer出Invoker实现<br>默认是DubboProtocolrefer出DubboInvoker]
F-->G[JavassistProxyFactory把上步的Invoker实例wrap起来生成消费的proxy]
DubboNamespaceHandler
dubbo:reference在spring中是一个自定义扩展标签。既然是自定义扩展,那么我们就要明白spring是如何开放了其自定义标签扩展机制的?或者说我要是想自定义一个标签该怎么和spring对接?
在dubbo-config-spring模块的/src/main/resources/META-INF/下发现了spring.handlers文件,其中内容如下
http\://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
同级目录的还有dubbo.xsd,spring.schemas文件。dubbo.xsd定义了这些自定义扩展标签的格式(结构)。
阅读DubboNamespaceHandler代码。是org.springframework.beans.factory.xml.NamespaceHandlerSupport的子类。遵循spring的对接规定嘛。
其主要干的事,就是向spring注册自定义的标签解析器:
public void init() {
registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(AnnotationBean.class, true));
}
dubbo对所有标签都用了DubboBeanDefinitionParser这个一个类处理。
DubboBeanDefinitionParser
DubboBeanDefinitionParser实现了org.springframework.beans.factory.xml.BeanDefinitionParser,构建(返回)BeanDefinition。
对于consumer侧reference bean在spring的BeanDefinition中的class是com.alibaba.dubbo.config.spring.ReferenceBean
dubbo在ReferenceBean的getObject中完成consumer侧真正消费者服务实例的创建。因为ReferenceBean也实现了spring的FactoryBean接口。
最终通过com.alibaba.dubbo.config.ReferenceConfig.createProxy(Map<String, String>)完成consumer的HelloService实例的创建。从ReferenceBean到ReferenceConfig.createProxy之间的调用栈如下:
ReferenceBean<T>(ReferenceConfig<T>).createProxy(Map<String,String>) line: 338
ReferenceBean<T>(ReferenceConfig<T>).init() line: 300
ReferenceBean<T>(ReferenceConfig<T>).get() line: 138
ReferenceBean<T>.getObject() line: 65
DefaultListableBeanFactory(FactoryBeanRegistrySupport).doGetObjectFromFactoryBean(FactoryBean<?>, String) line: 168
ReferenceConfig.createProxy分析
createProxy方法的主要流程:
graph TD
A(1.判断是哪种协议:InJVm,指定url直连,rpc-default-dubbo)-->B[2.组装出url]
B-->C[3.用对应的Protocol扩展实现结合url推出Invoker实例]
C-->D[4.调用proxyFactory.getProxy创建代理的reference bean]
第二步如果是InJvm,则处理代码如下:
URL url = new URL(Constants.LOCAL_PROTOCOL, NetUtils.LOCALHOST, 0, interfaceClass.getName()).addParameters(map);// 直接new的URL的protocol是InJvm,然后下一步refer
invoker = refprotocol.refer(interfaceClass, url);
第二步如果是默认的走zookeeper注册中心这种,类似我们demo那种,url组出来的样子大概如下:
registry://127.0.0.1:2182/com.alibaba.dubbo.registry.RegistryService?application=consumer-of-helloworld-app&dubbo=2.0.0&pid=42451&refer=application%3Dconsumer-of-helloworld-app%26dubbo%3D2.0.0%26interface%3Dcom.code260.ss.dubbo.demov.facade.service.HelloService%26methods%3DsayHello%26pid%3D42451%26side%3Dconsumer%26timeout%3D3000000%26timestamp%3D1588645452894®istry=zookeeper×tamp=1588645571661
这个url的加工过程有个重要环节就是com.alibaba.dubbo.config.AbstractInterfaceConfig.loadRegistries(boolean),该方法将注册中心的信息,如注册中心地址与端口127.0.0.1:2182,采用协议registry=zookeepe拼接到url中,并将url的协议设置成registry://,参见AbstractInterfaceConfig类192行处。
第三步 推出Invoker实例的逻辑比较简单,我们以默认的dubbo协议DubboProtocol为例子看下,就是new一个DubboInvoker实例的过程,将配置上下文url送进去:
public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
// create rpc invoker.
DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
invokers.add(invoker);
return invoker;
}
第四步,调用proxyFactory.getProxy创建代理的reference bean,下面单独一个段落分析。
ProxyFactory.getProxy
getProxy主要处理的逻辑和流程有:
- 植入回声服务EchoService com.alibaba.dubbo.rpc.proxy.AbstractProxyFactory.getProxy(Invoker
) - 生成代理类 com.alibaba.dubbo.common.bytecode.Proxy.getProxy(ClassLoader, Class<?>…),com.alibaba.dubbo.common.bytecode.ClassGenerator。生成办法跟扩展点机制的adaptive机制的手法类似,依靠Javassist完成,具体细节参见刚才提到的getProxy方法和ClassGenerator类。大致描述下过程:
- 先跟据interfaces构建proxy的class和Proxy的class,注意proxy的p的大小写!再用Proxy的实例调用其newInstance方法创建proxy实例。上面一句有点绕:proxy是相当于bean,Proxy相当于构建bean的builder,此处命名不是很友好,仅靠大小写区分。
- Proxy实例会做cache ProxyCacheMap。
- Proxy实例的命名是proxy后加一个整数 整数是通过PROXY_CLASS_COUNTER 这个AtomicLong维护
此处interfaces已经自动加入EchoService。
生成的代码:
Proxy相当于构建bean的builder:
[arthas@43605]$ jad com.alibaba.dubbo.common.bytecode.Proxy0
ClassLoader:
+-sun.misc.Launcher$AppClassLoader@18b4aac2
+-sun.misc.Launcher$ExtClassLoader@7823a2f9
Location:
/*
* Decompiled with CFR.
*
* Could not load the following classes:
* com.alibaba.dubbo.common.bytecode.ClassGenerator$DC
* com.alibaba.dubbo.common.bytecode.Proxy
*/
package com.alibaba.dubbo.common.bytecode;
import com.alibaba.dubbo.common.bytecode.ClassGenerator;
import com.alibaba.dubbo.common.bytecode.Proxy;
import com.alibaba.dubbo.common.bytecode.proxy0;
import java.lang.reflect.InvocationHandler;
public class Proxy0
extends Proxy
implements ClassGenerator.DC {
public Object newInstance(InvocationHandler invocationHandler) {
return new proxy0(invocationHandler);
}
}
proxy是相当于bean:
[arthas@43605]$ jad com.alibaba.dubbo.common.bytecode.proxy0
ClassLoader:
+-sun.misc.Launcher$AppClassLoader@18b4aac2
+-sun.misc.Launcher$ExtClassLoader@7823a2f9
Location:
/*
* Decompiled with CFR.
*
* Could not load the following classes:
* com.alibaba.dubbo.common.bytecode.ClassGenerator$DC
* com.alibaba.dubbo.rpc.service.EchoService
* com.code260.ss.dubbo.demov.facade.service.HelloService
*/
package com.alibaba.dubbo.common.bytecode;
import com.alibaba.dubbo.common.bytecode.ClassGenerator;
import com.alibaba.dubbo.rpc.service.EchoService;
import com.code260.ss.dubbo.demov.facade.service.HelloService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class proxy0
implements ClassGenerator.DC,
EchoService,
HelloService {
public static Method[] methods;
private InvocationHandler handler;
public void sayHello(String string) {
Object[] arrobject = new Object[]{string};
Object object = this.handler.invoke(this, methods[0], arrobject);
}
public Object $echo(Object object) {
Object[] arrobject = new Object[]{object};
Object object2 = this.handler.invoke(this, methods[1], arrobject);
return object2;
}
public proxy0() {
}
public proxy0(InvocationHandler invocationHandler) {
this.handler = invocationHandler;
}
}
至此,HelloService在consumer侧reference bean的实现类生成完毕。
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!