learn-tech/专栏/Dubbo源码解读与实战-完/39加餐:多个返回值不用怕,Merger合并器来帮忙.md
2024-10-16 00:01:16 +08:00

12 KiB
Raw Blame History

                        因收到Google相关通知网站将会择期关闭。相关通知内容
                        
                        
                        39  加餐多个返回值不用怕Merger 合并器来帮忙
                        你好,我是杨四正,今天我和你分享的主题是 Merger 合并器。

在上一课时中,我们分析 MergeableClusterInvoker 的具体实现时讲解过这样的内容MergeableClusterInvoker 中会读取 URL 中的 merger 参数值,如果 merger 参数以 “.” 开头,则表示 “.” 后的内容是一个方法名这个方法名是远程目标方法的返回类型中的一个方法MergeableClusterInvoker 在拿到所有 Invoker 返回的结果对象之后,会遍历每个返回结果,并调用 merger 参数指定的方法,合并这些结果值。

其实,除了上述指定 Merger 方法名称的合并方式之外Dubbo 内部还提供了很多默认的 Merger 实现,这也就是本课时将要分析的内容。本课时将详细介绍 MergerFactory 工厂类、Merger 接口以及针对 Java 中常见数据类型的 Merger 实现。

MergerFactory

在 MergeableClusterInvoker 使用默认 Merger 实现的时候,会通过 MergerFactory 以及服务接口返回值类型returnType选择合适的 Merger 实现。

在 MergerFactory 中维护了一个 ConcurrentHashMap 集合(即 MERGER_CACHE 字段),用来缓存服务接口返回值类型与 Merger 实例之间的映射关系。

MergerFactory.getMerger() 方法会根据传入的 returnType 类型,从 MERGER_CACHE 缓存中查找相应的 Merger 实现,下面我们来看该方法的具体实现:

public static Merger getMerger(Class returnType) {

if (returnType == null) { // returnType为空直接抛出异常

    throw new IllegalArgumentException("returnType is null");

}

Merger result;

if (returnType.isArray()) { // returnType为数组类型

    // 获取数组中元素的类型

    Class type = returnType.getComponentType();

    // 获取元素类型对应的Merger实现

    result = MERGER_CACHE.get(type);

    if (result == null) {

        loadMergers();

        result = MERGER_CACHE.get(type);

    }

    // 如果Dubbo没有提供元素类型对应的Merger实现则返回ArrayMerger

    if (result == null && !type.isPrimitive()) {

        result = ArrayMerger.INSTANCE;

    }

} else {

    // 如果returnType不是数组类型则直接从MERGER_CACHE缓存查找对应的Merger实例

    result = MERGER_CACHE.get(returnType);

    if (result == null) {

        loadMergers();

        result = MERGER_CACHE.get(returnType);

    }

}

return result;

}

loadMergers() 方法会通过 Dubbo SPI 方式加载 Merger 接口全部扩展实现的名称,并填充到 MERGER_CACHE 集合中,具体实现如下:

static void loadMergers() {

// 获取Merger接口的所有扩展名称

Set<String> names = ExtensionLoader.getExtensionLoader(Merger.class)

        .getSupportedExtensions();

for (String name : names) { // 遍历所有Merger扩展实现

    Merger m = ExtensionLoader.getExtensionLoader(Merger.class).getExtension(name);

    // 将Merger实例与对应returnType的映射关系记录到MERGER_CACHE集合中

    MERGER_CACHE.putIfAbsent(ReflectUtils.getGenericClass(m.getClass()), m);

}

}

ArrayMerger

在 Dubbo 中提供了处理不同类型返回值的 Merger 实现,其中不仅有处理 boolean[]、byte[]、char[]、double[]、float[]、int[]、long[]、short[] 等基础类型数组的 Merger 实现,还有处理 List、Set、Map 等集合类的 Merger 实现,具体继承关系如下图所示:

Merger 继承关系图

我们首先来看 ArrayMerger 实现:当服务接口的返回值为数组的时候,会使用 ArrayMerger 将多个数组合并成一个数组也就是将二维数组拍平成一维数组。ArrayMerger.merge() 方法的具体实现如下:

public Object[] merge(Object[]... items) {

if (ArrayUtils.isEmpty(items)) {

    // 传入的结果集合为空,则直接返回空数组

    return new Object[0];

}

int i = 0;

// 查找第一个不为null的结果

while (i < items.length && items[i] == null) {

    i++;

}

// 所有items数组中全部结果都为null则直接返回空数组

if (i == items.length) {

    return new Object[0];

}

Class<?> type = items[i].getClass().getComponentType();

int totalLen = 0;

for (; i < items.length; i++) {

    if (items[i] == null) { // 忽略为null的结果

        continue;

    }

    Class<?> itemType = items[i].getClass().getComponentType();

    if (itemType != type) { // 保证类型相同

        throw new IllegalArgumentException("Arguments' types are different");

    }

    totalLen += items[i].length;

}

if (totalLen == 0) { // 确定最终数组的长度

    return new Object[0];

}

Object result = Array.newInstance(type, totalLen);

int index = 0;

// 遍历全部的结果数组将items二维数组中的每个元素都加到result中形成一维数组

for (Object[] array : items) {

    if (array != null) {

        for (int j = 0; j < array.length; j++) {

            Array.set(result, index++, array[j]);

        }

    }

}

return (Object[]) result;

}

其他基础数据类型数组的 Merger 实现,与 ArrayMerger 的实现非常类似,都是将相应类型的二维数组拍平成同类型的一维数组,这里以 IntArrayMerger 为例进行分析:

public int[] merge(int[]... items) {

if (ArrayUtils.isEmpty(items)) {

    // 检测传入的多个int[]不能为空

    return new int[0];

}

// 直接使用Stream的API将多个int[]数组拍平成一个int[]数组

return Arrays.stream(items).filter(Objects::nonNull)

        .flatMapToInt(Arrays::stream)

        .toArray();

}

剩余的其他基础类型的 Merger 实现类例如FloatArrayMerger、IntArrayMerger、LongArrayMerger、BooleanArrayMerger、ByteArrayMerger、CharArrayMerger、DoubleArrayMerger 等,这里就不再赘述,你若感兴趣的话可以参考源码进行学习。

MapMerger

SetMerger、ListMerger 和 MapMerger 是针对 Set 、List 和 Map 返回值的 Merger 实现,它们会将多个 Set或 List、Map集合合并成一个 Set或 List、Map集合核心原理与 ArrayMerger 的实现类似。这里我们先来看 MapMerger 的核心实现:

public Map merge(Map... items) {

if (ArrayUtils.isEmpty(items)) {

    // 空结果集时这就返回空Map

    return Collections.emptyMap();

}

// 将items中所有Map集合中的KV添加到result这一个Map集合中

Map<Object, Object> result = new HashMap<Object, Object>();

Stream.of(items).filter(Objects::nonNull).forEach(result::putAll);

return result;

}

接下来再看 SetMerger 和 ListMerger 的核心实现:

public Set