文章目录

  • 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"/>

服务端启动过程

  1. 暴露本地服务
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);
    }
}
  1. 暴露远程服务
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是不能对非接口类进行代理的