复合校验技术解读
2025/12/3大约 6 分钟
复合校验技术解读
1. 核心概念
复合校验(Composite Validation) 是一种结合了 组合模式(Composite Pattern) 和 责任链模式(Chain of Responsibility Pattern) 思想的设计方式。
它通过 树形结构 组织多个校验组件,并以 层次化 + 顺序化 的方式执行,使得复杂的业务校验逻辑得以高内聚、低耦合地管理和扩展。
📌 简单来说:
- 组合模式 解决的是"如何组织校验组件"。
- 责任链模式 解决的是“如何顺序执行这些校验逻辑”。
- 淘票票项目的复合校验,本质是 两者结合的改进型设计。
2. 核心类及关系
2.1 抽象基类
// AbstractComposite.java - 复合校验的核心抽象类
public abstract class AbstractComposite<T> {
protected List<AbstractComposite<T>> list = new ArrayList<>(); // 子组件列表
protected abstract void execute(T param); // 执行具体业务逻辑
public abstract String type(); // 组件类型(如 user_register_check)
public abstract Integer executeParentOrder(); // 父级执行顺序
public abstract Integer executeTier(); // 执行层级
public abstract Integer executeOrder(); // 同层级执行顺序
}👉 作用:定义了组件的基本行为和树形结构的必要信息。
2.2 容器管理类
// CompositeContainer.java - 复合校验容器
public class CompositeContainer<T> {
private final Map<String, AbstractComposite> allCompositeInterfaceMap = new HashMap<>();
// 构建组件树结构
private static AbstractComposite build(Collection<AbstractComposite> components) {
// 按层级和顺序构建树形结构
}
// 执行指定类型的复合校验
public void execute(String type, T param) {
AbstractComposite compositeInterface = Optional.ofNullable(allCompositeInterfaceMap.get(type))
.orElseThrow(() -> new TaoPiaoPiaoFrameException(BaseCode.COMPOSITE_NOT_EXIST));
compositeInterface.allExecute(param);
}
}👉 作用:
- 容器:统一管理所有校验组件
- 执行器:负责按照树形结构调度组件执行
3. 具体实现类
3.1 抽象用户注册校验器
public abstract class AbstractUserRegisterCheckHandler extends AbstractComposite<UserRegisterDto> {
@Override
public String type() {
return CompositeCheckType.USER_REGISTER_CHECK.getValue(); // "user_register_check"
}
}👉 作用:为用户注册场景下的校验提供统一入口。
3.2 具体校验组件
1. 验证码校验组件
@Component
public class UserRegisterVerifyCaptcha extends AbstractUserRegisterCheckHandler {
@Override
protected void execute(UserRegisterDto param) {
// 1. 密码一致性校验
if (!param.getPassword().equals(param.getConfirmPassword())) {
throw new TaoPiaoPiaoFrameException(BaseCode.TWO_PASSWORDS_DIFFERENT);
}
// 2. 验证码状态检查
String verifyCaptcha = redisCache.get(
RedisKeyBuild.createRedisKey(RedisKeyManage.VERIFY_CAPTCHA_ID, param.getCaptchaId()),
String.class
);
// 3. 验证码有效性校验
if (VerifyCaptcha.YES.getValue().equals(verifyCaptcha)) {
CaptchaVO captchaVO = new CaptchaVO();
captchaVO.setCaptchaVerification(param.getCaptchaVerification());
ResponseModel responseModel = captchaHandle.verification(captchaVO);
if (!responseModel.isSuccess()) {
throw new TaoPiaoPiaoFrameException(responseModel.getRepCode(), responseModel.getRepMsg());
}
}
}
@Override public Integer executeParentOrder() { return 0; } // 根节点
@Override public Integer executeTier() { return 1; } // 第一层级
@Override public Integer executeOrder() { return 1; } // 顺序
}2. 用户存在性校验组件
@Component
public class UserRegisterExist extends AbstractUserRegisterCheckHandler {
@Override
protected void execute(UserRegisterDto param) {
// 查询手机号是否存在
LambdaQueryWrapper<UserMobile> queryWrapper = Wrappers.lambdaQuery(UserMobile.class)
.eq(UserMobile::getMobile, param.getMobile());
UserMobile userMobile = userMobileMapper.selectOne(queryWrapper);
if (Objects.nonNull(userMobile)) {
throw new TaoPiaoPiaoFrameException(BaseCode.USER_EXIST);
}
}
@Override public Integer executeParentOrder() { return 1; } // 依赖验证码校验
@Override public Integer executeTier() { return 2; } // 第二层级
@Override public Integer executeOrder() { return 1; }
}4. 组件关系和执行流程
4.1 树形结构关系
UserRegisterCheckHandler (根节点, Tier=1)
├── UserRegisterVerifyCaptcha (Tier=2, Order=1)
└── UserRegisterExist (Tier=2, Order=2)
└── UserRegisterFrequency (Tier=3, Order=1)4.2 执行流程
// 层次遍历执行所有组件
public void allExecute(T param) {
Queue<AbstractComposite<T>> queue = new LinkedList<>();
queue.add(this); // 添加根节点
while (!queue.isEmpty()) {
int levelSize = queue.size();
for (int i = 0; i < levelSize; i++) {
AbstractComposite<T> current = queue.poll();
current.execute(param); // 执行当前逻辑
queue.addAll(current.list); // 添加子节点
}
}
}5. 使用方式
5.1 服务层调用
@Service
public class UserService {
@Autowired
private CompositeContainer compositeContainer;
@Transactional(rollbackFor = Exception.class)
public Boolean register(UserRegisterDto userRegisterDto) {
// 执行复合校验
compositeContainer.execute(CompositeCheckType.USER_REGISTER_CHECK.getValue(), userRegisterDto);
// 校验通过后保存用户
User user = new User();
BeanUtils.copyProperties(userRegisterDto, user);
user.setId(uidGenerator.getUid());
userMapper.insert(user);
return true;
}
}5.2 初始化容器
public class CompositeInit extends AbstractApplicationStartEventListenerHandler {
private final CompositeContainer compositeContainer;
@Override
public void executeInit(ConfigurableApplicationContext context) {
compositeContainer.init(context); // 启动时构建组件树
}
}6. 相关概念
组合模式(Composite Pattern)
- 定义:将对象组合成树形结构以表示“部分-整体”层次。
- 应用:不同校验组件被组合成一棵校验树。
责任链模式(Chain of Responsibility Pattern)
- 定义:使多个对象都有机会处理请求,从而避免请求与处理者的耦合。
- 应用:在树结构中,不同层级的校验器按顺序依次执行。
分层校验
- 概念:将复杂逻辑拆解为多层,每层只做本层校验。
- 优势:逻辑清晰,可维护性强。
动态构建
- 概念:运行时根据
executeTier、executeOrder、executeParentOrder信息动态组装树。 - 优势:无需硬编码依赖,新增校验只需实现类。
- 概念:运行时根据
7. 设计优势
- 高内聚低耦合:各个校验组件职责单一,彼此独立。
- 易于扩展:新增校验仅需新增一个组件类即可。
- 灵活配置:通过层级与顺序控制执行逻辑。
- 层次清晰:树形结构使复杂逻辑分层明确。
- 可复用性:同一校验组件可用于不同业务场景。
相关疑问
为什么CompositeContainer能获取AbstractComposite类型的bean实例
这是因为Spring框架的类型匹配机制和依赖注入原理。让我详细解释:
1. Spring的Bean类型匹配机制
// Spring框架核心方法:getBeansOfType()
Map<String, AbstractComposite> compositeInterfaceMap =
applicationEvent.getBeansOfType(AbstractComposite.class);这个方法能够获取到所有类型为AbstractComposite或其子类的Bean实例,原因如下:
继承关系链:
AbstractComposite<T> (抽象类)
↑
AbstractUserRegisterCheckHandler (用户注册校验抽象类)
↑
UserRegisterVerifyCaptcha (具体校验组件)
UserRegisterExist (具体校验组件)
UserRegisterFrequency (具体校验组件)
↑
(以及其他所有继承自AbstractComposite的组件)2. Spring Bean注册机制
组件自动注册:
// UserRegisterVerifyCaptcha.java
@Component // 这个注解使类成为Spring Bean
public class UserRegisterVerifyCaptcha extends AbstractUserRegisterCheckHandler {
// ...
}
// UserRegisterExist.java
@Component // 自动注册为Spring Bean
public class UserRegisterExist extends AbstractUserRegisterCheckHandler {
// ...
}当Spring容器启动时:
- 扫描所有带有[@Component]注解的类
- 创建这些类的实例并注册到IoC容器中
- 记录每个Bean的实际类型和声明类型
3. 多态性原理
Java多态性:
// 虽然实际创建的是具体类实例,但它们都是AbstractComposite的子类
UserRegisterVerifyCaptcha captchaChecker = new UserRegisterVerifyCaptcha();
AbstractComposite composite = captchaChecker; // 多态赋值
// Spring容器中存储的是实际类型,但可以通过父类型引用获取4. getBeansOfType()工作原理
// Spring内部实现逻辑(简化版)
public <T> Map<String, T> getBeansOfType(Class<T> type) {
Map<String, T> result = new HashMap<>();
// 遍历所有已注册的Bean
for (String beanName : beanDefinitionNames) {
Class<?> beanClass = getBeanClass(beanName);
// 检查是否是指定类型的子类或实现类
if (type.isAssignableFrom(beanClass)) {
// 如果是,则添加到结果中
result.put(beanName, (T) getBean(beanName));
}
}
return result;
}5. 实际执行过程
步骤1:Spring容器启动
// Spring扫描并创建以下Bean实例:
// - UserRegisterVerifyCaptcha (实际类型)
// - UserRegisterExist (实际类型)
// - UserRegisterFrequency (实际类型)
// 这些都继承自 AbstractComposite步骤2:类型匹配
// 当调用 getBeansOfType(AbstractComposite.class) 时:
// Spring检查每个Bean是否是AbstractComposite的子类
// UserRegisterVerifyCaptcha instanceof AbstractComposite ? true
// UserRegisterExist instanceof AbstractComposite ? true
// UserRegisterFrequency instanceof AbstractComposite ? true步骤3:返回匹配结果
// 最终返回包含所有校验组件的Map:
Map<String, AbstractComposite> result = {
"userRegisterVerifyCaptcha": UserRegisterVerifyCaptcha实例,
"userRegisterExist": UserRegisterExist实例,
"userRegisterFrequency": UserRegisterFrequency实例
}6. 代码示例验证
``java
// 验证多态性
@Test
public void testPolymorphism() {
// 实际创建具体类实例
UserRegisterVerifyCaptcha concrete = new UserRegisterVerifyCaptcha();
// 通过父类引用
AbstractComposite abstractRef = concrete;
// 类型检查
assertTrue(abstractRef instanceof AbstractComposite); // true
assertTrue(abstractRef instanceof AbstractUserRegisterCheckHandler); // true
assertTrue(abstractRef instanceof UserRegisterVerifyCaptcha); // true
}
### 总结
**CompositeContainer能获取AbstractComposite类型Bean的根本原因**:
1. **继承关系**:所有具体的校验组件都继承自[AbstractComposite]
2. **Spring机制**:`getBeansOfType()`利用Java反射和类型检查机制
3. **多态支持**:Java的多态性允许父类引用指向子类实例
4. **自动注册**:[@Component]注解使校验组件自动成为Spring Bean
这种设计充分利用了面向对象的继承和多态特性,以及Spring框架的依赖注入机制,实现了灵活的组件管理和组合模式.