Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

关于扩展点SPI方案的重构 #1092

Closed
yanrongzhen opened this issue Mar 10, 2023 · 6 comments · Fixed by #1102
Closed

关于扩展点SPI方案的重构 #1092

yanrongzhen opened this issue Mar 10, 2023 · 6 comments · Fixed by #1102
Assignees
Labels
type: refactor Code refactoring work
Milestone

Comments

@yanrongzhen
Copy link
Member

yanrongzhen commented Mar 10, 2023

需求建议

重构扩展点(SPI)功能

现有的扩展点的注册和使用都是通过ServiceLoader实现.
(cn.hippo4j.common.spi.DynamicThreadPoolServiceLoader)

有什么问题:

  1. 每次使用前都要去关注是否注册到了register map中.
  2. 由于一个扩展点接口可能拥有多个实现, 需要写很多冗余的代码去触发和判断List当中是否有满足条件的结果.

怎么解决:

注册的问题 - 结合spring

在Spring启动时, 扫描容器中所有被打上@Realization(表示扩展点的实现类) 注解的 Bean, 并注册到ExtensionRegistry当中, 是一个 Map<Class<?>, List> extensionMap (扩展点接口类 <-> 扩展点实现list) 的注册表.
使用时, 通过调用的扩展点接口类, 在注册表当中找到对应的实现类对象List.

结果处理的问题 - 引入结果规约处理器Reducer

Reducer本质上是一个DataCollector, 通过统一的ExtensionInvoker触发器, 传入扩展点接口class, callback行为, 和一个Reducer.

触发代码可能如下: (比如场景是想找到第一个结果为非空的MyObject对象)

MyObject result = ExtensionInvoker.reduceExecute
                (ITestExtension.class, ITestExtension::foo, request, Reducers.firstOf(Objects::nonNull));

函数原型:

public static <T extends IExtension, E, R> R reduceExecute(Class<T> targetClz, ExtensionCallback<T, E> callback,
                                                               Reducer<T, E, R> reducer);

这样我们就可以通过一行代码干净的解决扩展注册/寻找和结果处理的问题

规约函数, 可能出现如下几种类型:

  1. ResultObj FirstOf(Predicate p) -- 第一个遇到结果满足p的, 直接返回
  2. Boolean AllMatch(Predicate p) -- 判断执行结果是否都满足p
  3. Boolean AnyMatch(Predicate p) -- 判断任一执行结果是否满足p
  4. .... 可以继续扩展规约函数 (FlatMap, FlatList 等等)
@magestacks magestacks added the type: refactor Code refactoring work label Mar 10, 2023
@weihubeats
Copy link
Member

挺好的建议,但是这样改造后我们在非spring环境下就无法使用spi了,spi作为一个基础组件就和spring绑定了

@Createsequence
Copy link
Collaborator

Createsequence commented Mar 10, 2023

我尝试理解一下你的思路,目前是 SPI 机制存在两个问题:

  • 使用前必须要手动的确认扩展点已经注册,因此会存着较多诸如 InstanceInfoProviderFactory 这样,在静态块中手动注册的情况,不够优雅,也无法集中的管理注册逻辑;
  • 同接口的实现可能有多个,目前的 API 并不能很方便根据需求的完成对一批实现的过滤或者匹配操作;

而这个 issues 的核心应该旨是在于优化这两个痛点:

  • 第一个问题依靠 Spring 的自动装配解决,在某个类集中的将所有的扩展 Bean 注册到注册表中(某种程度上来说就是 BeanFactory 本身?);
  • 第二个则是添加新的 API 或者过滤器,让我们根据特定的条件快速获得需要的实现类;

不知我这么理解对吗?

@yanrongzhen
Copy link
Member Author

挺好的建议,但是这样改造后我们在非spring环境下就无法使用spi了,spi作为一个基础组件就和spring绑定了

@weihubeats 是的,不过我理解hippo4j本身不可能脱离spring。这种模式是对于传统spi方式的一种spring增强,两种模式可以共存

@yanrongzhen
Copy link
Member Author

yanrongzhen commented Mar 12, 2023

我尝试理解一下你的思路,目前是 SPI 机制存在两个问题:

  • 使用前必须要手动的确认扩展点已经注册,因此会存着较多诸如 InstanceInfoProviderFactory 这样,在静态块中手动注册的情况,不够优雅,也无法集中的管理注册逻辑;
  • 同接口的实现可能有多个,目前的 API 并不能很方便根据需求的完成对一批实现的过滤或者匹配操作;

而这个 issues 的核心应该旨是在于优化这两个痛点:

  • 第一个问题依靠 Spring 的自动装配解决,在某个类集中的将所有的扩展 Bean 注册到注册表中(某种程度上来说就是 BeanFactory 本身?);
  • 第二个则是添加新的 API 或者过滤器,让我们根据特定的条件快速获得需要的实现类;

不知我这么理解对吗?

@Createsequence 是的,可以这么理解。不过第二点中更准确点是获取了我们希望的扩展点执行结果。至于由哪个实现类提供取决于我们对于结果的预期(比如 第一个非空结果)。

@yanrongzhen
Copy link
Member Author

@magestacks 可以指派给我, 我来试下

@magestacks magestacks added this to the 1.5.0 milestone Mar 13, 2023
@weihubeats
Copy link
Member

@magestacks 可以指派给我, 我来试下

Very welcome

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: refactor Code refactoring work
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants