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&registry=zookeeper&timestamp=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主要处理的逻辑和流程有:

  1. 植入回声服务EchoService com.alibaba.dubbo.rpc.proxy.AbstractProxyFactory.getProxy(Invoker)
  2. 生成代理类 com.alibaba.dubbo.common.bytecode.Proxy.getProxy(ClassLoader, Class<?>…),com.alibaba.dubbo.common.bytecode.ClassGenerator。生成办法跟扩展点机制的adaptive机制的手法类似,依靠Javassist完成,具体细节参见刚才提到的getProxy方法和ClassGenerator类。大致描述下过程:
    1. 先跟据interfaces构建proxy的class和Proxy的class,注意proxy的p的大小写!再用Proxy的实例调用其newInstance方法创建proxy实例。上面一句有点绕:proxy是相当于beanProxy相当于构建bean的builder,此处命名不是很友好,仅靠大小写区分。
    2. Proxy实例会做cache ProxyCacheMap。
    3. 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的实现类生成完毕。