分布式事务——hmily SPI机制源码分析

分布式事务——hmily SPI机制源码分析

1. 背景

hmily事务日志存储支持Mysqlmongodbrediszookeeper等,支持Spring CloudDubbosofa-rpc等主流RPC框架,而hmily这种插件式设计是通过SPI机制实现的。接下来通过源码分析下hmily的SPI机制

2. 源码

hmily SPI使用示例:在选择分布式事务模式的时候,有TCCTAC两种实现

HmilyTransactionAspectInvoker

static {
    FACTORY_MAP.put(TransTypeEnum.TCC, ExtensionLoaderFactory.load(HmilyTransactionHandlerFactory.class, "tcc"));
    FACTORY_MAP.put(TransTypeEnum.TAC, ExtensionLoaderFactory.load(HmilyTransactionHandlerFactory.class, "tac"));
}

hmily中使用到了如下的方法来通过SPI机制获取接口实现

// 通过接口类获取实现类实例,适用于只需要加载一种实现的情况
ExtensionLoaderFactory.load(Class<T> service);

// 通过接口类和SPI名称获取实现类实例,适用于需要加载多种实现,通过名称区分的情况
ExtensionLoaderFactory.load(Class<T> service, String name);

// 通过接口类加载所有实现类实例
ExtensionLoaderFactory.loadAll(Class<T> service);

1. ExtensionLoaderFactory.load(Class service)

ExtensionLoaderFactory

public static <T> T load(final Class<T> service) {
    return ExtensionLoader.getExtensionLoader(service).load(findClassLoader());
}

/**
 * 获取当前类的ClassLoader
 */
private static ClassLoader findClassLoader() {
    return ExtensionLoaderFactory.class.getClassLoader();
}

ExtensionLoader

// <ServiceInterface.class, ExtensionLoader<>(clazz)>
private static final Map<Class<?>, ExtensionLoader<?>> LOADERS = new ConcurrentHashMap<>();

// 存储List<ExtensionEntity>
private final Holder<List<ExtensionEntity>> entitiesHolder = new Holder<>();

// 保存扩展类实例
// <HmilySPI.name, 实例>
private final Map<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();

// <HmilySPI.name, ExtensionEntity>
private final Map<String, ExtensionEntity> nameToEntityMap = new ConcurrentHashMap<>();

// <Class, ExtensionEntity>
private final Map<Class<?>, ExtensionEntity> classToEntityMap = new ConcurrentHashMap<>();

/**
 * 创建ExtensionLoader
 */
public static <T> ExtensionLoader<T> getExtensionLoader(final Class<T> clazz) {
    if (clazz == null) {
        throw new NullPointerException("extension clazz is null");
    }
    if (!clazz.isInterface()) {
        throw new IllegalArgumentException("extension clazz (" + clazz + "is not interface!");
    }
    ExtensionLoader<T> extensionLoader = (ExtensionLoader<T>) LOADERS.get(clazz);
    if (extensionLoader != null) {
        return extensionLoader;
    }
    LOADERS.putIfAbsent(clazz, new ExtensionLoader<>(clazz));
    return (ExtensionLoader<T>) LOADERS.get(clazz);
}

public T load(final ClassLoader loader) {
    return loadExtension(loader);
}

private T loadExtension(final ClassLoader loader) {
    // 加载对应interface class的扩展类
    loadAllExtensionClass(loader);
    // 找到第一个扩展类
    ExtensionEntity extensionEntity = getDefaultExtensionEntity();
    // 根据class创建实例
    return getExtensionInstance(extensionEntity, null, null);
}

loadAllExtensionClass: 加载对应interface class的扩展类

private List<Class<?>> loadAllExtensionClass(final ClassLoader loader) {
    List<ExtensionEntity> entityList = entitiesHolder.getValue();
    if (null == entityList) {
        synchronized (entitiesHolder) {
            entityList = entitiesHolder.getValue();
            if (null == entityList) {
                // 获取interface class扩展实体ExtensionEntity
                entityList = findAllExtensionEntity(loader);
                entitiesHolder.setValue(entityList);
            }
        }
    }
    // 将serviceClass属性整合
    return entityList.stream().map(ExtensionEntity::getServiceClass).collect(Collectors.toList());
}

/**
 * 解析META-INF/hmily/下的内容,获取@HmilySPI属性转为ExtensionEntity对象返回
 */
private List<ExtensionEntity> findAllExtensionEntity(final ClassLoader loader) {
    List<ExtensionEntity> entityList = new ArrayList<>();
    // 在目录META-INF/hmily/接口全路径的文件内容加载成对应的class类,从类中获取@HmilySPI注解属性保存到entityList
    loadDirectory(HMILY_DIRECTORY + clazz.getName(), loader, entityList);
    return entityList.stream().sorted(Comparator.comparing(ExtensionEntity::getOrder)).collect(Collectors.toCollection(LinkedList::new));
}

private void loadDirectory(final String dir, final ClassLoader classLoader, final List<ExtensionEntity> entityList) {
    try {
        // 通过ClassLoader获取目录META-INF/hmily/接口全路径下文件地址
        Enumeration<URL> urls = classLoader != null ? classLoader.getResources(dir) : ClassLoader.getSystemResources(dir);
        if (urls != null) {
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                // 加载目录下所有的文件
                loadResources(entityList, url, classLoader);
            }
        }
    } catch (IOException t) {
        LOGGER.error("load extension class error {}", dir, t);
    }
}

private void loadResources(final List<ExtensionEntity> entityList, final URL url, final ClassLoader classLoader) {
    try (InputStream inputStream = url.openStream()) {
        // Properties属性来加载文件内容
        Properties properties = new Properties();
        properties.load(inputStream);
        // 循环文件内容属性
        properties.forEach((k, v) -> {
            String name = (String) k;
            if (null != name && !"".equals(name)) {
                try {
                    // 加载对应的class保存到entityList
                    loadClass(entityList, name, classLoader);
                } catch (ClassNotFoundException e) {
                    LOGGER.warn("Load [{}] class fail. {}", name, e.getMessage());
                }
            }
        });
    } catch (IOException e) {
        throw new IllegalStateException("load extension resources error", e);
    }
}

private void loadClass(final List<ExtensionEntity> entityList, final String className, final ClassLoader loader) throws ClassNotFoundException {
    if (!containsClazz(className, loader)) {
        // 将类全路径构造成Class类型
        Class<?> clazz = Class.forName(className, true, loader);
        if (!clazz.isAssignableFrom(clazz)) {
            throw new IllegalStateException("load extension resources error," + clazz + " subtype is not of " + clazz);
        }
        String name = null;
        int order = 0;
        ScopeType scope = ScopeType.SINGLETON;
        HmilySPI hmilySPI = clazz.getAnnotation(HmilySPI.class);
        if (null != hmilySPI) {
            name = hmilySPI.value();
            order = hmilySPI.order();
            scope = hmilySPI.scopeType();
        }
        // 构造ExtensionEntity,其中包含了@HmilySPI注解的内容
        ExtensionEntity result = new ExtensionEntity(name, clazz, order, scope);
        entityList.add(result);
        classToEntityMap.put(clazz, result);
        if (null != name) {
            nameToEntityMap.put(name, result);
        }
    }
}

getDefaultExtensionEntity():获取第一个扩展类

private ExtensionEntity getDefaultExtensionEntity() {
    return entitiesHolder.getValue().stream().findFirst().orElse(null);
}

getExtensionInstance():根据class创建实例

private T getExtensionInstance(final ExtensionEntity entity, final Class<?>[] argTypes, final Object[] args) {
    if (entity == null) {
        log.error("not found service provider for : " + clazz.getName());
        return null;
    }
    // 默认单例
    if (ScopeType.SINGLETON.equals(entity.getScopeType())) {
        Holder<Object> holder = cachedInstances.get(entity.getName());
        if (holder == null) {
            cachedInstances.putIfAbsent(entity.getName(), new Holder<>());
            holder = cachedInstances.get(entity.getName());
        }
        Object instance = holder.getValue();
        if (instance == null) {
            synchronized (cachedInstances) {
                instance = holder.getValue();
                if (instance == null) {
                    // 创建扩展类实例
                    instance = createNewExtension(entity, argTypes, args);
                    holder.setValue(instance);
                }
            }
        }
        return (T) instance;
    } else {
        return createNewExtension(entity, argTypes, args);
    }
}

private T createNewExtension(final ExtensionEntity entity, final Class<?>[] argTypes, final Object[] args) {
    try {
        return initInstance(entity.getServiceClass(), argTypes, args);
    } catch (Exception t) {
        throw new IllegalStateException("Extension instance(entity: " + entity + ", class: " + clazz + ")  could not be instantiated: " + t.getMessage(), t);
    }
}

private T initInstance(final Class<?> implClazz, final Class<?>[] argTypes, final Object[] args)
    throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
    // 通过反射实例化扩展类
    T result;
    if (null != argTypes && null != args) {
        Constructor<?> constructor = implClazz.getDeclaredConstructor(argTypes);
        result = clazz.cast(constructor.newInstance(args));
    } else {
        // clazz是implClazz的接口定义
        result = clazz.cast(implClazz.newInstance());
    }
    if (result instanceof InitializeSPI) {
        ((InitializeSPI) result).init();
    }
    return result;
}

整体流程图:
在这里插入图片描述

2. ExtensionLoaderFactory.load(Class service, String name)

ExtensionLoader

public T load(final String name, final ClassLoader loader) {
    return loadExtension(name, loader, null, null);
}

private T loadExtension(final String name, final ClassLoader loader, final Class<?>[] argTypes, final Object[] args) {
    loadAllExtensionClass(loader);
    // 根据名称从缓存中获取对应的ExtensionEntity
    ExtensionEntity extensionEntity = getCachedExtensionEntity(name);
    return getExtensionInstance(extensionEntity, argTypes, args);
}

方法1主要的区别是getCachedExtensionEntity()通过名称从缓存中获取ExtensionEntity

private ExtensionEntity getCachedExtensionEntity(final String name) {
    // 根据@HmilySPI中的name属性获取ExtensionEntity
    return nameToEntityMap.get(name);
}

这样就实现了根据指定名称获取实现类了。

3. ExtensionLoaderFactory.loadAll(Class service)

ExtensionLoader

public List<T> loadAll(final ClassLoader loader) {
    return loadAll(null, null, loader);
}


private List<T> loadAll(final Class<?>[] argsType, final Object[] args, final ClassLoader loader) {
    // 获取所有对应接口实现类class
    List<Class<?>> allClazzs = getAllExtensionClass(loader);
    if (allClazzs.isEmpty()) {
        return Collections.emptyList();
    }
    // 返回所有实现类实例
    return allClazzs.stream().map(t -> {
        ExtensionEntity extensionEntity = classToEntityMap.get(t);
        return getExtensionInstance(extensionEntity, argsType, args);
    }).collect(Collectors.toList());
}

3. 总结

SPI机制在框架设计中使用得很多,比如著名的Dubbo SPI,还有Spring Boot中的spring.factories等,能实现插件化的设计,在开发中间件的时候是一种很不错的思路。



世界那么大,感谢遇见,未来可期…

欢迎同频共振的那一部分人

作者公众号:Tarzan写bug

淘宝店:Tarzan小店 提供一元解决Java问题和其他方面的解决方案,欢迎咨询

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值