# 引用服务
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进行初始化。主要过程有:
![refer流程](http://www.uxiaowo.com/dubbo/refer.png)
## 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();
}
```