-
组件概述
1.1 要解决的核心问题
在业务系统开发中,经常遇到这样的场景:同一个业务接口(如支付、风控、消息推送),需要根据不同的业务场景(如不同的客户、渠道、产品类型)切换到不同的实现算法。 传统的做法可能是使用大量的 if-else或 switch-case语句进行硬编码,导致代码臃肿、重复率高、可维护性差,且每次新增场景都需要修改原有代码,违反开闭原则。本组件旨在提供一套优雅、灵活、非侵入式的解决方案,使得业务逻辑能够根据场景码(BizScenario)自动路由到对应的实现类,从而实现业务的柔性扩展和高效治理。
1.2 设计目标
- 解耦:将接口的定义与其具体实现完全分离。
- 动态路由:在运行时,根据传入的业务场景信息,动态选择并执行合适的实现。
- 开闭原则:系统支持扩展新的实现类,而无需修改现有的核心代码和路由逻辑。
- 易于集成:通过与Spring框架无缝集成,实现扩展点的自动扫描和注册。
-
核心设计理念
该组件的设计深受策略模式和微内核架构的影响。其核心思想是将每个业务场景的具体实现视为一个可插拔的“策略”或“插件”。框架本身作为一个“微内核”,负责管理这些插件的生命周期和路由策略,而不关心插件的具体业务逻辑。
3. ## 核心模块详解
3.1 元数据与建模(BizScenario, ExtensionCoordinate)
这是框架的基石,用于精确描述一个扩展点实例。
BizScenario(业务场景):定义了路由的维度。通过domain(领域)、scene(场景)等属性,唯一确定一个业务身份。其getUniqueIdentity()方法生成的路由键是寻址的关键。ExtensionCoordinate(扩展点坐标):是BizScenario和 扩展点接口类型 的组合。它构成了存储和查找扩展点的唯一键(Key),其正确重写的equals和hashCode方法是保证在Map中正确工作的前提。
3.2 注册中心(ExtensionRepository)
这是框架的服务注册表,扮演着核心的存储角色。
- 职责:维护一个
ConcurrentHashMap<ExtensionCoordinate, ExtensionPoint>,用于存储所有注册到容器中的扩展点实例。 - 特点:线程安全,提供了最基本的注册(
registerExtPoint)和查询(getExtension)方法。
3.3 自动注册器(ExtensionAutoRegister)
这是框架与Spring容器集成的关键,实现了“服务自动发现”。
-
时机:在Spring容器启动完成后,通过
@PostConstruct注解触发。 -
过程:
- 扫描:利用Spring的
ApplicationContext,获取所有带有@Extension注解的Bean。 - 注册:遍历这些Bean,并将其交给
ExtensionRegister完成具体的注册逻辑。
- 扫描:利用Spring的
3.4 注册逻辑核心(ExtensionRegister)
这是“服务注册” 逻辑的核心,负责处理每个扩展点实现类的元信息并将其注册到仓库中。
-
关键步骤:
- 解析注解:从实现类上解析
@Extension注解,获取domain和scene信息,构建BizScenario对象。 - 计算扩展点接口:通过
calculateExtensionPoint方法,遍历实现类的所有接口,找到那个直接或间接继承自标记接口ExtensionPoint的接口。该接口的全限定名是路由的另一半关键信息。 - 查重与注册:将
BizScenario和接口类型组合成ExtensionCoordinate,检查是否已存在,若无则注册到ExtensionRepository。
- 解析注解:从实现类上解析
3.5 执行引擎(ExtensionExecutor)
这是框架面向用户的API网关,是“服务发现与调用” 的入口。
-
设计:继承自
AbstractComponentExecutor,模板方法模式封装了执行流程。 -
核心方法:
<T> T locateComponent(Class<T> targetClz, BizScenario bizScenario) -
双保险路由策略:
- 首先尝试根据精确的
BizScenario查找实现。 - 若未找到,则尝试使用默认的
BizScenario(即domain和scene为默认值)进行查找,这为实现全局默认逻辑提供了可能。 - 若仍未找到,则抛出异常。
- 首先尝试根据精确的
-
如何使用:四步上手
4.1 第一步:引入jar包
4.2 第二步:定义扩展点接口
接口需要直接或间接继承标记接口 ExtensionPoint。
public interface PaymentService extends ExtensionPoint {
PayResponse pay(PayRequest request);
}
4.3 第三步:实现扩展点(使用 @Extension注解)
为实现类添加 @Extension注解,并指定其所属的业务场景。
@Component
@Extension(domain = "payment", scene = "wechat", value = "微信支付场景")
public class WechatPaymentService implements PaymentService {
@Override
public PayResponse pay(PayRequest request) {
// 微信支付的具体逻辑
return new PayResponse("Wechat Pay Success");
}
}
@Component
@Extension(domain = "payment", scene = "alipay", value = "支付宝支付场景")
public class AlipayPaymentService implements PaymentService {
@Override
public PayResponse pay(PayRequest request) {
// 支付宝支付的具体逻辑
return new PayResponse("Alipay Pay Success");
}
}
4.4 第四步:在代码中调用执行
在Service或Controller中,注入 ExtensionExecutor,通过业务场景执行相应逻辑。
@RestController
public class PaymentController {
@Resource
private ExtensionExecutor extensionExecutor;
@PostMapping("/pay")
public PayResponse pay(@RequestParam String channel, @RequestBody PayRequest request){
// 1. 构建业务场景
BizScenario bizScenario = new BizScenario.Builder()
.domain("payment")
.scene(channel) // 如 "wechat", "alipay"
.build();
// 2. 执行扩展点
return extensionExecutor.execute(PaymentService.class, bizScenario, paymentService -> paymentService.pay(request));
}
}
4.5 第五步:享受自动化带来的便利
无需手动管理 PaymentService的实现类。框架会自动扫描、注册它们。当需要新增一种支付方式(例如“云闪付”)时,只需:
- 创建一个新的
YunshanPaymentService实现类并加上@Extension(domain = "payment", scene = "unionpay")注解。 - 重新启动应用即可。
-
总结与优势
5.1 设计模式汇总
| 设计模式 | 解决的核心问题 | 在组件中的价值 |
|---|---|---|
| 策略模式 | 避免大量的if-else分支 | 核心价值:实现基于场景码的动态路由 |
| 工厂模式 | 对象创建的复杂性 | 统一管理核心组件的实例化 |
| 模板方法 | 算法骨架的复用 | 封装统一的扩展点执行流程 |
| 注册表模式 | 服务的集中管理 | 提供高效的服务查找和路由 |
5.2 架构价值
- 架构清晰:各组件职责单一,符合单一职责原则。
- 非侵入性:业务实现类只需添加一个注解,对原有代码无侵入。
- 高可扩展性:新增业务场景实现无需修改任何框架和路由代码。
- 与Spring生态无缝集成:利用Spring的IoC容器进行Bean管理和依赖注入。
- 类型安全:在编译期即可保证类型匹配,避免运行时类型转换错误。
5.3 简化调用与提升开发效率
函数式编程的结合使得组件调用变得非常简洁,直接提升了开发效率。
- Lambda表达式简化调用:使用者无需编写繁琐的匿名内部类,可以直接用Lambda表达式或方法引用简洁地表达业务逻辑。这使得代码非常紧凑,可读性更强。
// 传统的命令式风格可能需要这样:
ExtensionPoint component = executor.locateComponent(SomeInterface.class, bizScenario);
SomeResult result = component.doSomething(input);
// 而使用函数式设计,只需要一行:
SomeResult result = executor.execute(SomeInterface.class, bizScenario, comp -> comp.doSomething(input));
设计思路:模板方法封装固定流程:将“定位组件 -> 执行逻辑”这个固定流程封装在 execute方法中,使用者只需关注核心业务逻辑(即 Function或 Consumer中的代码)。这极大地减少了模板代码,让业务逻辑更加内聚和清晰。
5.4 支持多样化的业务场景
通过区分有返回值和无返回值的操作,您的设计能够灵活应对不同的业务场景。
Function<T, R>用于有返回值的操作,例如查询、计算、数据转换等。Consumer<T>用于无返回值的操作,例如发送消息、更新状态、触发流程等。这种区分使得组件API更加精确,符合不同场景的语义要求。
5.5 进阶用法
这套框架非常适用于具有多租户、多渠道、多业务线等需要根据上下文进行动态路由的场景,能极大提升代码的整洁度和团队的开发效率。