美文网首页
04-dubbo客户端创建代理类的过程

04-dubbo客户端创建代理类的过程

作者: cjxz | 来源:发表于2019-01-24 15:55 被阅读0次

    上一篇文章我们介绍了dubbo和spring是如何串联起来的03-dubbo客户端与spring结合点分析。这一篇文件我们继续介绍客户端是如何创建代理类的。

    分析dubbo源码

    return (T) proxyFactory.getProxy(invoker);最后我们看到是proxyFactory.getProxy获得代理类。继续跟踪代码找到AbstractProxyFactory类里面的getProxy

    @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;
            //根据前面的invoker对象可以判断这个config为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]);
                    }
                }
            }
            //继续走下面的逻辑,因为interfaces确实为null,所以这个数组设置为两个接口
            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;
            }
            //默认使用JavassistProxyFactory
            return getProxy(invoker, interfaces);
        }
    

    我们可以看到interfaces数组里面有两个接口,一个是我们传入的接口invoker.getInterface()还有一个EchoService.class然后调用JavassistProxyFactory里面的方法创建对象

    /**
         * 参数invoker:invoker :interface com.order.OrderService -> zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?anyhost=true&application=consumer-of-helloworld-app&check=false&dubbo=2.5.3&interface=com.order.OrderService&methods=createOrder&pid=1702&side=consumer&timeout=5000&timestamp=1548126187262,directory: com.alibaba.dubbo.registry.integration.RegistryDirectory@7dc3712
         * 参数interfaces:OrderService和EchoService
         */
    public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
            return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
        }
    

    继续跟踪找到Proxy.java类这个是正在创建代理对象的方法

    public static Proxy getProxy(ClassLoader cl, Class<?>... ics) {
            if (ics.length > 65535)  //不能处理太多接口,所以需要确定长度。目前ics的长度是2
                throw new IllegalArgumentException("interface limit exceeded");
    
            StringBuilder sb = new StringBuilder();
            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(';');
            }
            //上面代码将接口名转换成字符串:com.order.OrderService;com.alibaba.dubbo.rpc.service.EchoService;
    
            // use interface class name list as key.
            String key = sb.toString();
    
            // 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);  //现在这个value是空
                    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);
            }
    
            //直到动态代理实现原理的同学这里就可以看的明白,这里就是自己实现动态代理,通过拼接字符串自己将源码拼成一个JAVA文件,然后通过class重新加载内存中
            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());
                    }
                }
    
                if (pkg == null)
                    pkg = PACKAGE_NAME;
    
                // 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();
            } catch (RuntimeException e) {
                throw e;
            } catch (Exception e) {
                throw new RuntimeException(e.getMessage(), e);
            } finally {
                // release ClassGenerator
                if (ccp != null)
                    ccp.release();
                if (ccm != null)
                    ccm.release();
                synchronized (cache) {
                    if (proxy == null)
                        cache.remove(key);
                    else
                        cache.put(key, new WeakReference<Proxy>(proxy));
                    cache.notifyAll();
                }
            }
            return proxy;
        }
    

    代码比较长我在这里解释一下他是如何实现的。如果你对动态代理比较熟悉的话应该知道这里实现的方式其实就是使用字符串拼接技术将代理类的源码拼凑出来,然后使用JVM的class机制将这个源码重新编译成.class文件文件,然后将这个class文件加载到JVM里面,然后通过newInstance()的方式将class实例化成对象。这个技术并不稀奇。在我的另一篇文章中已经完全实现过。我们看一下这个通过字符串拼接技术生成的java文件:

    public class proxy0 implements ClassGenerator.DC, EchoService, DemoService {
        // 方法数组
        public static Method[] methods;
        private InvocationHandler handler;
    
        public proxy0(InvocationHandler invocationHandler) {
            this.handler = invocationHandler;
        }
    
        public proxy0() {
        }
    
        public String sayHello(String string) {
            // 将参数存储到 Object 数组中
            Object[] arrobject = new Object[]{string};
            // 调用 InvocationHandler 实现类的 invoke 方法得到调用结果
            Object object = this.handler.invoke(this, methods[0], arrobject);
            // 返回调用结果
            return (String)object;
        }
    
        /** 回声测试方法 */
        public Object $echo(Object object) {
            Object[] arrobject = new Object[]{object};
            Object object2 = this.handler.invoke(this, methods[1], arrobject);
            return object2;
        }
    }
    

    以上源码来自:服务调用过程
    现在我们知道客户端代理类生成的全过程。下一篇我们再来分析客户端的请求是如何到达服务器端

    相关文章

      网友评论

          本文标题:04-dubbo客户端创建代理类的过程

          本文链接:https://www.haomeiwen.com/subject/ukzyjqtx.html