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