功能注册引擎设计文档
1. 概述
1.1 设计目标
本设计实现了一个基于注解驱动的业务-模板注册引擎,通过 @UseTemplate 注解在启动时自动绑定业务实现类与对应的处理模板,实现业务逻辑与处理流程的解耦。
1.2 核心特性
- ✅ 注解驱动的自动注册机制
- ✅ 泛型类型安全检查
- ✅ 函数式编程风格
- ✅ Spring Boot 集成
- ✅ 灵活的模板化处理流程
2. 核心组件设计
2.1 RegisterEnginV3 - 注册引擎
职责:
- 启动时扫描所有 Business 实现类
- 解析
@UseTemplate注解,绑定业务与模板 - 执行泛型类型一致性检查
- 提供统一的业务执行入口
关键代码:
@Service
@AllArgsConstructor
public class RegisterEnginV3<T, S extends Business<T>> {
@PostConstruct
public void init() {
businessesMap.forEach((txcode, businessService) -> {
// 1. 解析 @UseTemplate 注解
UseTemplate ann = businessService.getClass()
.getAnnotation(UseTemplate.class);
// 2. 获取对应的 Template Bean
Template<T, S> templateToUse =
(Template<T, S>) applicationContext
.getBean(ann.value());
// 3. 泛型类型检查
checkConsistentGenericType(businessService, templateToUse);
// 4. 注册到执行注册表
registry.put(txcode,
param -> templateToUse.handler(txcode, param, businessService));
});
}
public void run(String businessType, T params) {
registry.get(businessType).accept(params);
}
}
泛型参数说明:
T: 业务处理的参数类型(通常是BusinessContext<I, O>)S: 具体的 Business 实现类型
2.2 Template - 处理模板接口
设计理念: 采用函数式接口设计,定义统一的业务处理流程。
@FunctionalInterface
public interface Template<T, S extends Business<T>> {
void handler(String txcode, T param, S businessService);
}
实现示例:
@Component
public class TemplateImpl3<I, O> implements
Template<BusinessContext<I, O>, BusinessType3<I, O>> {
@Override
public void handler(String txcode,
BusinessContext<I, O> param,
BusinessType3<I, O> businessService) {
// 前置处理
// ...
// 执行业务逻辑
businessService.doBusiness2(param);
// 后置处理
// ...
}
}
2.3 Business - 业务接口层次
接口层次设计:
Business<T> (标记接口)
↓
BusinessType3<I, O> extends Business<BusinessContext<I, O>>
↓
Business3 implements BusinessType3<InputDto3, OutputDto3>
Business 基础接口:
public interface Business<T> {
// 标记接口,用于类型约束
}
业务类型接口:
public interface BusinessType3<I, O>
extends Business<BusinessContext<I, O>> {
void doBusiness2(BusinessContext<I, O> businessContext);
}
具体业务实现:
@Service
@UseTemplate(TemplateImpl3.class) // 👈 关键注解
public class Business3 implements
BusinessType3<InputDto3, OutputDto3> {
@Override
public void doBusiness2(BusinessContext<InputDto3, OutputDto3> ctx) {
OutputDto3 output = new OutputDto3();
output.setField2("result from Business3");
output.setField3("xxx");
ctx.setOutput(output);
}
}
2.4 @UseTemplate 注解
定义:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseTemplate {
Class<?> value(); // 指定使用的 Template 实现类
}
使用场景: 在 Business 实现类上标注,指定该业务使用的处理模板。
3. 数据流转设计
3.1 BusinessContext 上下文
@Data
public class BusinessContext<I, O> {
private I input; // 输入参数
private O output; // 输出结果
private OtherDto3 otherDto; // 其他上下文信息
}
3.2 执行流程
客户端调用
↓
RegisterEnginV3.run(businessType, params)
↓
从注册表获取 Consumer<T>
↓
Template.handler(txcode, param, businessService)
↓
BusinessService.doBusiness(param)
↓
返回结果(通过 BusinessContext)
4. 类型安全机制
4.1 泛型类型检查
private static <T, S extends Business<T>> void
checkConsistentGenericType(S businessService,
Template<T, S> templateToUse) {
Class<?> templateGeneric = getGeneric(templateToUse, Template.class);
Class<?> businessGeneric = getGeneric(businessService, Business.class);
if (businessGeneric != null && templateGeneric != null &&
!templateGeneric.isAssignableFrom(businessGeneric)) {
throw new IllegalStateException("❌ Template type mismatch");
}
}
4.2 类型不匹配示例
// ❌ 错误示例
@UseTemplate(TemplateImpl1.class) // 期望 BusinessContext<A, B>
public class Business3 implements
BusinessType3<InputDto3, OutputDto3> { // 实际 BusinessContext<InputDto3, OutputDto3>
// 启动时会抛出 IllegalStateException
}
5. Bean 命名策略
5.1 UniquePackageBeanNameGenerator
问题背景: 当多个包下存在同名 Business 类时,Spring 默认命名策略会导致 Bean 名称冲突。
解决方案:
public class UniquePackageBeanNameGenerator
extends AnnotationBeanNameGenerator {
@Override
public String generateBeanName(BeanDefinition definition,
BeanDefinitionRegistry registry) {
String beanClassName = definition.getBeanClassName();
String originalBeanName = super.generateBeanName(definition, registry);
if (beanClassName != null) {
try {
Class<?> clazz = Class.forName(beanClassName);
if (Business.class.isAssignableFrom(clazz)) {
return "NEW" + originalBeanName; // 添加前缀
}
} catch (ClassNotFoundException e) {
return originalBeanName;
}
}
return originalBeanName;
}
}
5.2 配置方式
@SpringBootApplication
@ComponentScan(nameGenerator = UniquePackageBeanNameGenerator.class)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
6. 使用示例
6.1 定义 DTO
@Data
public class InputDto3 {
private String field1;
}
@Data
public class OutputDto3 {
private String field2;
private String field3;
}
6.2 实现业务逻辑
@Service
@UseTemplate(TemplateImpl3.class)
public class Business3 implements
BusinessType3<InputDto3, OutputDto3> {
@Override
public void doBusiness2(BusinessContext<InputDto3, OutputDto3> ctx) {
InputDto3 input = ctx.getInput();
OutputDto3 output = new OutputDto3();
output.setField2("处理结果: " + input.getField1());
output.setField3("额外信息");
ctx.setOutput(output);
}
}
6.3 调用业务
@RestController
@AllArgsConstructor
public class BusinessController {
private final RegisterEnginV3<BusinessContext<?, ?>, ?> registerEngine;
@PostMapping("/execute/{businessType}")
public BusinessContext<?, ?> execute(
@PathVariable String businessType,
@RequestBody InputDto3 input) {
BusinessContext<InputDto3, OutputDto3> context =
new BusinessContext<>();
context.setInput(input);
registerEngine.run(businessType, context);
return context;
}
}
7. 优势与扩展性
7.1 设计优势
| 特性 | 说明 |
|---|---|
| 低耦合 | 业务逻辑与处理流程完全分离 |
| 高内聚 | 相同类型业务共享同一模板 |
| 类型安全 | 编译期 + 运行期双重类型检查 |
| 易扩展 | 新增业务只需实现接口并添加注解 |
| 可维护 | 统一的注册和执行机制 |
8. 异常处理
- 定义系统内部异常和自定义通信异常
Controller或者Service统一处理自定义异常和未知异常- 如果有不需要响应的场景,自定义一个不响应的异常
- 如果有多个远程调用,按多种远程调用的自定义异常来处理
9. 总结
本设计通过注解驱动的方式实现了业务逻辑的灵活注册和执行,主要特点:
✅ 声明式配置:通过 @UseTemplate 注解绑定关系
✅ 类型安全:泛型 + 反射实现编译期和运行期双重检查
✅ 高度解耦:业务逻辑与执行流程完全分离
✅ 易于扩展:新增业务无需修改注册引擎代码
适用场景:
- 多业务类型的统一处理框架
- 需要动态扩展业务的系统
- 业务流程标准化的场景
协议
本作品代码部分采用Apache 2.0协议 进行许可。遵循许可的前提下,你可以自由地对代码进行修改,再发布,可以将代码用作商业用途。但要求你:
- 署名:在原有代码和衍生代码中,保留原作者署名及代码来源信息。
- 保留许可证:在原有代码和衍生代码中,保留Apache 2.0协议文件。
- 署名:应在使用本文档的全部或部分内容时候,注明原作者及来源信息。
- 非商业性使用:不得用于商业出版或其他任何带有商业性质的行为。如需商业使用,请联系作者。
- 相同方式共享的条件:在本文档基础上演绎、修改的作品,应当继续以知识共享署名 4.0国际许可协议进行许可。