敏感词过滤这么玩?自定义注解 + DFA 算法,优雅又高效!
文章目录
- 一、为什么选 DFA 算法?
- 二、设计思路:注解 + AOP + DFA
- 目标效果示例:
- 三、代码实现
- 1. 自定义注解
- 2. DFA 敏感词工具类
- 3. AOP 拦截处理
- 4. 使用示例
- 四、整合与优化
- 五、总结
博主介绍:全网粉丝10w+、CSDN合伙人、华为云特邀云享专家,阿里云专家博主、星级博主,51cto明日之星,热爱技术和分享、专注于Java技术领域
🍅文末获取源码联系🍅
👇🏻 精彩专栏推荐订阅👇🏻 不然下次找不到哟
在做评论系统、论坛、社交应用时,敏感词过滤几乎是一个绕不开的话题。
“敏感词”并不一定指违规词,也可能是你系统内部的一些黑名单词,比如屏蔽某些广告词,替换掉特定品牌词等。
很多同学第一次接触这个需求时,通常会写一个最简单的实现:
String text = "这里是广告,点我拿红包!";
text = text.replace("广告", "***");
但这种方式的问题显而易见:
- 只能匹配固定词,无法支持大规模敏感词库;
- 替换效率低,尤其是词库很大时;
- 不灵活,难以和业务层解耦。
今天我们就来聊聊一种优雅高效的方案:自定义注解 + DFA 算法,让敏感词过滤既高效,又好维护,还能无侵入集成到 Spring 项目中。
一、为什么选 DFA 算法?
DFA(Deterministic Finite Automaton,确定有穷自动机)是一种高效的字符串匹配算法。相比于正则表达式和简单的 replace
:
- 高效:DFA 能在 O(n) 时间复杂度内完成文本扫描,适合大规模词库。
- 支持模糊匹配:不仅能精确匹配,还能检测出包含敏感词的长句。
- 可扩展:敏感词可以随时更新,不需要改代码。
常见实现是将敏感词构建成一棵前缀树(Trie 树),文本只需扫描一遍,就能快速判定是否包含敏感词。
二、设计思路:注解 + AOP + DFA
我们想要的效果是:
- 在需要过滤的字段/参数上,加一个注解
@SensitiveFilter
; - Spring AOP 自动拦截方法,在进入业务逻辑前替换掉敏感词;
- 业务层无需关心,过滤逻辑完全解耦。
目标效果示例:
@PostMapping("/comment")
public String addComment(@SensitiveFilter String content) {// content 已经是过滤后的文本return "保存成功:" + content;
}
如果用户输入 "这里是广告,点我拿红包!"
,
那么最终存入数据库的就是 "这里是**,点我拿红包!"
。
三、代码实现
我们分 4 步来实现:
1. 自定义注解
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface SensitiveFilter {// 替换字符,默认是 "*"String replaceChar() default "*";
}
2. DFA 敏感词工具类
这里用 Trie 树实现 DFA。
public class SensitiveWordUtil {private static final Map<Character, Map> sensitiveWordMap = new HashMap<>();// 初始化词库public static void init(List<String> words) {for (String word : words) {Map<Character, Map> nowMap = sensitiveWordMap;for (int i = 0; i < word.length(); i++) {char c = word.charAt(i);Map<Character, Map> nextMap = (Map<Character, Map>) nowMap.get(c);if (nextMap == null) {nextMap = new HashMap<>();nowMap.put(c, nextMap);}nowMap = nextMap;}// 词尾标记nowMap.put(' ', new HashMap<>());}}// 过滤文本public static String filter(String text, String replaceChar) {StringBuilder result = new StringBuilder();int i = 0;while (i < text.length()) {int matchLen = checkSensitive(text, i);if (matchLen > 0) {// 替换为指定字符for (int j = 0; j < matchLen; j++) {result.append(replaceChar);}i += matchLen;} else {result.append(text.charAt(i));i++;}}return result.toString();}// 检查从某个位置开始是否包含敏感词private static int checkSensitive(String text, int start) {Map<Character, Map> nowMap = sensitiveWordMap;int length = 0;for (int i = start; i < text.length(); i++) {char c = text.charAt(i);nowMap = (Map<Character, Map>) nowMap.get(c);if (nowMap == null) {return 0;}length++;if (nowMap.containsKey(' ')) {return length;}}return 0;}
}
初始化的时候,可以从数据库或配置文件里加载敏感词库,例如:
SensitiveWordUtil.init(Arrays.asList("广告", "红包", "涉黄", "违法"));
3. AOP 拦截处理
@Aspect
@Component
public class SensitiveFilterAspect {@Around("@annotation(org.springframework.web.bind.annotation.PostMapping) || " +"@annotation(org.springframework.web.bind.annotation.RequestMapping)")public Object doFilter(ProceedingJoinPoint joinPoint) throws Throwable {Object[] args = joinPoint.getArgs();Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();Parameter[] parameters = method.getParameters();for (int i = 0; i < parameters.length; i++) {if (parameters[i].isAnnotationPresent(SensitiveFilter.class) && args[i] instanceof String) {SensitiveFilter annotation = parameters[i].getAnnotation(SensitiveFilter.class);args[i] = SensitiveWordUtil.filter((String) args[i], annotation.replaceChar());}}return joinPoint.proceed(args);}
}
这样,当 Controller 层的接口收到字符串参数时,就会自动替换掉敏感词。
4. 使用示例
@RestController
public class CommentController {@PostMapping("/comment")public String addComment(@SensitiveFilter String content) {return "保存成功:" + content;}
}
输入:
POST /comment
body: 这里是广告,点我拿红包!
输出:
保存成功:这里是**,点我拿**
四、整合与优化
-
敏感词库动态加载
- 可以存到数据库,结合 Redis 做缓存。
- 后台增加“敏感词管理”功能,实时更新。
-
替换规则多样化
- 不仅仅是
"***"
,也可以替换成[违规词]
。 - 注解里增加参数:
mode = REPLACE / BLOCK
,选择直接拒绝还是替换。
- 不仅仅是
-
多层级拦截
- 不仅仅是
@Controller
参数,还能应用到 DTO 的字段上。 - 使用反射递归处理对象中的
@SensitiveFilter
字段。
- 不仅仅是
五、总结
本文我们实现了一个自定义注解 + DFA 算法的敏感词过滤方案:
- 用 DFA 提升匹配效率,支持大规模词库;
- 用注解 + AOP 解耦业务逻辑,使用简单;
- 方案灵活,可动态扩展敏感词库和替换策略。
这套方案不仅适合小项目,也能支撑中大型系统,尤其是评论区、弹幕、聊天消息等场景,能大大减少违规风险。
未来你还可以继续扩展:
- 敏感词分类(政治、广告、低俗),按业务场景应用;
- 多语言敏感词库(中文 + 英文混合);
- 结合机器学习做“上下文敏感”过滤。
一句话总结:优雅又高效的敏感词过滤,不是写个 replace
,而是让框架帮你做。 🚀
大家点赞、收藏、关注、评论啦 、查看👇🏻获取联系方式👇🏻