下界通配符(? super T)仅支持安全写入子类型,实现多类型补偿任务统一归仓;真正的“自愈”依赖任务建模、状态持久化、线程隔离与策略编排四大支柱。

下界通配符(? super T)本身不直接构建队列,也不具备“自愈”能力;它只是泛型类型安全的表达工具。真正实现多类型异常补偿逻辑的安全归仓,靠的是任务建模、状态持久化、线程隔离与策略编排——下界通配符仅在其中承担一个关键但低调的角色:让不同子类型的补偿任务能被统一收集、调度和归档到同一容器中。
为什么需要 ? super Task
当系统需支持多种补偿任务(如 HttpCompensateTask、DbRollbackTask、RabbitMQUndoTask),它们都继承自抽象基类 CompensateTask 时:
- 用
List只能存基类或其子类实例,但无法接收更宽泛的泛型写入(例如你不能把DbRollbackTask安全地 add 进List) - 而
List super CompensateTask>表示“可写入CompensateTask及其任意父类型”,实际使用中通常指向List或List,从而允许所有子类任务无类型擦除风险地加入同一集合 - 这是实现“统一归仓”的静态类型前提:避免运行时
ClassCastException,也避免为每种任务单独维护队列
异步重试队列的四个核心支柱
所谓“自愈型”,不是靠泛型语法自动修复,而是通过以下机制闭环:
- 任务快照持久化:每次发起异步通知或远程调用前,将任务参数、上下文、重试次数、预期超时时间序列化存入数据库或 Redis(带 TTL)。即使服务重启,也能从存储中捞出待重试项
-
线程池物理隔离:补偿任务调度器(如基于
ScheduledExecutorService或 Quartz 的定时扫描器)必须与业务主流程线程池完全分离。严禁用ForkJoinPool.commonPool()承载补偿任务,否则 I/O 阻塞会拖垮整个异步链路 -
幂等+状态机驱动:每个任务记录当前状态(
PENDING → RETRYING → SUCCESS → FAILED),执行前先查 DB 确认是否已成功。HTTP 调用加唯一请求 ID,DB 回滚加版本号或事务日志表防重复 -
异常分类路由:不是所有异常都重试。网络超时、503、连接拒绝等可重试;400 参数错误、401 认证失败、唯一键冲突等应立即标记
FAILED并触发人工告警。可用策略模式 + 异常类型匹配实现分发
实战归仓代码片段(精简版)
以下展示如何用 ? super CompensateTask 支撑统一归档,并衔接重试调度:
// 1. 统一归仓入口 —— 接收任意子类任务
public class CompensateTaskWarehouse {
private final List super CompensateTask> warehouse = new CopyOnWriteArrayList();
public
warehouse.add(task); // 编译期安全:T 是 CompensateTask 子类,? super CompensateTask 允许写入
persistToDb(task); // 同时落库,保障宕机不丢
}
}
// 2. 调度器只读取基类,屏蔽具体实现
public class RetryScheduler {
public void triggerPendingTasks() {
List
pending.forEach(task -> {
CompletableFuture.supplyAsync(() -> task.execute(), compensateExecutor)
.handle((result, ex) -> handleResult(task, result, ex));
});
}
}
```
安全归仓的关键细节
- 归仓操作本身必须是原子的:先写 DB 成功,再加入内存集合;若 DB 失败,禁止入内存,避免内存有而库无,导致重启后丢失
- 内存集合(如
CopyOnWriteArrayList)仅作临时缓存或监控统计,不可作为唯一任务源。所有调度必须以持久化存储为准 - 对
? super T的使用要克制:它只解决“写入兼容”,不解决“读取多态”。读取时仍需按基类方法设计,必要时用instanceof+ 模板方法分发逻辑 - 归仓后需配套清理机制:成功任务定期归档至历史表;失败超限任务转入死信队列并推送告警,防止无限堆积










