1. 程式人生 > >Dubbo原始碼解讀之動態代理

Dubbo原始碼解讀之動態代理

前言

或許我們已悉知Java的動態代理的方式:jdk——通過介面中的方法名,在動態生成的代理類中呼叫業務實現類的同名方法;cglib——通過繼承業務類,生成的動態代理類是業務類的子類,通過重寫業務方法進行代理。dubbo在沿用java的jdk方式外,還採取了javassist方式——通過位元組碼生成代替反射。

Dubbo代理概覽

類圖(這裡只包括我們比較常用的代理類)
image

從類圖我們可以看出Dubbo代理工廠主要實現了JavassistProxyFactory和JdkProxyFactory。

具體實現

我們先來看看ProxyFactory介面

@SPI("javassist")
public interface ProxyFactory {

    /**
     * create proxy.
     *
     * @param invoker
     * @return proxy
     */
    @Adaptive({Constants.PROXY_KEY})
    <T> T getProxy(Invoker<T> invoker) throws RpcException;

    /**
     * create proxy.
     *
     * @param invoker
     * @return proxy
     */
    @Adaptive({Constants.PROXY_KEY})
    <T> T getProxy(Invoker<T> invoker, boolean generic) throws RpcException;

    /**
     * create invoker.
     *
     * @param <T>
     * @param proxy
     * @param type
     * @param url
     * @return invoker
     */
    @Adaptive({Constants.PROXY_KEY})
    <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;

}

@SPI(“javassist”)註解表示Dubbo預設使用的代理模式,對應後面將會講到的JavassistProxyFactory。

@Adaptive({Constants.PROXY_KEY})配合@SPI一起使用,其中{Constants.PROXY_KEY}匹配請求url中引數key值,在ExtensionLoader動態載入擴充套件進行相應匹配。其詳細講解可參考博文https://blog.csdn.net/matthew_zhang/article/details/68966051

ProxyFactory介面有三個方法宣告:

  • T getProxy(Invoker invoker):獲取代理物件方法介面。
  • T getProxy(Invoker invoker, boolean generic):作用與前一個介面相同,多了一個generic引數,這個引數在dubbo:reference中配置,表示是否預設泛化介面。如果為泛化介面,將返回GenericService,此時消費者可用如下方式呼叫。
GenericService demoService = (GenericService) context.getBean("demoService");

Object result = demoService.$invoke("getPermissions", new String[] { "java.lang.Long" }, new Object[]{ 1L });
  • Invoker getInvoker(T proxy, Class type, URL url):獲取呼叫者方法介面。

繼續看AbstractProxyFactory抽象類

public abstract class AbstractProxyFactory implements ProxyFactory {

    @Override
    public <T> T getProxy(Invoker<T> invoker) throws RpcException {
        return getProxy(invoker, false);
    }

    @Override
    public <T> T getProxy(Invoker<T> invoker, boolean generic) throws RpcException {
        Class<?>[] interfaces = null;
        String config = invoker.getUrl().getParameter(Constants.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++) {
                    // TODO can we load successfully for a different classloader?.
                    interfaces[i + 2] = ReflectUtils.forName(types[i]);
                }
            }
        }
        if (interfaces == null) {
            interfaces = new Class<?>[]{invoker.getInterface(), EchoService.class};
        }

        if (!GenericService.class.isAssignableFrom(invoker.getInterface()) && generic) {
            int len = interfaces.length;
            Class<?>[] temp = interfaces;
            interfaces = new Class<?>[len + 1];
            System.arraycopy(temp, 0, interfaces, 0, len);
            interfaces[len] = com.alibaba.dubbo.rpc.service.GenericService.class;
        }

        return getProxy(invoker, interfaces);
    }

    public abstract <T> T getProxy(Invoker<T> invoker, Class<?>[] types);

}

AbstractProxyFactory初步實現了getProxy(Invoker invoker)和getProxy(Invoker invoker, boolean generic)介面(實際相當於一個介面),主要獲取了呼叫請求中的介面資訊interfaces,然後定義了一個抽象方法getProxy(Invoker invoker, Class<?>[] types),讓具體的類去實現代理物件的獲取。

下面就是具體的代理實現類JdkProxyFactory

public class JdkProxyFactory extends AbstractProxyFactory {

    @Override
    @SuppressWarnings("unchecked")
    public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
        return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), interfaces, new InvokerInvocationHandler(invoker));
    }

    @Override
    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);
            }
        };
    }

}

方式和jdk動態代理方式一致,這裡不再贅述(可參考博文)。

接下來就是本文的重點了,看看JavassistProxyFactory如何實現的。

public class JavassistProxyFactory extends AbstractProxyFactory {

    @Override
    @SuppressWarnings("unchecked")
    public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
        return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
    }

    @Override
    public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
        // TODO Wrapper cannot handle this scenario correctly: the classname contains '$'
        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);
            }
        };
    }

}

我們可以看到建立代理物件getProxy時,它採用了com.alibaba.dubbo.common.bytecode.Proxy 生成代理物件的工具類,步驟如下:

  • 遍歷所有入參介面,在確認介面和能被正常載入且可見(not private)情況下,將介面名稱以“;”隔開進行字串拼接。
for (int i = 0; i < ics.length; i++) {
            String itf = ics[i].getName();
            if (!ics[i].isInterface())
                throw new RuntimeException(itf + " is not a interface.");

            Class<?> tmp = null;
            try {
                tmp = Class.forName(itf, false, cl);
            } catch (ClassNotFoundException e) {
            }

            if (tmp != ics[i])
                throw new IllegalArgumentException(ics[i] + " is not visible from class loader");

            sb.append(itf).append(';');
        }
  • 通過類載入器初始化map快取,並以上文拼接的字串為key值,查詢map快取中是否存在此key值,若存在則返回代理物件。
 // get cache by class loader.
        Map<String, Object> cache;
        synchronized (ProxyCacheMap) {
            cache = ProxyCacheMap.get(cl);
            if (cache == null) {
                cache = new HashMap<String, Object>();
                ProxyCacheMap.put(cl, cache);
            }
        }

        Proxy proxy = null;
        synchronized (cache) {
            do {
                Object value = cache.get(key);
                if (value instanceof Reference<?>) {
                    proxy = (Proxy) ((Reference<?>) value).get();
                    if (proxy != null)
                        return proxy;
                }

                if (value == PendingGenerationMarker) {
                    try {
                        cache.wait();
                    } catch (InterruptedException e) {
                    }
                } else {
                    cache.put(key, PendingGenerationMarker);
                    break;
                }
            }
            while (true);
        }
  • 利用AtomicLong物件自增獲取一個long陣列來作為生產代理類的字尾,防止衝突;遍歷介面定義的所有方法,放入set集合判重,且構建代理物件方法體return ret= handler.invoke(this, methods[ix], args),通過InvokerInvocationHandler將介面方法委託呼叫。
 long id = PROXY_CLASS_COUNTER.getAndIncrement();
        String pkg = null;
        ClassGenerator ccp = null, ccm = null;
        try {
            ccp = ClassGenerator.newInstance(cl);

            Set<String> worked = new HashSet<String>();
            List<Method> methods = new ArrayList<Method>();

            for (int i = 0; i < ics.length; i++) {
                if (!Modifier.isPublic(ics[i].getModifiers())) {
                    String npkg = ics[i].getPackage().getName();
                    if (pkg == null) {
                        pkg = npkg;
                    } else {
                        if (!pkg.equals(npkg))
                            throw new IllegalArgumentException("non-public interfaces from different packages");
                    }
                }
                ccp.addInterface(ics[i]);

                for (Method method : ics[i].getMethods()) {
                    String desc = ReflectUtils.getDesc(method);
                    if (worked.contains(desc))
                        continue;
                    worked.add(desc);

                    int ix = methods.size();
                    Class<?> rt = method.getReturnType();
                    Class<?>[] pts = method.getParameterTypes();

                    StringBuilder code = new StringBuilder("Object[] args = new Object[").append(pts.length).append("];");
                    for (int j = 0; j < pts.length; j++)
                        code.append(" args[").append(j).append("] = ($w)$").append(j + 1).append(";");
                    code.append(" Object ret = handler.invoke(this, methods[" + ix + "], args);");
                    if (!Void.TYPE.equals(rt))
                        code.append(" return ").append(asArgument(rt, "ret")).append(";");

                    methods.add(method);
                    ccp.addMethod(method.getName(), method.getModifiers(), rt, pts, method.getExceptionTypes(), code.toString());
                }
            }
  • 建立代理例項物件ProxyInstance和代理類proxy
// create ProxyInstance class.
            String pcn = pkg + ".proxy" + id;
            ccp.setClassName(pcn);
            ccp.addField("public static java.lang.reflect.Method[] methods;");
            ccp.addField("private " + InvocationHandler.class.getName() + " handler;");
            ccp.addConstructor(Modifier.PUBLIC, new Class<?>[]{InvocationHandler.class}, new Class<?>[0], "handler=$1;");
            ccp.addDefaultConstructor();
            Class<?> clazz = ccp.toClass();
            clazz.getField("methods").set(null, methods.toArray(new Method[0]));

            // create Proxy class.
            String fcn = Proxy.class.getName() + id;
            ccm = ClassGenerator.newInstance(cl);
            ccm.setClassName(fcn);
            ccm.addDefaultConstructor();
            ccm.setSuperClass(Proxy.class);
            ccm.addMethod("public Object newInstance(" + InvocationHandler.class.getName() + " h){ return new " + pcn + "($1); }");
            Class<?> pc = ccm.toClass();
            proxy = (Proxy) pc.newInstance();

至此proxy代理類的構造已完成。接下來我們在看看getInvoker(T proxy, Class type, URL url) 方法。

public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
        // TODO Wrapper cannot handle this scenario correctly: the classname contains '$'
        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);
            }
        };
    }

此方法採用匿名內部類的模式建立呼叫者類new AbstractProxyInvoker(proxy, type, url),並在其內部doInvoke方法中實現代理類的實際呼叫。

總結

這篇文章是博主對dubbo動態代理的原始碼的初步解讀,有不當之處還望各位大佬多多指正。
關於dubbo動態代理選型方面的問題,可參考這一篇博文動態代理方案效能對比,評論區不容錯過。。。