1. 程式人生 > >Dubbo源碼閱讀筆記4

Dubbo源碼閱讀筆記4

rem rec abstract provide jdk handle cal ssi info

### 發布服務到本地

發布本地服務的代碼在ServiceConfig.doExportUrlsFor1Protocol方法裏
主要代碼如下

// 通過動態代理工廠生成實現類調用器Invoker, Invoker相當於動態代理類
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);

// 只是將代理和配置放到一起
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

// 發布服務到協議中,現在是InjvmProtocol
Exporter<?> exporter = protocol.export(wrapperInvoker);
exporters.add(exporter);

默認用的動態代理工廠是JavassistProxyFactory
通過AbstractProxyInvoker類封裝成Invoker
Wrapper是通過字節碼技術給類增加了幾個增強方法
想查看javassist生成的class源代碼可以通過CtClass類的writeFile方法輸出到本地文件,再反編譯

// JavassistProxyFactory.java
public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
    // TODO Wrapper類不能正確處理帶$的類名
    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);
        }
    };
}

JdkProxyFactory則是直接用反射調用

// JdkProxyFactory.java
public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
    return new AbstractProxyInvoker<T>(proxy, type, url) {
        @Override
        protected Object doInvoke(T proxy, String methodName,
                                  Class<?>[] parameterTypes,
                                  Object[] arguments) throws Throwable {
            Method method = proxy.getClass().getMethod(methodName, parameterTypes);
            return method.invoke(proxy, arguments);
        }
    };
}

接下來就是把invoker發布
用的協議是InjvmProtocol
injvm協議非常簡單,不用監聽端口什麽的,直接返回InjvmExporter實例
到這裏發布服務到本地已經完成了

// InjvmProtocol.java
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    return new InjvmExporter<T>(invoker, invoker.getUrl().getServiceKey(), exporterMap);
}

// InjvmExporter.java
class InjvmExporter<T> extends AbstractExporter<T> {

    private final String key;

    private final Map<String, Exporter<?>> exporterMap;

    InjvmExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap) {
        super(invoker);
        this.key = key;
        this.exporterMap = exporterMap;
        exporterMap.put(key, this);
    }

    public void unexport() {
        super.unexport();
        exporterMap.remove(key);
    }
}

### 調用本地服務

服務引用在初始化時生成目標接口的代理實現
在ReferenceConfig.createProxy方法生成

// ReferenceConfig.java
if (isJvmRefer) {
    URL url = new URL(Constants.LOCAL_PROTOCOL, NetUtils.LOCALHOST, 0, interfaceClass.getName()).addParameters(map);
    
    // 通過服務協議獲取服務調用器, refprotocol和發布時的protocol是同一實例
    invoker = refprotocol.refer(interfaceClass, url);
    if (logger.isInfoEnabled()) {
        logger.info("Using injvm service " + interfaceClass.getName());
    }
}

// InjvmProtocol.java
public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
    // exporterMap保存了已發布的服務,因為發布和引用都用的同一個protocol實例,所以exporterMap也是同一個。
    return new InjvmInvoker<T>(serviceType, url, url.getServiceKey(), exporterMap);
}

創建動態代理返回
默認用的動態代理工廠是JavassistProxyFactory,用字節碼技術生成目標接口的代理類
如果使用JdkProxyFactory,則沒有字節碼生成類的步驟,直接動態代理調用Invoker類的invoke方法

// ReferenceConfig.java
// 創建服務代理
return (T) proxyFactory.getProxy(invoker);

// JavassistProxyFactory.java
public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
    return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}

// JdkProxyFactory.java
public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
    return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), interfaces, new InvokerInvocationHandler(invoker));
}

// 重點**
// javassist生成的類繼承自Proxy,並實現了目標接口,目標方法會被實現一次,handler是上面的InvokerInvocationHandler實例
// 以下是生成的方法
public String sayHello(String $1) {
    Object[] args = new Object[1]; 
    args[0] = ($w)$1; 
    Object ret = handler.invoke(this, methods[0], args); 
    return (java.lang.String)ret;
}

// InvokerInvocationHandler.java
// 該類實現了InvocationHandler,目的是支持jdk動態代理
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();
}

最後在調用目標方法的時候,通過調用方invoker找到服務方invoker

// InjvmInvoker.java
// 此時在調用方invoker實例
public Result doInvoke(Invocation invocation) throws Throwable {
    // 通過exporterMap找到服務方exporter
    Exporter<?> exporter = InjvmProtocol.getExporter(exporterMap, getUrl());
    if (exporter == null) {
        throw new RpcException("Service [" + key + "] not found.");
    }
    RpcContext.getContext().setRemoteAddress(NetUtils.LOCALHOST, 0);
    // 拿出服務方invoker並調用方法
    return exporter.getInvoker().invoke(invocation);
}

Dubbo源碼閱讀筆記4