文章目录
- 1.背景
- 2. dubbo对接口的代理
- 2.1 dubbo服务端和消费端启动都默认采用Javassist代理
- 2.2 服务端设置JDK代理
- 2.3 消费端设置JDK代理
- 3. dubbo对非接口类的代理
- 4.总结
1.背景
当dubbo消费者启动时才会对引用的服务创建代理,这里面有个疑问,众所周知,dubbo通过JDK和Javassist来对接口进行动态代理,问题来了:
(1)dubbo对接口什么时候采用jdk代理,什么时候采用javassist代理?
(2)若dubbo对外暴露的不是接口,怎么代理?
2. dubbo对接口的代理
2.1 dubbo服务端和消费端启动都默认采用Javassist代理
dubbo消费端启动过程时创建代理
<!--服务端配置 -->
<dubbo:application name="debbo-provider" owner="programmer" organization="dubbox"/>
<dubbo:registry address="zookeeper://localhost:2181"/>
<dubbo:protocol name="dubbo" port="20880"/>
<dubbo:service interface="service.DemoService" ref="demoService" protocol="dubbo"/>
<bean id="demoService" class="Impl.DemoServiceImpl"/>
<!--消费端配置 -->
<dubbo:application name="dubbo-consumer" owner="programmer" organization="dubbox"/>
<dubbo:registry address="zookeeper://localhost:2181"/>
<dubbo:reference id="permissionService" interface="service.DemoService"/>
当dubbo消费者启动时才会对引用的服务创建代理,可参考:,可知当dubbo对接口进行代理时,会默认使用 JavassistProxyFactory 来创建引用服务的代理,JavassistProxyFactory 源码如下:
public class JavassistProxyFactory extends AbstractProxyFactory {
// 获取代理
public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}
public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
return new AbstractProxyInvoker<T>(proxy, type, url) {
@Override
protected Object doInvoke(T proxy, String methodName,
Class<?>[] parameterTypes,
Object[] arguments) throws Throwable {
return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
}
};
}
}
JavassistProxyFactory的getProxy方法看似跟jdk生成动态代理一样, 但这里的Proxy类不是jdk的类,而是dubbo自写的Proxy类(com.alibaba.dubbo.common.bytecode.Proxy),该类可利用javassist工具对接口生成代理代码(注意与反射的区别)。
2.2 服务端设置JDK代理
<!--服务端配置 -->
<dubbo:application name="debbo-provider" owner="programmer" organization="dubbox"/>
<dubbo:registry address="zookeeper://localhost:2181"/>
<dubbo:protocol name="dubbo" port="20880"/>
<dubbo:service interface="service.DemoService" ref="demoService" protocol="dubbo" proxy="jdk"/>
<bean id="demoService" class="Impl.DemoServiceImpl"/>
<!--消费端配置 -->
<dubbo:application name="dubbo-consumer" owner="programmer" organization="dubbox"/>
<dubbo:registry address="zookeeper://localhost:2181"/>
<dubbo:reference id="permissionService" interface="service.DemoService"/>
服务端启动过程
- 暴露本地服务
private void exportLocal(URL url) {
if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
URL local = URL.valueOf(url.toFullString())
.setProtocol(Constants.LOCAL_PROTOCOL)
.setHost(NetUtils.LOCALHOST)
.setPort(0);
Exporter<?> exporter = protocol.export(proxyFactory.getInvoker(ref, (Class) interfaceClass, local));
exporters.add(exporter);
}
}
这里主要看
proxyFactory.getInvoker(ref, (Class) interfaceClass, local)
参考proxyFactory的适配类源码,会根据url中的proxy参数值获取对应的扩展类,由于这里的配置了"jdk"代理,所以最终会调用 JdkProxyFactory 的 getInvoker 方法,即采用了JDK动态代理。
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class ProxyFactory$Adpative implements com.alibaba.dubbo.rpc.ProxyFactory {
public com.alibaba.dubbo.rpc.Invoker getInvoker(java.lang.Object arg0, java.lang.Class arg1,com.alibaba.dubbo.common.URL arg2) throws java.lang.Object {
//......
com.alibaba.dubbo.common.URL url = arg2;
String extName = url.getParameter("proxy", "javassist”);
//......
com.alibaba.dubbo.rpc.ProxyFactory extension =
(com.alibaba.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName);
return extension.getInvoker(arg0, arg1, arg2);
}
public java.lang.Object getProxy(com.alibaba.dubbo.rpc.Invoker arg0) throws
com.alibaba.dubbo.rpc.Invoker {
//......
com.alibaba.dubbo.common.URL url = arg0.getUrl();
String extName = url.getParameter("proxy", "javassist");
//......
com.alibaba.dubbo.rpc.ProxyFactory extension =
(com.alibaba.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName);
return extension.getProxy(arg0);
}
}
- 暴露远程服务
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
Exporter<?> exporter = protocol.export(invoker);
exporters.add(exporter);
这里的url是
registry://localhost:2181/com.alibaba.dubbo.registry.RegistryService?application=debbo-
provider&dubbo=2.0.2&organization=dubbox&owner=programmer&pid=27589®istry=zookeeper×tamp=1541600447325
与本地暴露服务的url不是同一个url,则在
proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()))
执行的时候,执行的是 JavassistProxyFactory 的getInvoker方法。
2.3 消费端设置JDK代理
<!--服务端配置 -->
<dubbo:application name="debbo-provider" owner="programmer" organization="dubbox"/>
<dubbo:registry address="zookeeper://localhost:2181"/>
<dubbo:protocol name="dubbo" port="20880"/>
<dubbo:service interface="service.DemoService" ref="demoService" protocol="dubbo"/>
<bean id="demoService" class="Impl.DemoServiceImpl"/>
<!--消费端配置 -->
<dubbo:application name="dubbo-consumer" owner="programmer" organization="dubbox"/>
<dubbo:registry address="zookeeper://localhost:2181"/>
<dubbo:reference id="permissionService" interface="service.DemoService" proxy="jdk"/>
消费端启动过程中创建代理时,
proxyFactory.getProxy(invoker);
会根据配置使用 JdkProxyFactory 的 getProxy 方法来创建引用服务的代理。
3. dubbo对非接口类的代理
上面有个疑问,dubbo能否对非接口类进行代理,也就是说对外暴露的服务不是接口形式的,通过源码判断,dubbo是不能对非接口类进行代理的。
dubbo会把dubbo service标签解析到ServiceConfig类中,若发现interface属性为null,dubbo会抛出异常,由此可见,dubbo是不能对非接口类进行代理的。
public class ServiceConfig<T> extends AbstractServiceConfig {
// 接口类型
private String interfaceName;
private Class<?> interfaceClass;
//......
protected synchronized void doExport() {
//......
if (interfaceName == null || interfaceName.length() == 0) {
throw new IllegalStateException("<dubbo:service interface=\"\" /> interface not allow null!");
}
//.......
}
4.总结
通过上面分析,总结如下:
- dubbo对服务创建代理方式默认采用Javassist代理,可通过在服务/消费端设置proxy属性来采用jdk代理方式,配置方式见上文
- dubbo是不能对非接口类进行代理的