首先来看一段代码来引入今天的话题
// 强制转换成EchoService
EchoService echoService = (EchoService) userReadService;
// 回声测试
Object status = echoService.$echo("Ok");
Assert.equals(status, "Ok");
以上代码正常情况下返回值status=Ok。可是很明显,我们写的Dubbo服务接口并没有与EchoService接口有任何继承关系,这是如何实现的呢?
这里涉及到另外一个知识点,Dubbo接口远程调用时实际上都是通过生成代理对象实现的。拿调用方举例,spring xml配置方式如下:
<dubbo:reference id="userReadService" interface="com.test.service.UserReadService" check="false" version="1.0.0" timeout="3000" />
那么xml又是如何转换为对象的呢?实际上是依赖spring.handlers机制初始化的。我们在dubbo的源码中可以看到以下配置,意思是使用com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
解析dubbo的xml配置文件。
好了,我们来看dubbo是如何解析reference配置的。在DubboNamespaceHandler类中我们可以找到以下代码:
public class DubboNamespaceHandler extends NamespaceHandlerSupport {
public void init() {
......
registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
......
}
}
也就是说通过以上代码将reference配置内容转换成ReferenceBean。我们再来看ReferenceBean的定义:
public class ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean, ApplicationContextAware, InitializingBean, DisposableBean {
ReferenceBean实现了FactoryBean,也就是说ReferenceBean本质上就是个spring的FactoryBean。到这里,熟悉spring的小伙伴一下子就明白了,xml配置最终会转化为ReferenceBean.getObject()类。
继续看:
public Object getObject() throws Exception {
return get();
}
public synchronized T get() {
if (destroyed){
throw new IllegalStateException("Already destroyed!");
}
if (ref == null) {
init();
}
return ref;
}
这里可以看到最终返回的是成员变量ref。继续往下看:
private void init() {
......
//attributes通过系统context进行存储.
StaticContext.getSystemContext().putAll(attributes);
ref = createProxy(map);
}
终于看到了想要的信息ref = createProxy(map)。
private T createProxy(Map<String, String> map) {
......
// 创建服务代理
return (T) proxyFactory.getProxy(invoker);
}
这里关键信息来了:
public abstract class AbstractProxyFactory implements ProxyFactory {
public <T> T getProxy(Invoker<T> invoker) throws RpcException {
Class<?>[] interfaces = null;
String config = invoker.getUrl().getParameter("interfaces");
if (config != null && config.length() > 0) {
String[] types = Constants.COMMA_SPLIT_PATTERN.split(config);
if (types != null && types.length > 0) {
interfaces = new Class<?>[types.length + 2];
interfaces[0] = invoker.getInterface();
interfaces[1] = EchoService.class;
for (int i = 0; i < types.length; i ++) {
interfaces[i + 1] = ReflectUtils.forName(types[i]);
}
}
}
if (interfaces == null) {
interfaces = new Class<?>[] {invoker.getInterface(), EchoService.class};
}
return getProxy(invoker, interfaces);
}
public abstract <T> T getProxy(Invoker<T> invoker, Class<?>[] types);
}
这里出现了预期的EchoService.class。
再来看getProxy(Invoker invoker, Class<?>[] types)的默认实现:
public class JavassistProxyFactory extends AbstractProxyFactory {
@SuppressWarnings("unchecked")
public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}
......
}
这里就是最终生成代理类的地方了。Proxy.getProxy方法核心逻辑是通过字符串拼接的方式生成一个代理类描述,并将EchoService设置为该代理的implement接口,然后再对生成的类进行动态加载。我们通过程序debug可以代码,生成的类其实是一个InvokerInvocationHandler
对象。
我们来简单回顾下上文内容:
DubboNamespaceHandler#init
--> registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
--> ReferenceBean#getObject
--> ReferenceConfig#init
--> ReferenceConfig#createProxy
--> AbstractProxyFactory#getProxy
--> JavassistProxyFactory#getProxy
--> Proxy#getProxy
通过以上过程最终生成了InvokerInvocationHandler
代理对象,而InvokerInvocationHandler
对象中的Invoker成员变量正式我们文章开头强制类型转换为EchoService的子类。
至此,我们就已经解读了为什么dubbo中的任何ReferenceBean都可以强制类型转换为EchoService了。
今天我们主要介绍的是xml形式的实现,通过springboot注解形式的实现过程类似,只是上层解析入口换成了springboot的SPI机制实现类。