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的创建与注册。

posted @ 2021-01-18 20:04  随花四散  阅读(1884)  评论(0编辑  收藏  举报