小象《金融数据分析》第二期

buildInvokerChain() 方法用于构建调用链,初步浏览下来发现调用链应该是由 Filter 扩展类构成。那么这些 Filter 扩展类又从何而来呢?这行代码很关键!!!

List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);

对于这段代码我们应该有很强的亲切感,但仔细看又稍稍有所不同。实际上被 @Activate 注解标记的扩展类会被加载到 ExtensionLoader 类的 cachedActivates 集合中。
我们在调用 ExtensionLoader 类的 getActivateExtension() 时,会根据我们传入的 key 和 group 值从 cachedActivates 集合中获取满足当前条件的 filter 对象。
拿到 filters 集合后,会用链表的形式拼接 filter 调用链,举个例子:
假设当前获取到的 filters 集合中保存的 filter 对象为 filter0、filter1、filter2。我们对 filters 集合进行倒序遍历。最后获得的 last 其实为新建的 ivk2 对象。如果我们调用 last 的 invoke 方法,调用链如下图所示:

小象《金融数据分析》第二期

End

本文介绍了 Export() 方法被调用的时机以及基本流程。并且花了一定篇幅对 Dubbo 服务本地暴露进行了分析。其中掺杂了不少代码的分析,可能没有面面俱到吧。还是建议大家自己自己 Debug 一下,很多东西瞬间秒懂,有助于源码理解。下一篇文章我们介绍 Dubbo 服务远程暴露。

appendProperties()

protected static void appendProperties(AbstractConfig config) {
    if (config == null) {
        return;
    }
    // getTagName:获取去除了 Bean/Config 结尾的小写类名(ApplicationConfig->application)
    String prefix = "dubbo." + getTagName(config.getClass()) + ".";
    Method[] methods = config.getClass().getMethods();
    for (Method method : methods) {
        try {
            String name = method.getName();
            // 1、方法长度大于3;2、方法以 set 开头;3、方法修饰符类型为 public;4、形参个数为 1;5、形参类型为基本类型
            if (name.length() > 3 && name.startsWith("set") && Modifier.isPublic(method.getModifiers())
                    && method.getParameterTypes().length == 1 && isPrimitive(method.getParameterTypes()[0])) {
                // camelToSplitName: 举个例子 ApplicationConfig——>application.config
                String property = StringUtils.camelToSplitName(name.substring(3, 4).toLowerCase() + name.substring(4), ".");

                String value = null;
                if (config.getId() != null && config.getId().length() > 0) {
                    // 拼接属性名称,并尝试获取对应属性
                    String pn = prefix + config.getId() + "." + property;
                    value = System.getProperty(pn);
                    if (!StringUtils.isBlank(value)) {
                        logger.info("Use System Property " + pn + " to config dubbo");
                    }
                }
                if (value == null || value.length() == 0) {
                    // 比如当前 config 为 ApplicationConfig,pn = dubbo.application.xxx
                    String pn = prefix + property;
                    value = System.getProperty(pn);
                    if (!StringUtils.isBlank(value)) {
                        logger.info("Use System Property " + pn + " to config dubbo");
                    }
                }
                if (value == null || value.length() == 0) {
                    Method getter;
                    try {
                        getter = config.getClass().getMethod("get" + name.substring(3));
                    } catch (NoSuchMethodException e) {
                        try {
                            getter = config.getClass().getMethod("is" + name.substring(3));
                        } catch (NoSuchMethodException e2) {
                            getter = null;
                        }
                    }
                    if (getter != null) {
                        if (getter.invoke(config) == null) {
                            // 尝试使用 ConfigUtils.getProperty() 方法获取属性值
                            // 尝试从 dubbo.properties.file 文件或 dubbo.properties 文件中读取属性
                            if (config.getId() != null && config.getId().length() > 0) {
                                value = ConfigUtils.getProperty(prefix + config.getId() + "." + property);
                            }
                            if (value == null || value.length() == 0) {
                                value = ConfigUtils.getProperty(prefix + property);
                            }
                            if (value == null || value.length() == 0) {
                                String legacyKey = legacyProperties.get(prefix + property);
                                if (legacyKey != null && legacyKey.length() > 0) {
                                    value = convertLegacyValue(legacyKey, ConfigUtils.getProperty(legacyKey));
                                }
                            }

                        }
                    }
                }
                if (value != null && value.length() > 0) {
                    method.invoke(config, convertPrimitive(method.getParameterTypes()[0], value));
                }
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
    }
}

appendParameters()

protected static void appendParameters(Map<String, String> parameters, Object config) {
    appendParameters(parameters, config, null);
}
protected static void appendParameters(Map<String, String> parameters, Object config, String prefix) {
    if (config == null) {
        return;
    }
    Method[] methods = config.getClass().getMethods();
    // 遍历 config 类方法集合
    for (Method method : methods) {
        try {
            String name = method.getName();
            // 找到满足以下的方法:以set/is 开头,非 getClass;方法修饰符为 public;方法参数个数为 0;返回类型为基本类型
            if ((name.startsWith("get") || name.startsWith("is"))
                    && !"getClass".equals(name)
                    && Modifier.isPublic(method.getModifiers())
                    && method.getParameterTypes().length == 0
                    && isPrimitive(method.getReturnType())) {
                // 获取 parameter 注解
                Parameter parameter = method.getAnnotation(Parameter.class);
                // @Parameter(excluded = true),直接跳过
                if (method.getReturnType() == Object.class || parameter != null && parameter.excluded()) {
                    continue;
                }
                int i = name.startsWith("get") ? 3 : 2;
                String prop = StringUtils.camelToSplitName(name.substring(i, i + 1).toLowerCase() + name.substring(i + 1), ".");
                String key;
                if (parameter != null && parameter.key().length() > 0) {
                    key = parameter.key();
                } else {
                    key = prop;
                }
                // 利用反射调用 config 类中的 get/is 方法
                Object value = method.invoke(config);
                String str = String.valueOf(value).trim();
                if (value != null && str.length() > 0) {
                    // 是否需要转义,UTF-8
                    if (parameter != null && parameter.escaped()) {
                        str = URL.encode(str);
                    }
                    if (parameter != null && parameter.append()) {
                        String pre = parameters.get(Constants.DEFAULT_KEY + "." + key);
                        if (pre != null && pre.length() > 0) {
                            str = pre + "," + str;
                        }
                        pre = parameters.get(key);
                        if (pre != null && pre.length() > 0) {
                            str = pre + "," + str;
                        }
                    }
                    if (prefix != null && prefix.length() > 0) {
                        key = prefix + "." + key;
                    }
                    // key/value 添加到 parameters 集合
                    parameters.put(key, str);
                } else if (parameter != null && parameter.required()) {
                    throw new IllegalStateException(config.getClass().getSimpleName() + "." + key + " == null");
                }
                // 方法名为 getParameters();方法修饰符为 public;方法形参个数为0;返回类型为 Map
            } else if ("getParameters".equals(name)
                    && Modifier.isPublic(method.getModifiers())
                    && method.getParameterTypes().length == 0
                    && method.getReturnType() == Map.class) {
                Map<String, String> map = (Map<String, String>) method.invoke(config, new Object[0]);
                if (map != null && map.size() > 0) {
                    String pre = (prefix != null && prefix.length() > 0 ? prefix + "." : "");
                    for (Map.Entry<String, String> entry : map.entrySet()) {
                        parameters.put(pre + entry.getKey().replace('-', '.'), entry.getValue());
                    }
                }
            }
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
    }
}

该方法会调用当前类对象的 isXXX/getXXX 方法(非 getClass 方法;方法修饰符为 public;形参个数为 0;返回类型为基本类型),获取其返回值构造键值对添加到指定 map 集合中;同时也会解析 getParameters() 返回的结果,构造键值对注入到 map 集合中。

本BLOG上原创文章未经本人许可,不得用于商业用途及传统媒体。网络媒体转载请注明出处,否则属于侵权行为。https://juejin.im/post/5c2b7ab46fb9a049d236273b