分布式事务——hmily SPI机制源码分析
1. 背景
hmily
事务日志存储支持Mysql
、mongodb
、redis
、zookeeper
等,支持Spring Cloud
、Dubbo
、sofa-rpc
等主流RPC框架,而hmily
这种插件式设计是通过SPI机制
实现的。接下来通过源码分析下hmily的SPI机制
。
2. 源码
hmily SPI
使用示例:在选择分布式事务模式的时候,有TCC
和TAC
两种实现
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问题和其他方面的解决方案,欢迎咨询