目標:從源碼的角度分析服務引用過程。
前面服務暴露過程的文章講解到,服務引用有兩種方式,一種就是直連,也就是直接指定服務的地址來進行引用,這種方式更多的時候被用來作服務測試,不建議在生產環境使用這樣的方法,由於直連不適合服務治理,dubbo自己就是一個服務治理的框架,提供了不少服務治理的功能。因此更多的時候,咱們都不會選擇繞過註冊中心,而是經過註冊中心的方式來進行服務引用。html
大體能夠分爲三個步驟:java
dubbo服務的引用起點就相似於bean加載。dubbo中有一個類ReferenceBean,它實現了FactoryBean接口,繼承了ReferenceConfig,因此ReferenceBean做爲dubbo中能生產對象的工廠Bean,而咱們要引用服務,也就是要有一個該服務的對象。spring
服務引用被觸發有兩個時機:apache
默認狀況下,Dubbo 使用懶漢式引用服務。若是須要使用餓漢式,可經過配置 <dubbo:reference> 的 init 屬性開啓。segmentfault
由於ReferenceBean實現了FactoryBean接口的getObject()方法,因此在加載bean的時候,會調用ReferenceBean的getObject()方法數組
public Object getObject() { return get(); }
這個get方法是ReferenceConfig的get()方法緩存
public synchronized T get() { // 檢查而且更新配置 checkAndUpdateSubConfigs(); // 若是被銷燬,則拋出異常 if (destroyed) { throw new IllegalStateException("The invoker of ReferenceConfig(" + url + ") has already destroyed!"); } // 檢測 代理對象ref 是否爲空,爲空則經過 init 方法建立 if (ref == null) { // 用於處理配置,以及調用 createProxy 生成代理類 init(); } return ref; }
關於checkAndUpdateSubConfigs()方法前一篇文章已經講了,我就再也不講述。這裏關注init方法。該方法也是處理各種配置的開始。服務器
private void init() { // 若是已經初始化過,則結束 if (initialized) { return; } // 設置初始化標誌爲true initialized = true; // 本地存根合法性校驗 checkStubAndLocal(interfaceClass); // mock合法性校驗 checkMock(interfaceClass); // 用來存放配置 Map<String, String> map = new HashMap<String, String>(); // 存放這是消費者側 map.put(Constants.SIDE_KEY, Constants.CONSUMER_SIDE); // 添加 協議版本、發佈版本,時間戳 等信息到 map 中 appendRuntimeParameters(map); // 若是是泛化調用 if (!isGeneric()) { // 得到版本號 String revision = Version.getVersion(interfaceClass, version); if (revision != null && revision.length() > 0) { // 設置版本號 map.put(Constants.REVISION_KEY, revision); } // 得到全部方法 String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames(); if (methods.length == 0) { logger.warn("No method found in service interface " + interfaceClass.getName()); map.put(Constants.METHODS_KEY, Constants.ANY_VALUE); } else { // 把全部方法簽名拼接起來放入map map.put(Constants.METHODS_KEY, StringUtils.join(new HashSet<String>(Arrays.asList(methods)), Constants.COMMA_SEPARATOR)); } } // 加入服務接口名稱 map.put(Constants.INTERFACE_KEY, interfaceName); // 添加metrics、application、module、consumer、protocol的全部信息到map appendParameters(map, metrics); appendParameters(map, application); appendParameters(map, module); appendParameters(map, consumer, Constants.DEFAULT_KEY); appendParameters(map, this); Map<String, Object> attributes = null; if (CollectionUtils.isNotEmpty(methods)) { attributes = new HashMap<String, Object>(); // 遍歷方法配置 for (MethodConfig methodConfig : methods) { // 把方法配置加入map appendParameters(map, methodConfig, methodConfig.getName()); // 生成重試的配置key String retryKey = methodConfig.getName() + ".retry"; // 若是map中已經有該配置,則移除該配置 if (map.containsKey(retryKey)) { String retryValue = map.remove(retryKey); // 若是配置爲false,也就是不重試,則設置重試次數爲0次 if ("false".equals(retryValue)) { map.put(methodConfig.getName() + ".retries", "0"); } } // 設置異步配置 attributes.put(methodConfig.getName(), convertMethodConfig2AyncInfo(methodConfig)); } } // 獲取服務消費者 ip 地址 String hostToRegistry = ConfigUtils.getSystemProperty(Constants.DUBBO_IP_TO_REGISTRY); // 若是爲空,則獲取本地ip if (StringUtils.isEmpty(hostToRegistry)) { hostToRegistry = NetUtils.getLocalHost(); } // 設置消費者ip map.put(Constants.REGISTER_IP_KEY, hostToRegistry); // 建立代理對象 ref = createProxy(map); // 生產服務key String serviceKey = URL.buildKey(interfaceName, group, version); // 根據服務名,ReferenceConfig,代理類構建 ConsumerModel, // 並將 ConsumerModel 存入到 ApplicationModel 中 ApplicationModel.initConsumerModel(serviceKey, buildConsumerModel(serviceKey, attributes)); }
該方法大體分爲如下幾個步驟:併發
在這裏處理配置到邏輯比較清晰。下面就是看ReferenceConfig的createProxy()方法。app
private T createProxy(Map<String, String> map) { // 根據配置檢查是否爲本地調用 if (shouldJvmRefer(map)) { // 生成url,protocol使用的是injvm URL url = new URL(Constants.LOCAL_PROTOCOL, Constants.LOCALHOST_VALUE, 0, interfaceClass.getName()).addParameters(map); // 利用InjvmProtocol 的 refer 方法生成 InjvmInvoker 實例 invoker = refprotocol.refer(interfaceClass, url); if (logger.isInfoEnabled()) { logger.info("Using injvm service " + interfaceClass.getName()); } } else { // 若是url不爲空,則用戶可能想進行直連來調用 if (url != null && url.length() > 0) { // user specified URL, could be peer-to-peer address, or register center's address. // 當須要配置多個 url 時,可用分號進行分割,這裏會進行切分 String[] us = Constants.SEMICOLON_SPLIT_PATTERN.split(url); // 遍歷全部的url if (us != null && us.length > 0) { for (String u : us) { URL url = URL.valueOf(u); if (StringUtils.isEmpty(url.getPath())) { // 設置接口全限定名爲 url 路徑 url = url.setPath(interfaceName); } // 檢測 url 協議是否爲 registry,如果,代表用戶想使用指定的註冊中心 if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) { // 將 map 轉換爲查詢字符串,並做爲 refer 參數的值添加到 url 中 urls.add(url.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map))); } else { // 合併 url,移除服務提供者的一些配置(這些配置來源於用戶配置的 url 屬性), // 好比線程池相關配置。並保留服務提供者的部分配置,好比版本,group,時間戳等 // 最後將合併後的配置設置爲 url 查詢字符串中。 urls.add(ClusterUtils.mergeUrl(url, map)); } } } } else { // assemble URL from register center's configuration // 校驗註冊中心 checkRegistry(); // 加載註冊中心的url List<URL> us = loadRegistries(false); if (CollectionUtils.isNotEmpty(us)) { // 遍歷全部的註冊中心 for (URL u : us) { // 生成監控url URL monitorUrl = loadMonitor(u); if (monitorUrl != null) { // 加入監控中心url的配置 map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString())); } // 添加 refer 參數到 url 中,並將 url 添加到 urls 中 urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map))); } } // 若是urls爲空,則拋出異常 if (urls.isEmpty()) { throw new IllegalStateException("No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please config <dubbo:registry address=\"...\" /> to your spring config."); } } // 若是隻有一個註冊中心,則直接調用refer方法 if (urls.size() == 1) { // 調用 RegistryProtocol 的 refer 構建 Invoker 實例 invoker = refprotocol.refer(interfaceClass, urls.get(0)); } else { List<Invoker<?>> invokers = new ArrayList<Invoker<?>>(); URL registryURL = null; // 遍歷全部的註冊中心url for (URL url : urls) { // 經過 refprotocol 調用 refer 構建 Invoker, // refprotocol 會在運行時根據 url 協議頭加載指定的 Protocol 實例,並調用實例的 refer 方法 // 把生成的Invoker加入到集合中 invokers.add(refprotocol.refer(interfaceClass, url)); // 若是是註冊中心的協議 if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) { // 則設置registryURL registryURL = url; // use last registry url } } // 優先用註冊中心的url if (registryURL != null) { // registry url is available // use RegistryAwareCluster only when register's cluster is available // 只有當註冊中心當連接可用當時候,採用RegistryAwareCluster URL u = registryURL.addParameter(Constants.CLUSTER_KEY, RegistryAwareCluster.NAME); // The invoker wrap relation would be: RegistryAwareClusterInvoker(StaticDirectory) -> FailoverClusterInvoker(RegistryDirectory, will execute route) -> Invoker // 由集羣進行多個invoker合併 invoker = cluster.join(new StaticDirectory(u, invokers)); } else { // not a registry url, must be direct invoke. // 直接進行合併 invoker = cluster.join(new StaticDirectory(invokers)); } } } // 若是須要覈對該服務是否可用,而且該服務不可用 if (shouldCheck() && !invoker.isAvailable()) { // make it possible for consumer to retry later if provider is temporarily unavailable // 修改初始化標誌爲false initialized = false; // 拋出異常 throw new IllegalStateException("Failed to check the status of the service " + interfaceName + ". No provider available for the service " + (group == null ? "" : group + "/") + interfaceName + (version == null ? "" : ":" + version) + " from the url " + invoker.getUrl() + " to the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion()); } if (logger.isInfoEnabled()) { logger.info("Refer dubbo service " + interfaceClass.getName() + " from url " + invoker.getUrl()); } /** * @since 2.7.0 * ServiceData Store */ // 元數據中心服務 MetadataReportService metadataReportService = null; // 加載元數據服務,若是成功 if ((metadataReportService = getMetadataReportService()) != null) { // 生成url URL consumerURL = new URL(Constants.CONSUMER_PROTOCOL, map.remove(Constants.REGISTER_IP_KEY), 0, map.get(Constants.INTERFACE_KEY), map); // 把消費者配置加入到元數據中心中 metadataReportService.publishConsumer(consumerURL); } // create service proxy // 建立服務代理 return (T) proxyFactory.getProxy(invoker); }
該方法的大體邏輯可用分爲如下幾步:
Invoker 是 Dubbo 的核心模型,表明一個可執行體。在服務提供方,Invoker 用於調用服務提供類。在服務消費方,Invoker 用於執行遠程調用。Invoker 是由 Protocol 實現類構建而來。關於這幾個接口的定義介紹能夠參考《dubbo源碼解析(十九)遠程調用——開篇》,Protocol 實現類有不少,下面會分析 RegistryProtocol 和 DubboProtocol,咱們能夠看到上面的源碼中講到,當只有一個註冊中心的時候,會直接使用RegistryProtocol。因此先來看看RegistryProtocol的refer()方法。
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException { // 取 registry 參數值,並將其設置爲協議頭,默認是dubbo url = URLBuilder.from(url) .setProtocol(url.getParameter(REGISTRY_KEY, DEFAULT_REGISTRY)) .removeParameter(REGISTRY_KEY) .build(); // 得到註冊中心實例 Registry registry = registryFactory.getRegistry(url); // 若是是註冊中心服務,則返回註冊中心服務的invoker if (RegistryService.class.equals(type)) { return proxyFactory.getInvoker((T) registry, type, url); } // group="a,b" or group="*" // 將 url 查詢字符串轉爲 Map Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(REFER_KEY)); // 得到group值 String group = qs.get(Constants.GROUP_KEY); if (group != null && group.length() > 0) { // 若是有多個組,或者組配置爲*,則使用MergeableCluster,並調用 doRefer 繼續執行服務引用邏輯 if ((COMMA_SPLIT_PATTERN.split(group)).length > 1 || "*".equals(group)) { return doRefer(getMergeableCluster(), registry, type, url); } } // 只有一個組或者沒有組配置,則直接執行doRefer return doRefer(cluster, registry, type, url); }
上面的邏輯比較簡單,若是是註冊服務中心,則直接建立代理。若是不是,先處理組配置,根據組配置來決定Cluster的實現方式,而後調用doRefer方法。
private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) { // 建立 RegistryDirectory 實例 RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url); // 設置註冊中心 directory.setRegistry(registry); // 設置協議 directory.setProtocol(protocol); // all attributes of REFER_KEY // 全部屬性放到map中 Map<String, String> parameters = new HashMap<String, String>(directory.getUrl().getParameters()); // 生成服務消費者連接 URL subscribeUrl = new URL(CONSUMER_PROTOCOL, parameters.remove(REGISTER_IP_KEY), 0, type.getName(), parameters); // 註冊服務消費者,在 consumers 目錄下新節點 if (!ANY_VALUE.equals(url.getServiceInterface()) && url.getParameter(REGISTER_KEY, true)) { directory.setRegisteredConsumerUrl(getRegisteredConsumerUrl(subscribeUrl, url)); // 註冊服務消費者 registry.register(directory.getRegisteredConsumerUrl()); } // 建立路由規則鏈 directory.buildRouterChain(subscribeUrl); // 訂閱 providers、configurators、routers 等節點數據 directory.subscribe(subscribeUrl.addParameter(CATEGORY_KEY, PROVIDERS_CATEGORY + "," + CONFIGURATORS_CATEGORY + "," + ROUTERS_CATEGORY)); // 一個註冊中心可能有多個服務提供者,所以這裏須要將多個服務提供者合併爲一個,生成一個invoker Invoker invoker = cluster.join(directory); // 在服務提供者處註冊消費者 ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory); return invoker; }
該方法大體能夠分爲如下步驟:
首先仍是從DubboProtocol的refer()開始。
public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException { optimizeSerialization(url); // create rpc invoker. // 建立一個DubboInvoker實例 DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers); // 加入到集合中 invokers.add(invoker); return invoker; }
建立DubboInvoker比較簡單,調用了構造方法,這裏主要講這麼生成ExchangeClient,也就是getClients方法。
能夠參考《dubbo源碼解析(二十四)遠程調用——dubbo協議》的(三)DubboProtocol中的源碼分析。最新版本基本沒有什麼變化,只是由於加入了配置中心,配置的優先級更加明確了,因此增長了xml配置優先級高於properties配置的代碼邏輯,都比較容易理解。
其中若是是配置的共享,則得到共享客戶端對象,也就是getSharedClient()方法,不然新建客戶端也就是initClient()方法。
能夠參考《dubbo源碼解析(二十四)遠程調用——dubbo協議》的(三)DubboProtocol中的源碼分析,該方法比較簡單,先訪問緩存,若緩存未命中,則經過 initClient 方法建立新的 ExchangeClient 實例,並將該實例傳給 ReferenceCountExchangeClient 構造方法建立一個帶有引用計數功能的 ExchangeClient 實例。
能夠參考《dubbo源碼解析(二十四)遠程調用——dubbo協議》的(三)DubboProtocol中的源碼分析,initClient 方法首先獲取用戶配置的客戶端類型,最新版本已經改成默認 netty4。而後設置用戶心跳配置,而後檢測用戶配置的客戶端類型是否存在,不存在則拋出異常。最後根據 lazy 配置決定建立什麼類型的客戶端。這裏的 LazyConnectExchangeClient 代碼並非很複雜,該類會在 request 方法被調用時經過 Exchangers 的 connect 方法建立 ExchangeClient 客戶端。下面咱們分析一下 Exchangers 的 connect 方法。
public static ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException { if (url == null) { throw new IllegalArgumentException("url == null"); } if (handler == null) { throw new IllegalArgumentException("handler == null"); } url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange"); // 獲取 Exchanger 實例,默認爲 HeaderExchangeClient return getExchanger(url).connect(url, handler); }
getExchanger 會經過 SPI 加載 HeaderExchangeClient 實例,這個方法比較簡單。接下來分析 HeaderExchangeClient 的connect的實現。
public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException { // 建立 HeaderExchangeHandler 對象 // 建立 DecodeHandler 對象 // 經過 Transporters 構建 Client 實例 // 建立 HeaderExchangeClient 對象 return new HeaderExchangeClient(Transporters.connect(url, new DecodeHandler(new HeaderExchangeHandler(handler))), true); }
其中HeaderExchangeHandler、DecodeHandler等能夠參考《dubbo源碼解析(九)遠程通訊——Transport層》和《dubbo源碼解析(十)遠程通訊——Exchange層》的分析。這裏重點關注Transporters 構建 Client,也就是Transporters的connect方法。
能夠參考《dubbo源碼解析(八)遠程通訊——開篇》的(十)Transporters中源碼分析。其中得到自適應拓展類,該類會在運行時根據客戶端類型加載指定的 Transporter 實現類。若用戶未配置客戶端類型,則默認加載 NettyTransporter,並調用該類的 connect 方法。假設是netty4的實現,則執行如下代碼。
public Client connect(URL url, ChannelHandler listener) throws RemotingException { return new NettyClient(url, listener); }
到這裏爲止,DubboProtocol生成invoker過程也結束了。再回到createProxy方法的最後一句代碼,根據invoker建立服務代理對象。
爲服務接口生成代理對象。有了代理對象,便可進行遠程調用。首先來看AbstractProxyFactory 的 getProxy()方法。
能夠參考《dubbo源碼解析(二十三)遠程調用——Proxy》的(一)AbstractProxyFactory的源碼分析。能夠看到第二個getProxy方法其實就是獲取 interfaces 數組,調用到第三個getProxy方法時,該getProxy是個抽象方法,由子類來實現,咱們仍是默認它的代理實現方式爲Javassist。因此能夠看JavassistProxyFactory的getProxy方法。
public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) { // 生成 Proxy 子類(Proxy 是抽象類)。並調用 Proxy 子類的 newInstance 方法建立 Proxy 實例 return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker)); }
咱們重點看Proxy的getProxy方法。
/** * Get proxy. * * @param ics interface class array. * @return Proxy instance. */ public static Proxy getProxy(Class<?>... ics) { // 得到Proxy的類加載器來進行生成代理類 return getProxy(ClassHelper.getClassLoader(Proxy.class), ics); } /** * Get proxy. * * @param cl class loader. * @param ics interface class array. * @return Proxy instance. */ public static Proxy getProxy(ClassLoader cl, Class<?>... ics) { if (ics.length > Constants.MAX_PROXY_COUNT) { throw new IllegalArgumentException("interface limit exceeded"); } StringBuilder sb = new StringBuilder(); // 遍歷接口列表 for (int i = 0; i < ics.length; i++) { String itf = ics[i].getName(); // 檢測是不是接口,若是不是,則拋出異常 if (!ics[i].isInterface()) { throw new RuntimeException(itf + " is not a interface."); } Class<?> tmp = null; try { // 從新加載接口類 tmp = Class.forName(itf, false, cl); } catch (ClassNotFoundException e) { } // 檢測接口是否相同,這裏 tmp 有可能爲空,也就是該接口沒法被類加載器加載的。 if (tmp != ics[i]) { throw new IllegalArgumentException(ics[i] + " is not visible from class loader"); } // 拼接接口全限定名,分隔符爲 ; sb.append(itf).append(';'); } // use interface class name list as key. // 使用拼接後的接口名做爲 key String key = sb.toString(); // get cache by class loader. Map<String, Object> cache; // 把該類加載器加到本地緩存 synchronized (ProxyCacheMap) { cache = ProxyCacheMap.computeIfAbsent(cl, k -> new HashMap<>()); } Proxy proxy = null; synchronized (cache) { do { // 從緩存中獲取 Reference<Proxy> 實例 Object value = cache.get(key); if (value instanceof Reference<?>) { proxy = (Proxy) ((Reference<?>) value).get(); if (proxy != null) { return proxy; } } // 併發控制,保證只有一個線程能夠進行後續操做 if (value == PendingGenerationMarker) { try { // 其餘線程在此處進行等待 cache.wait(); } catch (InterruptedException e) { } } else { // 放置標誌位到緩存中,並跳出 while 循環進行後續操做 cache.put(key, PendingGenerationMarker); break; } } while (true); } long id = PROXY_CLASS_COUNTER.getAndIncrement(); String pkg = null; ClassGenerator ccp = null, ccm = null; try { // 建立 ClassGenerator 對象 ccp = ClassGenerator.newInstance(cl); Set<String> worked = new HashSet<>(); List<Method> methods = new ArrayList<>(); for (int i = 0; i < ics.length; i++) { // 檢測接口訪問級別是否爲 protected 或 privete if (!Modifier.isPublic(ics[i].getModifiers())) { // 獲取接口包名 String npkg = ics[i].getPackage().getName(); if (pkg == null) { pkg = npkg; } else { // 非 public 級別的接口必須在同一個包下,否者拋出異常 if (!pkg.equals(npkg)) { throw new IllegalArgumentException("non-public interfaces from different packages"); } } } // 添加接口到 ClassGenerator 中 ccp.addInterface(ics[i]); // 遍歷接口方法 for (Method method : ics[i].getMethods()) { // 獲取方法描述,可理解爲方法簽名 String desc = ReflectUtils.getDesc(method); // 若是方法描述字符串已在 worked 中,則忽略。考慮這種狀況, // A 接口和 B 接口中包含一個徹底相同的方法 if (worked.contains(desc)) { continue; } worked.add(desc); int ix = methods.size(); // 獲取方法返回值類型 Class<?> rt = method.getReturnType(); // 獲取參數列表 Class<?>[] pts = method.getParameterTypes(); // 生成 Object[] args = new Object[1...N] StringBuilder code = new StringBuilder("Object[] args = new Object[").append(pts.length).append("];"); for (int j = 0; j < pts.length; j++) { // 生成 args[1...N] = ($w)$1...N; code.append(" args[").append(j).append("] = ($w)$").append(j + 1).append(";"); } // 生成 InvokerHandler 接口的 invoker 方法調用語句,以下: // Object ret = handler.invoke(this, methods[1...N], args); code.append(" Object ret = handler.invoke(this, methods[").append(ix).append("], args);"); // 返回值不爲 void if (!Void.TYPE.equals(rt)) { // 生成返回語句,形如 return (java.lang.String) ret; code.append(" return ").append(asArgument(rt, "ret")).append(";"); } methods.add(method); // 添加方法名、訪問控制符、參數列表、方法代碼等信息到 ClassGenerator 中 ccp.addMethod(method.getName(), method.getModifiers(), rt, pts, method.getExceptionTypes(), code.toString()); } } if (pkg == null) { pkg = PACKAGE_NAME; } // create ProxyInstance class. // 構建接口代理類名稱:pkg + ".proxy" + id,好比 org.apache.dubbo.proxy0 String pcn = pkg + ".proxy" + id; ccp.setClassName(pcn); ccp.addField("public static java.lang.reflect.Method[] methods;"); // 生成 private java.lang.reflect.InvocationHandler handler; ccp.addField("private " + InvocationHandler.class.getName() + " handler;"); // 爲接口代理類添加帶有 InvocationHandler 參數的構造方法,好比: // porxy0(java.lang.reflect.InvocationHandler arg0) { // handler=$1; // } ccp.addConstructor(Modifier.PUBLIC, new Class<?>[]{InvocationHandler.class}, new Class<?>[0], "handler=$1;"); // 爲接口代理類添加默認構造方法 ccp.addDefaultConstructor(); // 生成接口代理類 Class<?> clazz = ccp.toClass(); clazz.getField("methods").set(null, methods.toArray(new Method[0])); // create Proxy class. // 構建 Proxy 子類名稱,好比 Proxy1,Proxy2 等 String fcn = Proxy.class.getName() + id; ccm = ClassGenerator.newInstance(cl); ccm.setClassName(fcn); ccm.addDefaultConstructor(); ccm.setSuperClass(Proxy.class); // 爲 Proxy 的抽象方法 newInstance 生成實現代碼,形如: // public Object newInstance(java.lang.reflect.InvocationHandler h) { // return new org.apache.dubbo.proxy0($1); // } ccm.addMethod("public Object newInstance(" + InvocationHandler.class.getName() + " h){ return new " + pcn + "($1); }"); Class<?> pc = ccm.toClass(); // 生成 Proxy 實現類 proxy = (Proxy) pc.newInstance(); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } finally { // release ClassGenerator if (ccp != null) { // 釋放資源 ccp.release(); } if (ccm != null) { ccm.release(); } synchronized (cache) { if (proxy == null) { cache.remove(key); } else { // 寫緩存 cache.put(key, new WeakReference<Proxy>(proxy)); } // 喚醒其餘等待線程 cache.notifyAll(); } } return proxy; }
代碼比較多,大體能夠分爲如下幾步:
到這裏,接口代理類生成後,服務引用也就結束了。
參考官方文檔: https://dubbo.apache.org/zh-c...
該文章講解了dubbo的服務引用過程,下一篇就講解服務方法調用過程。