dubbo源码(十)-dubbo服务导出详解-2

服务导出

导出 Dubbo 服务, 前置工作做完,接下来就可以进行服务导出了。具体步骤如下:

  1. 获取注册中心列表,由于支持多注册中心,因此注册中心list
  2. 根据注册中心,创建rpc协议
  3. 进行本地暴露或者远程暴露

服务导出分为导出到本地 (JVM),和导出到远程。在深入分析服务导出的源码前,我们先来从宏观层面上看一下服务导出逻辑。如下:

private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
    
    // 省略无关代码
    
    if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
            .hasExtension(url.getProtocol())) {
        // 加载 ConfiguratorFactory,并生成 Configurator 实例,然后通过实例配置 url
        url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                .getExtension(url.getProtocol()).getConfigurator(url).configure(url);
    }

    String scope = url.getParameter(Constants.SCOPE_KEY);
    // 如果 scope = none,则什么都不做
    if (!Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) {
        // scope != remote,导出到本地
        if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) {
            exportLocal(url);
        }

        // scope != local,导出到远程
        if (!Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope)) {
            if (registryURLs != null && !registryURLs.isEmpty()) {
                for (URL registryURL : registryURLs) {
                    url = url.addParameterIfAbsent(Constants.DYNAMIC_KEY, registryURL.getParameter(Constants.DYNAMIC_KEY));
                    // 加载监视器链接
                    URL monitorUrl = loadMonitor(registryURL);
                    if (monitorUrl != null) {
                        // 将监视器链接作为参数添加到 url 中
                        url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString());
                    }

                    String proxy = url.getParameter(Constants.PROXY_KEY);
                    if (StringUtils.isNotEmpty(proxy)) {
                        registryURL = registryURL.addParameter(Constants.PROXY_KEY, proxy);
                    }

                    // 为服务提供类(ref)生成 Invoker
                    Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
                    // DelegateProviderMetaDataInvoker 用于持有 Invoker 和 ServiceConfig
                    DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

                    // 导出服务,并生成 Exporter
                    Exporter<?> exporter = protocol.export(wrapperInvoker);
                    exporters.add(exporter);
                }
                
            // 不存在注册中心,仅导出服务
            } else {
                Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
                DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

                Exporter<?> exporter = protocol.export(wrapperInvoker);
                exporters.add(exporter);
            }
        }
    }
    this.urls.add(url);
}

上面代码根据 url 中的 scope 参数决定服务导出方式,分别如下:

  • scope = none,不导出服务
  • scope != remote,导出到本地
  • scope != local,导出到远程

这里写了一个伪代码便于大家理解,如下:

public class DubboProviderSimpleCode {

    private final List<Exporter<?>> exporters = new ArrayList<Exporter<?>>();

    /**
     * 获取注册中心url列表
     * [registry://192.168.33.10:2181/com.alibaba.dubbo.registry.RegistryService?application=provider&check=false&dubbo=2.0.1&group=dubbo-learn&organization=dubbo-learn&owner=hui.wang&pid=87270&register=true&registry=zookeeper&timestamp=1550484891114]
     */
    List<URL> registryURLs = loadRegistries();//获取注册中心url列表

    for (ProtocolConfig protocolConfig : protocols) {

        /**
         * 创建协议url
         * dubbo://192.168.33.1:20880/com.hui.wang.dubbo.learn.api.MyService?anyhost=true&application=provider&bind.ip=192.168.33.1&bind.port=20880&dubbo=2.0.1&generic=false&interface=com.hui.wang.dubbo.learn.api.MyService&methods=say&organization=dubbo-learn&owner=hui.wang&pid=87636&revision=1.0.0&side=provider&timestamp=1550485766884&version=1.0
         */
        URL url = new URL(name, host, port, path, map);

        /**
         * 本地暴露
         */
        if (<dubbo:service scope!=remote>) {

            /**
             * 构造injvm协议的url
             * injvm://127.0.0.1/com.hui.wang.dubbo.learn.api.MyService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.hui.wang.dubbo.learn.api.MyService&methods=sayHello&pid=2956&side=provider&timestamp=1507004625957
             */
            URL local = URL.valueOf(url.toFullString())
                    .setProtocol(Constants.LOCAL_PROTOCOL)
                    .setHost(NetUtils.LOCALHOST)
                    .setPort(0);
            Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, local);
            Exporter<?> exporter = protocol.export(invoker);
            exporters.add(exporter);
        }

        /**
         * 远程暴露
         */
        if (<dubbo:service scope!=local>) {
            for (URL registryURL : registryURLs) {
                Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
                Exporter<?> exporter = protocol.export(invoker);
                exporters.add(exporter);
            }
        }
    }
}
创建Invoker

可以看到无论是服务本地导出还是远程导出,都涉及创建Invoker。在 Dubbo 中,Invoker 是一个非常重要的模型。在服务提供端,以及服务引用端均会出现 Invoker。

既然 Invoker 如此重要,那么我们很有必要搞清楚 Invoker 的用途。Invoker 是由 ProxyFactory 创建而来,Dubbo 默认的 ProxyFactory 实现类是 JavassistProxyFactory。下面我们到 JavassistProxyFactory 代码中,探索Invoker的创建过程。如下:

    @Override
    public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
        // TODO Wrapper cannot handle this scenario correctly: the classname contains '$'
        // 为目标类创建 Wrapper
        final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
        // 创建匿名 Invoker 类对象,并实现 doInvoke 方法。
        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);
            }
        };
    }

JavassistProxyFactorygetInvoker方法主要做了两件事:

  • 用目标类创建了WrapperClass
  • 创建AbstractProxyInvoker类对象,并实现doInvoke方法。

下面我们首先看一下,用目标类创建了WrapperClass,这里调用了WrappergetWrapper方法,具体代码如下:

    /**
     * get wrapper.
     *
     * @param c Class instance.
     * @return Wrapper instance(not null).
     */
    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 实例
        Wrapper ret = WRAPPER_MAP.get(c);
        if (ret == null) {
            // 缓存未命中,创建 Wrapper
            ret = makeWrapper(c);
            // 写入缓存
            WRAPPER_MAP.put(c, ret);
        }
        return ret;
    }

这段代码逻辑也比较简单,在缓存中获取,取不到就创建,我们继续看一下创建makeWrapper方法的逻辑:

private static Wrapper makeWrapper(Class<?> c) {
        // 检测 c 是否为基本类型,若是则抛出异常
        if (c.isPrimitive())
            throw new IllegalArgumentException("Can not create wrapper for primitive type: " + c);

        String name = c.getName();
        ClassLoader cl = ClassHelper.getClassLoader(c);

        // c1 用于存储 setPropertyValue 方法代码
        StringBuilder c1 = new StringBuilder("public void setPropertyValue(Object o, String n, Object v){ ");
        // c2 用于存储 getPropertyValue 方法代码
        StringBuilder c2 = new StringBuilder("public Object getPropertyValue(Object o, String n){ ");
        // c3 用于存储 invokeMethod 方法代码
        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); }");

        // <property name, property types>
        // pts 用于存储成员变量名和类型
        Map<String, Class<?>> pts = new HashMap<String, Class<?>>();

        // <method desc, Method instance>
        // ms 用于存储方法描述信息(可理解为方法签名)及 Method 实例
        Map<String, Method> ms = new LinkedHashMap<String, Method>();

        // method names.
        // mns 为方法名列表
        List<String> mns = new ArrayList<String>();

        // declaring method names.
        // dmns 用于存储“定义在当前类中的方法”的名称
        List<String> dmns = new ArrayList<String>();

// --------------------------------✨ 分割线1 ✨-------------------------------------

        // get all public field.
        // 获取 public 访问级别的字段,并为所有字段生成条件判断语句
        for (Field f : c.getFields()) {
            String fn = f.getName();
            Class<?> ft = f.getType();
            // 忽略关键字 static 或 transient 修饰的变量
            if (Modifier.isStatic(f.getModifiers()) || Modifier.isTransient(f.getModifiers()))
                continue;

            // 生成条件判断及赋值语句,比如:
            // if( $2.equals("name") ) { w.name = (java.lang.String) $3; return;}
            // if( $2.equals("age") ) { w.age = ((Number) $3).intValue(); return;}
            c1.append(" if( $2.equals(\"").append(fn).append("\") ){ w.").append(fn).append("=").append(arg(ft, "$3")).append("; return; }");

            // 生成条件判断及返回语句,比如:
            // if( $2.equals("name") ) { return ($w)w.name; }
            c2.append(" if( $2.equals(\"").append(fn).append("\") ){ return ($w)w.").append(fn).append("; }");

            // 存储 <字段名, 字段类型> 键值对到 pts 中
            pts.put(fn, ft);
        }

// --------------------------------✨ 分割线2 ✨-------------------------------------

        Method[] methods = c.getMethods();
        // get all public method.
        // 检测 c 中是否包含在当前类中声明的方法
        boolean hasMethod = hasMethods(methods);
        if (hasMethod) {
            c3.append(" try{");
        }
        for (Method m : methods) {
            //ignore Object's method.
            // 忽略 Object 中定义的方法
            if (m.getDeclaringClass() == Object.class)
                continue;

            String mn = m.getName();
            // 生成方法名判断语句,比如:
            // if ( "sayHello".equals( $2 )
            c3.append(" if( \"").append(mn).append("\".equals( $2 ) ");

            // 生成“运行时传入的参数数量与方法参数列表长度”判断语句,比如:
            // && $3.length == 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;
                }
            }

            // 对重载方法进行处理,考虑下面的方法:
            //    1. void sayHello(Integer, String)
            //    2. void sayHello(Integer, Integer)
            // 方法名相同,参数列表长度也相同,因此不能仅通过这两项判断两个方法是否相等。
            // 需要进一步判断方法的参数类型
            if (override) {
                if (len > 0) {
                    for (int l = 0; l < len; l++) {
                        // 生成参数类型进行检测代码,比如:
                        // && $3[0].getName().equals("java.lang.Integer")
                        //    && $3[1].getName().equals("java.lang.String")
                        c3.append(" && ").append(" $3[").append(l).append("].getName().equals(\"")
                                .append(m.getParameterTypes()[l].getName()).append("\")");
                    }
                }
            }

            // 添加 ) {,完成方法判断语句,此时生成的代码可能如下(已格式化):
            // if ("sayHello".equals($2)
            //     && $3.length == 2
            //     && $3[0].getName().equals("java.lang.Integer")
            //     && $3[1].getName().equals("java.lang.String")) {
            c3.append(" ) { ");

            // 根据返回值类型生成目标方法调用语句
            if (m.getReturnType() == Void.TYPE)
                // w.sayHello((java.lang.Integer)$4[0], (java.lang.String)$4[1]); return null;
                c3.append(" w.").append(mn).append('(').append(args(m.getParameterTypes(), "$4")).append(");").append(" return null;");
            else
                // return w.sayHello((java.lang.Integer)$4[0], (java.lang.String)$4[1]);
                c3.append(" return ($w)w.").append(mn).append('(').append(args(m.getParameterTypes(), "$4")).append(");");

            // 添加 }, 生成的代码形如(已格式化):
            // if ("sayHello".equals($2)
            //     && $3.length == 2
            //     && $3[0].getName().equals("java.lang.Integer")
            //     && $3[1].getName().equals("java.lang.String")) {
            //
            //     w.sayHello((java.lang.Integer)$4[0], (java.lang.String)$4[1]);
            //     return null;
            // }
            c3.append(" }");

            // 添加方法名到 mns 集合中
            mns.add(mn);
            // 检测当前方法是否在 c 中被声明的
            if (m.getDeclaringClass() == c)
                // 若是,则将当前方法名添加到 dmns 中
                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(" }");
        }

        // 添加 NoSuchMethodException 异常抛出代码
        c3.append(" throw new " + NoSuchMethodException.class.getName() + "(\"Not found method \\\"\"+$2+\"\\\" in class " + c.getName() + ".\"); }");

// --------------------------------✨ 分割线3 ✨-------------------------------------

        // deal with get/set method.
        // 处理 get/set 方法
        Matcher matcher;
        for (Map.Entry<String, Method> entry : ms.entrySet()) {
            String md = entry.getKey();
            Method method = (Method) entry.getValue();
            // 匹配以 get 开头的方法
            if ((matcher = ReflectUtils.GETTER_METHOD_DESC_PATTERN.matcher(md)).matches()) {
                // 获取属性名
                String pn = propertyName(matcher.group(1));
                // 生成属性判断以及返回语句,示例如下:
                // if( $2.equals("name") ) { return ($w).w.getName(); }
                c2.append(" if( $2.equals(\"").append(pn).append("\") ){ return ($w)w.").append(method.getName()).append("(); }");
                pts.put(pn, method.getReturnType());

            // 匹配以 is/has/can 开头的方法
            } else if ((matcher = ReflectUtils.IS_HAS_CAN_METHOD_DESC_PATTERN.matcher(md)).matches()) {
                String pn = propertyName(matcher.group(1));
                // 生成属性判断以及返回语句,示例如下:
                // if( $2.equals("dream") ) { return ($w).w.hasDream(); }
                c2.append(" if( $2.equals(\"").append(pn).append("\") ){ return ($w)w.").append(method.getName()).append("(); }");
                pts.put(pn, method.getReturnType());

            // 匹配以 set 开头的方法
            } else if ((matcher = ReflectUtils.SETTER_METHOD_DESC_PATTERN.matcher(md)).matches()) {
                Class<?> pt = method.getParameterTypes()[0];
                // 生成属性判断以及 setter 调用语句,示例如下:
                // if( $2.equals("name") ) { w.setName((java.lang.String)$3); return; }
                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);
            }
        }

        // 添加 NoSuchPropertyException 异常抛出代码
        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() + ".\"); }");

// --------------------------------✨ 分割线4 ✨-------------------------------------

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

        // 添加字段
        // property name array.
        cc.addField("public static String[] pns;");
        // property type map.
        cc.addField("public static " + Map.class.getName() + " pts;");
        // all method name array.
        cc.addField("public static String[] mns;");
        // declared method name array.
        cc.addField("public static String[] dmns;");
        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());
            // 创建 Wrapper 实例
            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();
        }
    }

上面代码很长,大家耐心看一下。我们在上面代码中做了大量的注释,并按功能对代码进行了分块,以帮助大家理解代码逻辑。下面对这段代码进行讲解。

  1. 首先我们把目光移到分割线1之上的代码,这段代码主要用于进行一些初始化操作。比如创建 c1、c2、c3 以及 pts、ms、mns 等变量,以及向 c1、c2、c3 中添加方法定义和类型转换代码。接下来是分割线1到分割线2之间的代码,这段代码用于为 public 级别的字段生成条件判断取值与赋值代码。这段代码不是很难看懂,就不多说了。
  2. 继续向下看,分割线2和分隔线3之间的代码用于为定义在当前类中的方法生成判断语句,和方法调用语句。因为需要对方法重载进行校验,因此到这这段代码看起来有点复杂。不过耐心开一下,也不是很难理解。
  3. 接下来是分割线3和分隔线4之间的代码,这段代码用于处理 getter、setter 以及以 is/has/can 开头的方法。处理方式是通过正则表达式获取方法类型(get/set/is/...),以及属性名。之后为属性名生成判断语句,然后为方法生成调用语句。
  4. 最后我们再来看一下分隔线4以下的代码,这段代码通过 ClassGenerator 为刚刚生成的代码构建 Class 类,并通过反射创建对象。ClassGenerator 是 Dubbo 自己封装的,该类的核心是 toClass() 的重载方法 toClass(ClassLoader, ProtectionDomain),该方法通过 javassist 构建 Class。

这段代码可能有点难理解,这里我写了一个单元测试,可以给大家看一下,首先我先创建了一个class,取名为MyClass,代码如下:

public class MyClass {


    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void say(String msg) {
        System.out.println("this is say " + msg);
    }

    public String myMsg(String message) {
        return message;
    }
}

然后我写了一个单元测试类,代码如下:

    @Test
    public void testMyClass() throws InvocationTargetException {
        final Wrapper wrapper = Wrapper.getWrapper(new MyClass().getClass());
        wrapper.invokeMethod(new MyClass(), "say", new Class[] {String.class}, new String[] {"yes"});
    }

这里的打印结果为:

this is say yes

同时,生成的Wrapper的代码如下:

public class MyClassWapper {

    /**
     * "name","age"
     */
    public static String[] pns;

    /**
     * "name" -> "class java.lang.String"
     * "age" -> "int"
     */
    public static Map pts;

    /**
     * "setName","myMsg","getName","say","setAge","getAge"
     */
    public static String[] mns;

    /**
     * "setName","myMsg","getName","say","setAge","getAge"
     */
    public static String[] dmns;

    public String[] getPropertyNames(){ return pns; }

    public boolean hasProperty(String n){ return pts.containsKey(n); }

    public Class getPropertyType(String n){ return (Class)pts.get(n); }

    public String[] getMethodNames(){ return mns; }

    public String[] getDeclaredMethodNames(){ return dmns; }

    public void setPropertyValue(Object o, String n, Object v) {
        com.hui.wang.dubbo.learn.provider.wrapper.MyClass w;
        try {
            w = ((com.hui.wang.dubbo.learn.provider.wrapper.MyClass) o);
        } catch (Throwable e) {
            throw new IllegalArgumentException(e);
        }
        if (n.equals("name")) {
            w.setName((java.lang.String) v);
            return;
        }
        if (n.equals("age")) {
            w.setAge(((Number) v).intValue());
            return;
        }
        throw new com.alibaba.dubbo.common.bytecode.NoSuchPropertyException("Not found property \"" + n + "\" filed or setter method in class com.hui.wang.dubbo.learn.provider.wrapper.MyClass.");
    }

    public Object getPropertyValue(Object o, String n) {
        com.hui.wang.dubbo.learn.provider.wrapper.MyClass w;
        try {
            w = ((com.hui.wang.dubbo.learn.provider.wrapper.MyClass) o);
        } catch (Throwable e) {
            throw new IllegalArgumentException(e);
        }

        if (n.equals("name")) {
            return (String) w.getName();
        }
        if (n.equals("age")) {
            return (int) w.getAge();
        }
        throw new com.alibaba.dubbo.common.bytecode.NoSuchPropertyException("Not found property \"" + n + "\" filed or setter method in class com.hui.wang.dubbo.learn.provider.wrapper.MyClass.");
    }

    public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws java.lang.reflect.InvocationTargetException {
        com.hui.wang.dubbo.learn.provider.wrapper.MyClass w;
        try {
            w = ((com.hui.wang.dubbo.learn.provider.wrapper.MyClass) o);
        } catch (Throwable e) {
            throw new IllegalArgumentException(e);
        } try {
            if ("getName".equals(n) && p.length == 0) {
                return w.getName();
            }
            if ("setName".equals(n) && p.length == 1) {
                w.setName((java.lang.String) v[0]);
                return null;
            }
            if ("myMsg".equals(n) && p.length == 1) {
                return w.myMsg((java.lang.String) v[0]);
            }
            if ("say".equals(n) && p.length == 1) {
                w.say((java.lang.String) v[0]);
                return null;
            }
            if ("setAge".equals(n) && p.length == 1) {
                w.setAge(((Number) v[0]).intValue());
                return null;
            }
            if ("getAge".equals(n) && p.length == 0) {
                return w.getAge();
            }
        } catch (Throwable e) {
            throw new java.lang.reflect.InvocationTargetException(e);
        }
        throw new com.alibaba.dubbo.common.bytecode.NoSuchMethodException("Not found method \"" + n + "\" in class com.hui.wang.dubbo.learn.provider.wrapper.MyClass.");
    }
}

相信到这里,大家对WrappergetWrapper有了一定了解。

关于JavassistProxyFactorygetInvoker方法已经讲解完了,先是创建一个Wrapper Class ,然后使用刚才的Wrapper Class 实例,创建一个AbstractProxyInvoker类对象,并实现doInvoke方法。

下面贴出com.hui.wang.dubbo.learn.provider.service.MyServiceImpl生成的Warpper Class代码:

import com.alibaba.dubbo.common.bytecode.ClassGenerator;
import com.alibaba.dubbo.common.bytecode.NoSuchMethodException;
import com.alibaba.dubbo.common.bytecode.NoSuchPropertyException;
import com.alibaba.dubbo.common.bytecode.Wrapper;
import com.hui.wang.dubbo.learn.provider.service.MyServiceImpl;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;

public class Wrapper1 extends Wrapper implements ClassGenerator.DC {

    public static String[] pns;

    public static Map pts;

    public static String[] mns;

    public static String[] dmns;

    public static Class[] mts0;

    public String[] getPropertyNames() {
        return pns;
    }

    public boolean hasProperty(String string) {
        return pts.containsKey(string);
    }

    public Class getPropertyType(String string) {
        return (Class) pts.get(string);
    }

    public String[] getMethodNames() {
        return mns;
    }

    public String[] getDeclaredMethodNames() {
        return dmns;
    }

    public void setPropertyValue(Object object, String string, Object object2) {
        try {
            MyServiceImpl myServiceImpl = (MyServiceImpl) object;
        } catch (Throwable throwable) {
            throw new IllegalArgumentException(throwable);
        }
        throw new NoSuchPropertyException(new StringBuffer().append("Not found property \"").append(string).append("\" filed or setter method in class com.hui.wang.dubbo.learn.provider.service.MyServiceImpl.").toString());
    }

    public Object getPropertyValue(Object object, String string) {
        try {
            MyServiceImpl myServiceImpl = (MyServiceImpl) object;
        } catch (Throwable throwable) {
            throw new IllegalArgumentException(throwable);
        }
        throw new NoSuchPropertyException(new StringBuffer().append("Not found property \"").append(string).append("\" filed or setter method in class com.hui.wang.dubbo.learn.provider.service.MyServiceImpl.").toString());
    }

    public Object invokeMethod(Object object, String string, Class[] arrclass, Object[] arrobject) throws InvocationTargetException {
        MyServiceImpl myServiceImpl;
        try {
            myServiceImpl = (MyServiceImpl) object;
        } catch (Throwable throwable) {
            throw new IllegalArgumentException(throwable);
        }
        try {
            if ("say".equals(string) && arrclass.length == 1) {
                return myServiceImpl.say((String) arrobject[0]);
            }
        } catch (Throwable throwable) {
            throw new InvocationTargetException(throwable);
        }
        throw new NoSuchMethodException(new StringBuffer().append("Not found method \"").append(string).append("\" in class com.hui.wang.dubbo.learn.provider.service.MyServiceImpl.").toString());
    }
}
导出服务到本地

接下来,我们开始进入服务导出代码代码分析,先看导出服务到本地的过程,代码如下:

private void exportLocal(URL url) {
    // 如果 URL 的协议头等于 injvm,说明已经导出到本地了,无需再次导出
    if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
        URL local = URL.valueOf(url.toFullString())
            .setProtocol(Constants.LOCAL_PROTOCOL)    // 设置协议头为 injvm
            .setHost(LOCALHOST)
            .setPort(0);
        ServiceClassHolder.getInstance().pushServiceClass(getServiceClass(ref));
        // 创建 Invoker,并导出服务,这里的 protocol 会在运行时调用 InjvmProtocol 的 export 方法
        Exporter<?> exporter = protocol.export(
            proxyFactory.getInvoker(ref, (Class) interfaceClass, local));
        exporters.add(exporter);
    }
}

exportLocal 方法比较简单,首先根据 URL 协议头决定是否导出服务。若需导出,则创建一个新的 URL 并将协议头、主机名以及端口设置成新的值。然后创建Invoker,并调用 InjvmProtocolexport方法导出服务。下面我们来看一下InjvmProtocolexport方法都做了哪些事情。

    @Override
    public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
        return new InjvmInvoker<T>(serviceType, url, url.getServiceKey(), exporterMap);
    }

njvmProtocol 的 export 方法仅创建了一个 InjvmExporter,无其他逻辑。到此导出服务到本地就分析完了。

接下来我们分析导出服务到远程的过程。由于篇幅太多,我们放到下节中介绍。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,026评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,655评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,726评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,204评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,558评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,731评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,944评论 2 314
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,698评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,438评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,633评论 2 247
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,125评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,444评论 3 255
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,137评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,103评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,888评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,772评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,669评论 2 271

推荐阅读更多精彩内容