# 引用服務
dubbo中引用服務時,首先需要在配置文件使用`dubbo:reference`標簽引用接口,最主要的目的是指定引用的服務名;之后,在程序中使用Spring的Context的getBean()返回接口的代理實例;有了這個實例后就可以在Consumer端調用接口相關的方法。
`dubbo:reference`使用的ReferenceBean實現了FactoryBean接口,getBean時會調用getObject()方法,這是獲取引用的入口。
>FactoryBean:以Bean結尾,表示它是一個Bean,不同于普通Bean的是:它是實現了FactoryBean<T>接口的Bean,根據該Bean的Id從BeanFactory中獲取的實際上是FactoryBean的getObject()返回的對象,而不是FactoryBean本身, 如果要獲取FactoryBean對象,可以在id前面加一個&符號來獲取。
getObject首先會檢查是否已經獲取過,如果沒有獲取過才會執行init進行初始化。主要過程有:

## 1.檢查配置
獲取consumer端全局配置,檢查是否指定了interfaceName,檢查指定的接口類是否存在;可以配置一個屬性`dubbo.resolve.file`,指定一個文件或使用默認的文件`dubbo-resolve.properties`,內部可以配置將interfaceName轉換為url,這個url是點對點直連服務地址;
根據application>module>consumer依次獲取注冊中心,監控中心等信息,組裝成map。這個map中保存了服務及全局配置的信息,用于后續生成代理對象。
## 2.創建invoker
引用服務時的invoker與發布服務時創建的invoker不同,發布服務中時為了在客戶端請求時服務器調用實現類,而引用服務時生成的invoker是為了封裝底層的序列化、通信等操作。
1. 將map轉為url,如果resolver中指定了url,那么不從injvm中引用;默認情況下如果本地有服務暴露,則引用本地服務;若為injvm協議則使用protocol的refer獲取invoker,具體的過程可見injvm實現。
```
private static final Protocol refprotocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
invoker = refprotocol.refer(interfaceClass, url);
```
2.如果不是injvm協議,會檢查是否提供了點對點直連地址,如果提供了則解析地址加入urls中;如果沒有提供則會加載注冊中心,獲取注冊中心的url
3.如果有一個直連地址或一個注冊中心,會使用這個地址創建invoker
```
invoker = refprotocol.refer(interfaceClass, urls.get(0));
```
4.如果有多個直連地址或多個注冊中心,則會遍歷地址,生成多個invoker加入到一個invoker列表,然后將該列表包裝為Directory對象,加入到Cluster中。
```
List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
URL registryURL = null;
for (URL url : urls) {
invokers.add(refprotocol.refer(interfaceClass, url));
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
registryURL = url; // 用了最后一個registry url
}
}
if (registryURL != null) { // 有 注冊中心協議的URL
// 對有注冊中心的Cluster 只用 AvailableCluster
URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME);
invoker = cluster.join(new StaticDirectory(u, invokers));
} else { // 不是 注冊中心的URL
invoker = cluster.join(new StaticDirectory(invokers));
}
```
在這里,需要對Directory對象和Cluster進行說明。
Directory接口保存了某個服務的Invoker列表
```
public interface Directory<T> extends Node {
Class<T> getInterface(); // 獲取接口類
List<Invoker<T>> list(Invocation invocation) throws RpcException; // 列出Invoker列表
}
```
Cluster接口的目的是存在多個服務提供者時,失敗自動切換,當出現失敗,重試其它服務器。通過觀察接口,我們可以看出,Cluster中的Directory保存Invoker列表,Cluster又封裝為一個Invoker,在doInvoker中做失敗切換的策略。
```
public interface Cluster {
<T> Invoker<T> join(Directory<T> directory) throws RpcException;
}
```
### 協議引用服務
1. 由于此時的url是`registry://`,會首先獲取
```
|- ProtocolFilterWrapper
|- ProtocolListenerWrapper
|- RegistryProtocol
```
ProtocolFilterWrapper和ProtocolListenerWrapper在`registry://`時不起作用,會直接交給RegistryProtocol處理,RegistryProtocol內部首先獲取注冊中心,如果配置了group屬性則使用MergeableCluster,否則使用Cluster$Adaptive;
2.RegistryProtocol會創建RegistryDirectory保存invoker列表,首先通知注冊中心注冊要消費的服務;使用注冊中心的register()注冊`consumer://xxx/xxx?xxx`,在zookeeper上創建`/dubbo/接口名/consumers/consumer://xxx`節點。
3. 之后,使用RegistryDirectory的subscribe方法訂閱服務消費者`consumer://xxx/xxx?xxx`,根據url中category的值創建以下幾個節點,并添加監聽事件,監聽事件在RegistryDirectory中,也就是說如果providers、configurators、routers節點發生變化,RegistryDirectory會根據注冊中心的配置修改內部的Invoker列表
```
[/dubbo/com.alibaba.dubbo.demo.DemoService/providers,
/dubbo/com.alibaba.dubbo.demo.DemoService/configurators,
/dubbo/com.alibaba.dubbo.demo.DemoService/routers]
```
4. 最后使用`cluster.join(directory)`創建一個可以進行失敗重試的Invoker返回。
## 3.創建代理
生成invoker后,最重要的一步時生成代理對象給應用程序使用,使用下面的程序生成指定接口的實例
```
private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
(T) proxyFactory.getProxy(invoker);
```
proxyFactory對象與發布服務流程中一樣,也是下面的結構,
```
-| StubProxyFactoryWrapper
-| JavassistProxyFactory
```
StubProxyFactoryWrapper的getProxy方法中使用內部的JavassistProxyFactory的getProxy方法,并對GenericService類型的接口進行處理。
JavassistProxyFactory的getProxy方法中會將consumer端生成的invoker實例包裝為指定的服務接口的實例,便于應用程序直接調用相關方法。
```
public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}
```
首先使用 Proxy.getProxy()創建了interfaces接口的實現類A,newInstance方法會實例化這個實現類A并將invoker包裝為InvokerInvocationHandler傳給實現類的實例。
```
Proxy.getProxy() 動態創建實現類A
new A(new InvokerInvocationHandler(invoker));
```
在實現上,getProxy()會動態生成一個類A,內部包含了接口中定義的方法,內部會使用InvokerInvocationHandler執行相關邏輯
```
// 動態創建實現類A的sayHello方法會執行handler的invoke方法
public abstract java.lang.String com.alibaba.dubbo.demo.DemoService.sayHello(java.lang.String){
Object[] args = new Object[1]; args[0] = ($w)$1; Object ret = handler.invoke(this, methods[0], args); return (java.lang.String)ret;
}
```
而InvokerInvocationHandler中執行invoker的invoke方法,由于不同協議生成的Invoker不同,會執行不同的調用。
```
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
Class<?>[] parameterTypes = method.getParameterTypes();
if (method.getDeclaringClass() == Object.class) {
return method.invoke(invoker, args);
}
if ("toString".equals(methodName) && parameterTypes.length == 0) {
return invoker.toString();
}
if ("hashCode".equals(methodName) && parameterTypes.length == 0) {
return invoker.hashCode();
}
if ("equals".equals(methodName) && parameterTypes.length == 1) {
return invoker.equals(args[0]);
}
return invoker.invoke(new RpcInvocation(method, args)).recreate();
}
```