Minimalist Spring Boot 3: Annotation‑Based Efficient Sensitive‑Word Handling

This guide demonstrates how to use a custom @SensitiveFilter annotation together with an AOP aspect and the lightweight sensitive‑word library to implement global sensitive‑word filtering in Spring Boot 3, supporting both automatic replacement and detection modes while eliminating repetitive code across controllers.

Java Tech Enthusiast
Java Tech Enthusiast
Java Tech Enthusiast
Minimalist Spring Boot 3: Annotation‑Based Efficient Sensitive‑Word Handling

Overview

In Java projects, sensitive‑word filtering is often needed across many endpoints (comments, private messages, content publishing). Manually invoking a utility class in each controller causes code duplication and maintenance difficulty. A custom annotation combined with Spring AOP provides a non‑intrusive, globally applicable solution using the sensitive-word library.

Dependency

<!-- Sensitive‑word filtering library (latest 0.29.5, includes default dictionary) -->
<dependency>
    <groupId>com.github.houbb</groupId>
    <artifactId>sensitive-word</artifactId>
    <version>0.29.5</version>
</dependency>

Custom Annotation

The annotation marks methods that require sensitive‑word processing. It allows configuration of the handling type (filter or check), the fields to process, and a custom message for detection mode.

import java.lang.annotation.*;
/**
 * Global sensitive‑word handling annotation.
 * Place on controller methods to automatically process request parameters.
 */
@Target({ElementType.METHOD}) // only methods
@Retention(RetentionPolicy.RUNTIME) // runtime visibility
@Documented
public @interface SensitiveFilter {
    /** filter (replace with '*') or check (detect) */
    String handleType() default "filter";
    /** fields to process when the argument is an object */
    String[] fields() default {};
    /** message shown when detection fails */
    String message() default "内容包含敏感词,请勿提交";
}

Utility Wrapper

A thin wrapper around SensitiveWordHelper provides three static‑like methods: filter, check, and getSensitiveWords. The default dictionary is used, so no extra configuration is required.

import com.github.houbb.sensitive.word.util.SensitiveWordHelper;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
/**
 * Utility class exposing SensitiveWordHelper methods.
 */
@Component
public class SensitiveWordUtil {
    public String filter(String text) {
        if (text == null || text.trim().isEmpty()) return text;
        return SensitiveWordHelper.replace(text, '*');
    }
    public boolean check(String text) {
        if (text == null || text.trim().isEmpty()) return false;
        return SensitiveWordHelper.contains(text);
    }
    public List<String> getSensitiveWords(String text) {
        if (text == null || text.trim().isEmpty()) return new ArrayList<>();
        return SensitiveWordHelper.findAll(text);
    }
}

AOP Aspect

The aspect intercepts any method annotated with @SensitiveFilter. It extracts the annotation attributes, iterates over method arguments, and applies either filtering or detection based on handleType. For object arguments, it reflects the specified fields and processes them individually.

import cn.iocoder.boot.utils.SensitiveWordUtil;
import jakarta.annotation.Resource;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/** Global sensitive‑word handling aspect */
@Aspect
@Component
public class SensitiveFilterAspect {
    @Resource
    private SensitiveWordUtil sensitiveWordUtil;

    @Around("@annotation(com.example.sensitive.config.SensitiveFilter)") // replace with actual package
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        SensitiveFilter filterAnno = method.getAnnotation(SensitiveFilter.class);
        Object[] args = joinPoint.getArgs();
        if (args == null || args.length == 0) return joinPoint.proceed();
        handleSensitive(args, filterAnno);
        return joinPoint.proceed(args);
    }

    private void handleSensitive(Object[] args, SensitiveFilter anno) {
        String handleType = anno.handleType();
        String[] fields = anno.fields();
        String message = anno.message();
        for (Object arg : args) {
            if (arg == null) continue;
            if (arg instanceof String) {
                String text = (String) arg;
                if ("filter".equals(handleType)) {
                    String filtered = sensitiveWordUtil.filter(text);
                    int idx = java.util.Arrays.asList(args).indexOf(arg);
                    args[idx] = filtered;
                } else if ("check".equals(handleType) && sensitiveWordUtil.check(text)) {
                    throw new RuntimeException(message);
                }
            } else {
                handleEntityField(arg, fields, handleType, message);
            }
        }
    }

    private void handleEntityField(Object entity, String[] fields, String handleType, String message) {
        for (String fieldName : fields) {
            if (fieldName == null || fieldName.trim().isEmpty()) continue;
            Field field = ReflectionUtils.findField(entity.getClass(), fieldName);
            if (field == null) continue;
            ReflectionUtils.makeAccessible(field);
            try {
                Object value = field.get(entity);
                if (!(value instanceof String)) continue;
                String text = (String) value;
                if ("filter".equals(handleType)) {
                    String filtered = sensitiveWordUtil.filter(text);
                    field.set(entity, filtered);
                } else if ("check".equals(handleType) && sensitiveWordUtil.check(text)) {
                    throw new RuntimeException(message);
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
}

Global Exception Handler

When handleType="check" detects a prohibited word, the aspect throws a RuntimeException. The following @RestControllerAdvice catches such exceptions and returns a unified error response.

import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/** Catch sensitive‑word detection exceptions */
@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(RuntimeException.class)
    public ResultVO handleSensitiveException(RuntimeException e) {
        if (e.getMessage().contains("敏感词")) {
            return ResultVO.fail(e.getMessage());
        }
        return ResultVO.fail("系统异常,请稍后重试");
    }
}

Usage Scenarios

1. Single String Parameter

For a simple comment submission endpoint, the annotation automatically filters the content string.

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CommentController {
    @PostMapping("/comment/submit")
    @SensitiveFilter // defaults to filter mode
    public ResultVO submitComment(@RequestParam String content) {
        System.out.println("过滤后评论内容:" + content);
        return ResultVO.success("评论提交成功");
    }

    @PostMapping("/comment/check")
    @SensitiveFilter(handleType = "check", message = "评论包含敏感词,审核不通过")
    public ResultVO checkComment(@RequestParam String content) {
        // If execution reaches here, no sensitive words were found.
        return ResultVO.success("审核通过");
    }
}

2. DTO Parameter

When the request body is an object (e.g., ArticleDTO), specify the fields that need processing.

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ArticleController {
    @PostMapping("/article/publish")
    @SensitiveFilter(fields = {"title", "content"})
    public ResultVO publishArticle(@RequestBody ArticleDTO articleDTO) {
        System.out.println("过滤后标题:" + articleDTO.getTitle());
        System.out.println("过滤后内容:" + articleDTO.getContent());
        return ResultVO.success("文章发布成功");
    }

    @lombok.Data
    public static class ArticleDTO {
        private Long userId;
        private String title;   // needs filtering
        private String content; // needs filtering
        private String category; // no filtering
    }
}

Conclusion

By leveraging the sensitive-word library and Spring AOP, the solution provides a concise, annotation‑driven approach for global sensitive‑word handling in Spring Boot 3. It eliminates repetitive utility calls, supports both replacement and detection modes, and can be extended with custom dictionaries if needed.

Open‑source repository: https://github.com/houbb/sensitive-word

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaaopSpring BootannotationSensitive Word Filtering
Java Tech Enthusiast
Written by

Java Tech Enthusiast

Sharing computer programming language knowledge, focusing on Java fundamentals, data structures, related tools, Spring Cloud, IntelliJ IDEA... Book giveaways, red‑packet rewards and other perks await!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.