美文网首页
dubbo的proxyFactory和动态代理相关

dubbo的proxyFactory和动态代理相关

作者: 安迪猪 | 来源:发表于2018-12-20 21:27 被阅读0次

    动态代理,可以理解为在系统运行期间,为目标类生成二进制的class文件并运行,当系统运行完,这个class文件也会消失。dubbo中,每一个spi注解的接口,都会通过动态代理,生成一个类似于ProtocolAdpative等的xxxAdpative的class文件。

    动态代理在dubbo的另一个典型应用是proxyFactory。
    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 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;
    
    }
    

    是一个接口,用于在服务提供端,将服务的具体实现类转为Invoker。而在消费端,通过 getProxy(Invoker<T> invoker)将invoker转为客户端需要的接口。
    在服务的发布端serviceConfig和消费端ReferenceConfig中,都会对proxyFactory通过ExtensionLoader拓展机制
    生成自适应类 ProxyFactory$Adpative(这个类会根据url的ProxyFactory参数选择对应的实现类进行操作)。

    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)  {
            if (arg2 == null) 
                throw new IllegalArgumentException("url == null");
    
            com.alibaba.dubbo.common.URL url = arg2;
            String extName = url.getParameter("proxy", "javassist");
            if(extName == null) 
                throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])");
    
            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);
        }
    

    通过查看com.alibaba.dubbo.rpc.proxy文件,

    stub=com.alibaba.dubbo.rpc.proxy.wrapper.StubProxyFactoryWrapper
    jdk=com.alibaba.dubbo.rpc.proxy.jdk.JdkProxyFactory
    javassist=com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory
    
    

    其中,JdkProxyFactory和JavassistProxyFactory是ProxyFactory 的具体扩展实现,StubProxyFactoryWrapper是实现了对代理工厂进行装饰的功能。从ProxyFactory 的接口的注解来看,JavassistProxyFactory是其默认实现。

    JavassistProxyFactory的getInvoker方法

    x先看看Invoker这个接口,其 Result invoke(Invocation invocation)方法,invocation包含了调用的方法以及参数。其通过invoke方法,得到方法的结果。

    public interface Invoker<T> extends Node {
    
        /**
         * get service interface.
         * 
         * @return service interface.
         */
        Class<T> getInterface();
    
        /**
         * invoke.
         * 
         * @param invocation
         * @return result
         * @throws RpcException
         */
        Result invoke(Invocation invocation) throws RpcException;
    
    }
    

    invoke接口有一个实现的类AbstractInvoker<T>,查看其invoke方法:

        public Result invoke(Invocation inv) throws RpcException {
            if(destroyed) {
                throw new RpcException("Rpc invoker for service " + this + " on consumer " + NetUtils.getLocalHost() 
                                                + " use dubbo version " + Version.getVersion()
                                                + " is DESTROYED, can not be invoked any more!");
            }
            RpcInvocation invocation = (RpcInvocation) inv;
            invocation.setInvoker(this);
            if (attachment != null && attachment.size() > 0) {
                invocation.addAttachmentsIfAbsent(attachment);
            }
            Map<String, String> context = RpcContext.getContext().getAttachments();
            if (context != null) {
                invocation.addAttachmentsIfAbsent(context);
            }
            if (getUrl().getMethodParameter(invocation.getMethodName(), Constants.ASYNC_KEY, false)){
                invocation.setAttachment(Constants.ASYNC_KEY, Boolean.TRUE.toString());
            }
            RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
            
            
            try {
                return doInvoke(invocation);
            } catch (InvocationTargetException e) { // biz exception
                Throwable te = e.getTargetException();
                if (te == null) {
                    return new RpcResult(e);
                } else {
                    if (te instanceof RpcException) {
                        ((RpcException) te).setCode(RpcException.BIZ_EXCEPTION);
                    }
                    return new RpcResult(te);
                }
            } catch (RpcException e) {
                if (e.isBiz()) {
                    return new RpcResult(e);
                } else {
                    throw e;
                }
            } catch (Throwable e) {
                return new RpcResult(e);
            }
        }
    

    上面这个方法,最终会调用 return doInvoke(invocation)。而在AbstractInvoker类中,doinvoke是

     protected abstract Result doInvoke(Invocation invocation) throws Throwable;
    

    是需要继承类实现的。

    而在JavassistProxyFactory中,其getInvoker方法通过返回一个新建的AbstractProxyInvoker匿名类,(其类里实现了doInvoke方法,这个doinvoke方法使得我们在通过)

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

    其重点在于 final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type); 通过我们的服务实现类,生成一个wrapper类,

        public static Wrapper getWrapper(Class<?> c)
        {
            while( ClassGenerator.isDynamicClass(c) ) // can not wrapper on dynamic class.
                c = c.getSuperclass();
    
            if( c == Object.class )
                return OBJECT_WRAPPER;
    
            Wrapper ret = WRAPPER_MAP.get(c);
            if( ret == null )
            {
                ret = makeWrapper(c);
                WRAPPER_MAP.put(c,ret);
            }
            return ret;
        }
    

    上面的重点在于 makeWrapper(c),通过服务的实现类,生成动态代理类。

        private static Wrapper makeWrapper(Class<?> c)
        {
            if( c.isPrimitive() )
                throw new IllegalArgumentException("Can not create wrapper for primitive type: " + c);
    
            String name = c.getName();
            ClassLoader cl = ClassHelper.getCallerClassLoader(Wrapper.class);
    
            StringBuilder c1 = new StringBuilder("public void setPropertyValue(Object o, String n, Object v){ ");
            StringBuilder c2 = new StringBuilder("public Object getPropertyValue(Object o, String n){ ");
            StringBuilder c3 = new StringBuilder("public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws " + InvocationTargetException.class.getName() + "{ ");
    
            c1.append(name).append(" w; try{ w = ((").append(name).append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }");
            c2.append(name).append(" w; try{ w = ((").append(name).append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }");
            c3.append(name).append(" w; try{ w = ((").append(name).append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }");
    
            Map<String, Class<?>> pts = new HashMap<String, Class<?>>(); // <property name, property types>
            Map<String, Method> ms = new LinkedHashMap<String, Method>(); // <method desc, Method instance>
            List<String> mns = new ArrayList<String>(); // method names.
            List<String> dmns = new ArrayList<String>(); // declaring method names.
            
            // get all public field.
            for( Field f : c.getFields() )
            {
                String fn = f.getName();
                Class<?> ft = f.getType();
                if( Modifier.isStatic(f.getModifiers()) || Modifier.isTransient(f.getModifiers()) )
                    continue;
    
                c1.append(" if( $2.equals(\"").append(fn).append("\") ){ w.").append(fn).append("=").append(arg(ft, "$3")).append("; return; }");
                c2.append(" if( $2.equals(\"").append(fn).append("\") ){ return ($w)w.").append(fn).append("; }");
                pts.put(fn, ft);
            }
            
            Method[] methods = c.getMethods();
            // get all public method.
            boolean hasMethod = hasMethods(methods);
            if( hasMethod ){
                c3.append(" try{");
            }
            for( Method m : methods )
            {
                if( m.getDeclaringClass() == Object.class ) //ignore Object's method.
                    continue;
    
                String mn = m.getName();
                c3.append(" if( \"").append(mn).append("\".equals( $2 ) ");
                int len = m.getParameterTypes().length;
                c3.append(" && ").append(" $3.length == ").append(len);
                
                boolean override = false;
                for( Method m2 : methods ) {
                    if (m != m2 && m.getName().equals(m2.getName())) {
                        override = true;
                        break;
                    }
                }
                if (override) {
                    if (len > 0) {
                        for (int l = 0; l < len; l ++) {
                            c3.append(" && ").append(" $3[").append(l).append("].getName().equals(\"")
                                .append(m.getParameterTypes()[l].getName()).append("\")");
                        }
                    }
                }
                
                c3.append(" ) { ");
                
                if( m.getReturnType() == Void.TYPE )
                    c3.append(" w.").append(mn).append('(').append(args(m.getParameterTypes(), "$4")).append(");").append(" return null;");
                else
                    c3.append(" return ($w)w.").append(mn).append('(').append(args(m.getParameterTypes(), "$4")).append(");");
    
                c3.append(" }");
                
                mns.add(mn);
                if( m.getDeclaringClass() == c )
                    dmns.add(mn);
                ms.put(ReflectUtils.getDesc(m), m);
            }
            if( hasMethod ){
                c3.append(" } catch(Throwable e) { " );
                c3.append("     throw new java.lang.reflect.InvocationTargetException(e); " );
                c3.append(" }");
            }
            
            c3.append(" throw new " + NoSuchMethodException.class.getName() + "(\"Not found method \\\"\"+$2+\"\\\" in class " + c.getName() + ".\"); }");
            
            // deal with get/set method.
            Matcher matcher;
            for( Map.Entry<String,Method> entry : ms.entrySet() )
            {
                String md = entry.getKey();
                Method method = (Method)entry.getValue();
                if( ( matcher = ReflectUtils.GETTER_METHOD_DESC_PATTERN.matcher(md) ).matches() )
                {
                    String pn = propertyName(matcher.group(1));
                    c2.append(" if( $2.equals(\"").append(pn).append("\") ){ return ($w)w.").append(method.getName()).append("(); }");
                    pts.put(pn, method.getReturnType());
                }
                else if( ( matcher = ReflectUtils.IS_HAS_CAN_METHOD_DESC_PATTERN.matcher(md) ).matches() )
                {
                    String pn = propertyName(matcher.group(1));
                    c2.append(" if( $2.equals(\"").append(pn).append("\") ){ return ($w)w.").append(method.getName()).append("(); }");
                    pts.put(pn, method.getReturnType());
                }
                else if( ( matcher = ReflectUtils.SETTER_METHOD_DESC_PATTERN.matcher(md) ).matches() )
                {
                    Class<?> pt = method.getParameterTypes()[0];
                    String pn = propertyName(matcher.group(1));
                    c1.append(" if( $2.equals(\"").append(pn).append("\") ){ w.").append(method.getName()).append("(").append(arg(pt,"$3")).append("); return; }");
                    pts.put(pn, pt);
                }
            }
            c1.append(" throw new " + NoSuchPropertyException.class.getName() + "(\"Not found property \\\"\"+$2+\"\\\" filed or setter method in class " + c.getName() + ".\"); }");
            c2.append(" throw new " + NoSuchPropertyException.class.getName() + "(\"Not found property \\\"\"+$2+\"\\\" filed or setter method in class " + c.getName() + ".\"); }");
    
            // make class
            long id = WRAPPER_CLASS_COUNTER.getAndIncrement();
            ClassGenerator cc = ClassGenerator.newInstance(cl);
            cc.setClassName( ( Modifier.isPublic(c.getModifiers()) ? Wrapper.class.getName() : c.getName() + "$sw" ) + id );
            cc.setSuperClass(Wrapper.class);
    
            cc.addDefaultConstructor();
            cc.addField("public static String[] pns;"); // property name array.
            cc.addField("public static " + Map.class.getName() + " pts;"); // property type map.
            cc.addField("public static String[] mns;"); // all method name array.
            cc.addField("public static String[] dmns;"); // declared method name array.
            for(int i=0,len=ms.size();i<len;i++)
                cc.addField("public static Class[] mts" + i + ";");
    
            cc.addMethod("public String[] getPropertyNames(){ return pns; }");
            cc.addMethod("public boolean hasProperty(String n){ return pts.containsKey($1); }");
            cc.addMethod("public Class getPropertyType(String n){ return (Class)pts.get($1); }");
            cc.addMethod("public String[] getMethodNames(){ return mns; }");
            cc.addMethod("public String[] getDeclaredMethodNames(){ return dmns; }");
            cc.addMethod(c1.toString());
            cc.addMethod(c2.toString());
            cc.addMethod(c3.toString());
    
            try
            {
                Class<?> wc = cc.toClass();
                // setup static field.
                wc.getField("pts").set(null, pts);
                wc.getField("pns").set(null, pts.keySet().toArray(new String[0]));
                wc.getField("mns").set(null, mns.toArray(new String[0]));
                wc.getField("dmns").set(null, dmns.toArray(new String[0]));
                int ix = 0;
                for( Method m : ms.values() )
                    wc.getField("mts" + ix++).set(null, m.getParameterTypes());
                return (Wrapper)wc.newInstance();
            }
            catch(RuntimeException e)
            {
                throw e;
            }
            catch(Throwable e)
            {
                throw new RuntimeException(e.getMessage(), e);
            }
            finally
            {
                cc.release();
                ms.clear();
                mns.clear();
                dmns.clear();
            }
        }
    

    上面这个方法太太太长了,其基本思想就是javassist动态代理的那一套,最终通过Class<?> wc = cc.toClass();生成Class<?>文件,最后通过(Wrapper)wc.newInstance()进行实例化然后返回。
    就看看具体生成什么类吧,主要看类里的invokeMethod方法:

    public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws
    java.lang.reflect.InvocationTargetException{
     cn.andy.dubbo.DataService w; 
     try{
      w= ((cn.andy.dubbo.DataService)$1);
       }catch(Throwable e){
        throw new IllegalArgumentException(e);
         }
       try{
        if( "getStringData".equals( $2 )  &&
    $3.length == 0 ) {
      return ($w)w.getStringData();
       }
        if( "dubboTest2".equals( $2)  &&  $3.length == 1 ) {
          return ($w)w.dubboTest2((java.lang.String)$4[0]);
           }
    if( "dubboTest".equals( $2 )  &&  $3.length == 1 ) {
      return ($w)w.dubboTest((java.lang.String)$4[0]); 
         }
       } catch(Throwable e) { 
            throw new java.lang.reflect.InvocationTargetException(e);
              } throw new com.alibaba.dubbo.common.bytecode.NoSuchMethodException("Not found method
    \""+$2+"\" in class cn.andy.dubbo.DataService.");
     }
    

    而我们的接口是:

    public interface DataService {
        
        int dubboTest(String id);
        
        int dubboTest2(String id);
        
        String getStringData();
    
    }
    

    实现类的名称是
    public class DataServiceImpl implements DataService
    从上面可以看出,所有调用wrapper的invokeMethod方法,都会转为调用我们真正的实现类。这样,在后期requestHandler 中,调用 return invoker.invoke(inv)时,就会调用上述的javassist生成的动态代理类,动态代理类最终调用我们真正的服务实现类。

    JdkProxyFactory的getInvoker方法

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

    JdkProxyFactory的就比较简单,就是简单的反射,调用我们的真正实现类。

    再看看JdkProxyFactory的getProxy方法

    首先,不管是JdkProxyFactory还是JavassistProxyFactory,都会先调用AbstractProxyFactory的getProxy方法。主要操作,就是增加了一个EchoService.class接口,每个服务都会增加这个接口,保证每个代理都可以使用回声服务。

      public <T> T getProxy(Invoker<T> invoker) throws RpcException {
            Class<?>[] interfaces = null;
            String config = invoker.getUrl().getParameter("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 ++) {
                        interfaces[i + 1] = ReflectUtils.forName(types[i]);
                    }
                }
            }
            if (interfaces == null) {
                interfaces = new Class<?>[] {invoker.getInterface(), EchoService.class};
            }
            return getProxy(invoker, interfaces);
        }
    
        public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
            return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), interfaces, new InvokerInvocationHandler(invoker));
        }
    

    这是典型的jdk动态代理的用法,根据接口获得的动态代理类,其调用的接口的方法都会转而调用InvokerInvocationHandler(invoker),

      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();
        }
    

    再看看JavassistProxyFactory的getProxy方法

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

    目前还没有分析,这里的Proxy.getProxy是使用javassist动态代理实现的。最终的结果应该也是 return invoker.invoke(new RpcInvocation(method, args)).recreate();吧

    相关文章

      网友评论

          本文标题:dubbo的proxyFactory和动态代理相关

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