• dubbo ReferenceBean与ServiceBean的创建流程


      ServiceBean与ReferenceBean是dubbo与spring整合后的调用的核心类。

    提供者在Spring中以ServiceBean的形式存在,消费者需要引用的服务由ReferenceBean构建,两种Bean中记录了dubbo调用的信息,在底层调用时

    @EnableDubbo

      SpringBoot项目如果需要开启dubbo则必须配置该注解,该注解继承了@DubboComponentScan,该注解会引入DubboComponentScanRegistrar,该Registrar会注册ServiceAnnotationBeanPostProcessor以及ReferenceAnnotationBeanPostProcessor两个后置处理器就是完成ServiceBean以及ReferenceBean创建的入口

    ReferenceBean

    第一步:获取类中被@Reference注解修饰的方法或者属性

      ReferenceAnnotationBeanPostProcessor中有如下方法,其代码相对简单这里不展开叙述。

     1 @Override
     2 
     3 public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
     4 
     5     if (beanType != null) {
     6 
     7         // 获取类中被@Reference注解引用的方法或者属性并包装成metadata
     8 
     9         // 将metadata放入缓存
    10 
    11         InjectionMetadata metadata = findReferenceMetadata(beanName, beanType, null);
    12 
    13         metadata.checkConfigMembers(beanDefinition);
    14 
    15     }
    16 
    17 }

    第二步:获取将引用实例化并且注入到对应的属性或方法中

      ReferenceAnnotationBeanPostProcessor中有如下方法。

     1 @Override
     2 
     3 public PropertyValues postProcessPropertyValues(
     4 
     5         PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
     6 
     7     // 获取类中被@Reference注解引用的方法或者属性并包装成metadata
     8 
     9     // 由于在第一步中已经获取完毕并且放入缓存,所以这里直接从缓存中就可以取到
    10 
    11     InjectionMetadata metadata = findReferenceMetadata(beanName, bean.getClass(), pvs);
    12 
    13     try {
    14 
    15         // 向bean中注入引用实例
    16 
    17         metadata.inject(bean, beanName, pvs);
    18 
    19     } catch (BeanCreationException ex) {
    20 
    21         throw ex;
    22 
    23     } catch (Throwable ex) {
    24 
    25         throw new BeanCreationException(beanName, "Injection of @Reference dependencies failed", ex);
    26 
    27     }
    28 
    29     return pvs;
    30 
    31 }

      我们顺着metadata#inject方法调用链向下,最后会进入到ReferenceBean创建的流程。

    第三步:创建ReferenceBean

      ReferenceAnnotationBeanPostProcessor$ReferenceFieldElement中有如下方法。

     1 @Override
     2 
     3 protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
     4 
     5 
     6 
     7     Class<?> referenceClass = field.getType();
     8 
     9     // 创建ReferenceBean
    10 
    11     referenceBean = buildReferenceBean(reference, referenceClass);
    12 
    13 
    14 
    15     ReflectionUtils.makeAccessible(field);
    16 
    17     // 创建服务引用实例,并将实例注入到属性中
    18 
    19    field.set(bean, referenceBean.getObject());
    20 
    21 
    22 
    23 }

      创建ReferenceBean中会将各种信息注册到ReferenceBean中,这里不做介绍,主要是最后一行,ReferenceBean#getObject方法,该方法是真正实例化服务引用的方法。我们可以看到ReferenceBean并没有作为Spring的bean存在,其只是用于实例化服务引用的工具类而已。

      ps:dubbo2.7.0之前实例化引用不会是在最后一行这里,因为ReferenceBean继承自了来自AbstractConfig的toString方法,该方法是利用反射原理,将所有方法名以get开头的方法调用一遍,这里就会调用到getObject方法。而在buildReferenceBean方法中会日志会打印出构建好的ReferenceBean,导致提前实例化了服务对象。不过不影响结果,只是在debug时会造成疑惑。

    第四步:实例化服务引用

      ReferenceBean#getObject最终会调到ReferenceConfig#init方法,该方法中包含了完整的实例化流程。

     1 private void init() {
     2 
     3     // 省略其他,前面的所有代码都是在初始化、构建属性
     4 
     5     // 实例化服务引用
     6 
     7     // 看方法名就知道,实际的服务引用是一个动态生成的代理类
     8 
     9     ref = createProxy(map);
    10 
    11 }
    12 
    13 
    14 
    15 // 协议,SPI,动态获取自适应类
    16 
    17 private static final Protocol refprotocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
    18 
    19 // 集群,SPI,动态获取自适应类
    20 
    21 private static final Cluster cluster = ExtensionLoader.getExtensionLoader(Cluster.class).getAdaptiveExtension();
    22 
    23 // 代理生成工厂,SPI,动态获取自适应类
    24 
    25 private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
    26 
    27 
    28 
    29 private T createProxy(Map<String, String> map) {
    30 
    31         // 省略前面的代码
    32 
    33         
    34 
    35         // 4.1 获取invoker,invoker就是实际远程调用工具
    36 
    37         if (urls.size() == 1) {
    38 
    39             invoker = refprotocol.refer(interfaceClass, urls.get(0));
    40 
    41         } else {
    42 
    43             List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
    44 
    45             URL registryURL = null;
    46 
    47             for (URL url : urls) {
    48 
    49                 invokers.add(refprotocol.refer(interfaceClass, url));
    50 
    51                 if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
    52 
    53                     registryURL = url; // use last registry url
    54 
    55                 }
    56 
    57             }
    58 
    59             if (registryURL != null) { // registry url is available
    60 
    61                 // use AvailableCluster only when register's cluster is available
    62 
    63                 URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME);
    64 
    65                 invoker = cluster.join(new StaticDirectory(u, invokers));
    66 
    67             } else { // not a registry url
    68 
    69                 invoker = cluster.join(new StaticDirectory(invokers));
    70 
    71             }
    72 
    73         }
    74 
    75     }
    76 
    77 
    78 
    79     // 省略部分
    80 
    81     
    82 
    83     // 4.2 创建代理类
    84 
    85     return (T) proxyFactory.getProxy(invoker);
    86 
    87 }

    4.1:获取invoker

      refprotocol是一个SPI,urls中存的是注册中心的url,其protocol是registry,所以这里会调用到RegistryProtocol。

     1 @Override
     2 
     3 @SuppressWarnings("unchecked")
     4 
     5 public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
     6 
     7     // 将真实的protocol设置到protocol属性中
     8 
     9     url = url.setProtocol(url.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_REGISTRY)).removeParameter(Constants.REGISTRY_KEY);
    10 
    11     // 4.1.1 获取registry
    12 
    13     Registry registry = registryFactory.getRegistry(url);
    14 
    15     if (RegistryService.class.equals(type)) {
    16 
    17         return proxyFactory.getInvoker((T) registry, type, url);
    18 
    19     }
    20 
    21 
    22 
    23     // group="a,b" or group="*"
    24 
    25     Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));
    26 
    27     String group = qs.get(Constants.GROUP_KEY);
    28 
    29     if (group != null && group.length() > 0) {
    30 
    31         if ((Constants.COMMA_SPLIT_PATTERN.split(group)).length > 1
    32 
    33                 || "*".equals(group)) {
    34 
    35             return doRefer(getMergeableCluster(), registry, type, url);
    36 
    37         }
    38 
    39     }
    40 
    41     return doRefer(cluster, registry, type, url);
    42 
    43 }
    44 
    45 
    46 
    47 private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
    48 
    49     RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
    50 
    51     directory.setRegistry(registry);
    52 
    53     directory.setProtocol(protocol);
    54 
    55     // all attributes of REFER_KEY
    56 
    57     Map<String, String> parameters = new HashMap<String, String>(directory.getUrl().getParameters());
    58 
    59     // 4.1.2 构建订阅url
    60 
    61     URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, parameters.remove(Constants.REGISTER_IP_KEY), 0, type.getName(), parameters);
    62 
    63     if (!Constants.ANY_VALUE.equals(url.getServiceInterface())
    64 
    65             && url.getParameter(Constants.REGISTER_KEY, true)) {
    66 
    67         // 4.1.3 注册        
    68 
    69         registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, Constants.CONSUMERS_CATEGORY,
    70 
    71                 Constants.CHECK_KEY, String.valueOf(false)));
    72 
    73     }
    74 
    75     // 4.1.4 订阅
    76 
    77     directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY,
    78 
    79             Constants.PROVIDERS_CATEGORY
    80 
    81                     + "," + Constants.CONFIGURATORS_CATEGORY
    82 
    83                     + "," + Constants.ROUTERS_CATEGORY));
    84 
    85 
    86 
    87     Invoker invoker = cluster.join(directory);
    88 
    89     ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
    90 
    91     return invoker;
    92 
    93 }

      首先获取注册中心,这里使用的是zookeeper,所以这里获取的ZooKeeperRegistry。

      然后是构建订阅的url并注册到zookeeper,即向zookeeper写入数据。

      最后是订阅服务提供者的目录。然后会将RegistryDirectory作为listener包装后注册到zookeeper中。在订阅结束之后也会显式回调listener的notify方法。在该方法中会创建服务的invoker,其流程比较简单这里就不展开叙述。

      我们使用的是dubbo协议,所以最终在创建invoker时会使用dubboProtocol,所以在dubboProtocol的refer方法打个断点,你就能看到整个订阅流程,这是一个相当长的调用链。详细介绍会在《服务注册与调用流程》中介绍。

    4.2:创建服务引用代理类

      1 proxyFactory是一个SPI,默认使用JavassistProxyFactory,所以这里就以JavassistProxyFactory为例。getProxy方法最终会调用到Proxy的getProxy方法。
      2 
      3 public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
      4 
      5     return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
      6 
      7 }
      8 
      9 
     10 
     11 public static Proxy getProxy(ClassLoader cl, Class<?>... ics) {
     12 
     13     
     14 
     15     ClassGenerator ccp = null, ccm = null;
     16 
     17     try {
     18 
     19         // 省略前面的代码,前面的代码是构建代理类的各种属性
     20 
     21         
     22 
     23         // 服务引用实例的代理类,它实现了我们自己的接口
     24 
     25         String pcn = pkg + ".proxy" + id;
     26 
     27         ccp.setClassName(pcn);
     28 
     29         ccp.addField("public static java.lang.reflect.Method[] methods;");
     30 
     31         ccp.addField("private " + InvocationHandler.class.getName() + " handler;");
     32 
     33         ccp.addConstructor(Modifier.PUBLIC, new Class<?>[]{InvocationHandler.class}, new Class<?>[0], "handler=$1;");
     34 
     35         ccp.addDefaultConstructor();
     36 
     37         Class<?> clazz = ccp.toClass();
     38 
     39         clazz.getField("methods").set(null, methods.toArray(new Method[0]));
     40 
     41 
     42 
     43         // 创建代理类,该代理类用于创建服务引用实例,即创建上面的类
     44 
     45         String fcn = Proxy.class.getName() + id;
     46 
     47         ccm = ClassGenerator.newInstance(cl);
     48 
     49         ccm.setClassName(fcn);
     50 
     51         ccm.addDefaultConstructor();
     52 
     53         ccm.setSuperClass(Proxy.class);
     54 
     55         // pcn就是上面类的名字
     56 
     57         ccm.addMethod("public Object newInstance("
     58 
     59          + InvocationHandler.class.getName() + 
     60 
     61          " h){ return new " + pcn + "($1); }");
     62 
     63         Class<?> pc = ccm.toClass();
     64 
     65         proxy = (Proxy) pc.newInstance();
     66 
     67     } catch (RuntimeException e) {
     68 
     69         throw e;
     70 
     71     } catch (Exception e) {
     72 
     73         throw new RuntimeException(e.getMessage(), e);
     74 
     75     } finally {
     76 
     77         // release ClassGenerator
     78 
     79         if (ccp != null)
     80 
     81             ccp.release();
     82 
     83         if (ccm != null)
     84 
     85             ccm.release();
     86 
     87         synchronized (cache) {
     88 
     89             if (proxy == null)
     90 
     91                 cache.remove(key);
     92 
     93             else
     94 
     95                 cache.put(key, new WeakReference<Proxy>(proxy));
     96 
     97             cache.notifyAll();
     98 
     99         }
    100 
    101     }
    102 
    103     return proxy;
    104 
    105 }

      其中ccp就是实际服务引用实例的代理类,我们程序中使用的对象就是该类。

      mInterfaces中包含了我们自己的接口service.Service。

      mFields中就包含了一个handler对象,该handler持有了之前创建的invoker对象。

      mMethods中就包含了我们自己接口中的方法sayHello,该方法的实现就是直接使用handler进行远程调用。

      ccm是用来生成ccp的代理类,调用ccp的newInstances方法会生成ccp的对象。

      类中只包含了一个newInstance方法,用于创建ccp实例。

      以上便完成了引用代理对象的创建与注入。

    ServiceBean

    第一步:获取类中被@Service注解修饰的对象

      ServiceAnnotationBeanProcessor实现了BeanDefinitionRegistryPostProcessor,所以在bean注册完之后会调用该类的postProcessBeanDefinitionRegistry方法。

    整个ServiceBean的注册流程非常简单,无非是扫描包,找到@Service注解修饰的对象,然后构建一个ServiceBean对象注入到Spring容器中。这一流程就不再过多叙述。

    第二步:加载配置

      第一步只完成了ServiceBean的注册,而核心创建流程就在ServiceBean中。

      ServiceBean实现了ApplicationListener接口,Spring容器加载完之后会通知ApplicationListener的实现类,即调用如下方法:

     1 @Override
     2 
     3 public void onApplicationEvent(ContextRefreshedEvent event) {
     4 
     5     if (isDelay() && !isExported() && !isUnexported()) {
     6 
     7         if (logger.isInfoEnabled()) {
     8 
     9             logger.info("The service ready on spring started. service: " + getInterface());
    10 
    11         }
    12 
    13         // 发布流程
    14 
    15         export();
    16 
    17     }
    18 
    19 }

      由于这一步比较简单,读者可以自行阅读export()方法的源码。

    第三步:发布

      跟着export()方法流程走,会进入到ServiceConfig#doExportUrls方法中,其部分代码如下:

     1 if (!Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) {
     2 
     3 
     4 
     5     // export to local if the config is not remote (export to remote only when config is remote)
     6 
     7     if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) {
     8 
     9         exportLocal(url);
    10 
    11     }
    12 
    13     // export to remote if the config is not local (export to local only when config is local)
    14 
    15     if (!Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope)) {
    16 
    17         
    18 
    19         if (registryURLs != null && !registryURLs.isEmpty()) {
    20 
    21             for (URL registryURL : registryURLs) {
    22 
    23                 url = url.addParameterIfAbsent(Constants.DYNAMIC_KEY, registryURL.getParameter(Constants.DYNAMIC_KEY));
    24 
    25                 URL monitorUrl = loadMonitor(registryURL);
    26 
    27                 if (monitorUrl != null) {
    28 
    29                     url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString());
    30 
    31                 }
    32 
    33                 if (logger.isInfoEnabled()) {
    34 
    35                     logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);
    36 
    37                 }
    38 
    39                 // 3.1 获取invoker
    40 
    41                 Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
    42 
    43                 // 3.2 将invoker与当前ServiceBean绑定
    44 
    45                 DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
    46 
    47                 // 3.3 发布
    48 
    49                 Exporter<?> exporter = protocol.export(wrapperInvoker);
    50 
    51                 exporters.add(exporter);
    52 
    53             }
    54 
    55         } else {
    56 
    57             Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
    58 
    59             DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
    60 
    61 
    62 
    63             Exporter<?> exporter = protocol.export(wrapperInvoker);
    64 
    65             exporters.add(exporter);
    66 
    67         }
    68 
    69     }
    70 
    71 }

      首先是获取invoker,该invoker包含了注册中心的url,然后将invoker将当前ServiceBean绑定,这样就能够通过该invoker获取服务、注册中心、配置等必须的数据,然后发布。

    注册中心url的protocol是registry,所以这里发布调用的是RegistryProtocol。

     1 @Override
     2 public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
     3     // 3.4 发布到本地 即绑定本地端口
     4     final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker);
     5 
     6     URL registryUrl = getRegistryUrl(originInvoker);
     7 
     8     // 3.5 获取注册中心
     9     final Registry registry = getRegistry(originInvoker);
    10     final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);
    11 
    12     
    13     boolean register = registedProviderUrl.getParameter("register", true);
    14 
    15     ProviderConsumerRegTable.registerProvider(originInvoker, registryUrl, registedProviderUrl);
    16     
    17     // 3.6 注册
    18     if (register) {
    19         register(registryUrl, registedProviderUrl);
    20         ProviderConsumerRegTable.getProviderWrapper(originInvoker).setReg(true);
    21     }
    22 
    23 
    24     final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registedProviderUrl);
    25     final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
    26     overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
    27     // 3.7 订阅
    28     registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
    29     return new DestroyableExporter<T>(exporter, originInvoker, overrideSubscribeUrl, registedProviderUrl);
    30 }

       到这里,整个流程已经相当清晰了。其中的细节比较简单,都是一些zookeeper操作,所以这里不仔细展开了。

      首先将当前服务绑定到本地端口用于监听远程调用。

      其次获取注册中心,我们使用zookeeper,所以这里获取的是ZooKeeperRegistry。

      然后想注册中心中注册我们的服务,即向zookeeper写数据。

      最后订阅路径,当有新的服务加入时会覆盖zk上的数据,这里就会收到回调。

      以上便完成了ServiceBean的创建与注册。

  • 相关阅读:
    floyd的魔改应用——洛谷P2419 [USACO08JAN]牛大赛Cow Contest 题解
    洛谷P2142 高精度减法 题解
    浅谈SPFA——洛谷P1576 最小花费 题解
    洛谷P1301 魔鬼之城 题解
    洛谷P1009 阶乘之和 题解
    20200926模拟
    [NOIP 2013]货车运输
    带权并查集--P2024 [NOI2001]食物链
    归并排序/树状数组求逆序对-lgP1908 逆序对
    LCA模块+求树上两点距离最短
  • 原文地址:https://www.cnblogs.com/ouhaitao/p/14294873.html
Copyright © 2020-2023  润新知