应单例懒加载敏感词库并同级并行脱敏:词库用AtomicBoolean+双重检查锁延迟初始化为Spring Bean;DTO转换中仅调用已加载的单例filter,同级评论用CompletableFuture.supplyAsync并行过滤,父子级保持同步递归。

词库应单例+懒加载,而非每次转换都 new
敏感词库(比如基于 Trie 构建的 DFA)初始化成本高,但一旦构建完成就是只读的。不要在每个 DTO 转换里重复构建,更不要让 Callable 去反复加载同一份词库。
- 把词库封装成 Spring Bean(@Scope("singleton")),构造时不做实际加载,改用
AtomicBoolean initialized+ 双重检查锁实现懒加载 - 加载逻辑(如从 DB/Redis/本地文件读取词表、构建成 DFA 树)放在
initIfNeeded()中,由首次调用方触发 - DTO 转换代码里只调用
sensitiveFilter.contains(text),不关心加载时机
DTO 转换本身用 CompletableFuture 编排,但别让 Callable 加载词库
多级评论需递归转换(如 CommentDTO → List<CommentDTO> 子列表),每层可能要对 content 做脱敏。这时可并行处理同级兄弟节点,但不是用 Callable 去加载词库,而是用它并行执行过滤逻辑。
- 用
CompletableFuture.supplyAsync(() -> filter(content), executor)对同级 N 条评论内容并行脱敏 - executor 建议用自定义线程池(非 commonPool),避免 IO 密集型任务挤占 ForkJoinPool
- filter 方法内部直接调用已初始化好的单例词库,无任何加载开销
- 父子级之间仍保持同步递归(树形结构天然有依赖),只在“同层平级”做并行
如果词库真需要热更新,用 ScheduledExecutorService 异步刷新
有些业务要求敏感词实时生效(如运营后台修改后 5 秒内生效)。此时不能靠每次转换时加载,而应由后台定时或监听事件异步重建词库实例,并原子替换引用。
- 维护一个
AtomicReference<SensitiveFilter>,指向当前可用词库 - 另起一个 ScheduledExecutorService,定期执行
loadNewFilter().thenUpdateRef() - DTO 转换时始终通过该引用获取最新实例,天然线程安全
- Callable 在这里只用于“加载新词库”这个后台任务,不参与请求链路
示例:同级评论并行脱敏的典型写法
假设你有一个评论列表 List<Comment> comments,要转成 List<CommentDTO>,其中 content 需过滤:
// 用固定线程池,避免 commonPool 被阻塞
private final ExecutorService filterPool = Executors.newFixedThreadPool(4);
List<CompletableFuture<CommentDTO>> futures = comments.stream()
.map(comment -> CompletableFuture.supplyAsync(() -> {
String filtered = sensitiveFilter.sanitize(comment.getContent()); // ← 单例词库,无加载逻辑
return new CommentDTO(comment.getId(), filtered,
convertChildren(comment.getChildren())); // 子树递归,同步
}, filterPool))
.toList();
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
.thenApply(v -> futures.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList()))
.join();

















