最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot實(shí)現(xiàn)企業(yè)級敏感詞攔截檢查系統(tǒng)的設(shè)計(jì)方案

 更新時(shí)間:2026年01月15日 09:59:54   作者:一路向北?  
本文介紹了基于SpringBoot和DFA算法的敏感詞檢測系統(tǒng)設(shè)計(jì),涵蓋業(yè)務(wù)背景、技術(shù)架構(gòu)、核心模塊、數(shù)據(jù)庫設(shè)計(jì)、使用示例、監(jiān)控與告警、安全設(shè)計(jì)、部署方案等多個(gè)方面,旨在實(shí)現(xiàn)高效的實(shí)時(shí)敏感詞檢測,保證內(nèi)容合規(guī)性,提升平臺穩(wěn)定性,需要的朋友可以參考下

一、設(shè)計(jì)背景與目標(biāo)

1.1 業(yè)務(wù)背景

在視頻評論、劇情評論、私信等用戶生成內(nèi)容(UGC)場景中,需要對用戶輸入進(jìn)行實(shí)時(shí)敏感詞檢測,保證內(nèi)容合規(guī)性,防止違規(guī)內(nèi)容傳播,降低平臺風(fēng)險(xiǎn)。

應(yīng)用場景

  • 評論系統(tǒng):視頻評論、劇情評論、回復(fù)內(nèi)容過濾
  • 私信系統(tǒng):用戶間私信內(nèi)容實(shí)時(shí)審核
  • 內(nèi)容發(fā)布:帖子、動態(tài)、資料等UGC內(nèi)容檢測
  • 用戶資料:昵稱、個(gè)性簽名等字段過濾

1.2 總體目標(biāo)

  • 高可用性:系統(tǒng)穩(wěn)定性 ≥ 99.9%,避免因敏感詞檢測導(dǎo)致業(yè)務(wù)不可用
  • 高性能:支持 15,000+ QPS,單次檢測耗時(shí) < 5ms,不影響業(yè)務(wù)響應(yīng)時(shí)間
  • 低侵入性:通過注解方式集成,業(yè)務(wù)代碼改動最小化
  • 靈活配置:支持多種處理策略(攔截、替換、標(biāo)記審核)、熱更新敏感詞庫
  • 精準(zhǔn)匹配:支持精確匹配、模糊匹配(變體、拆分、諧音)、白名單機(jī)制
  • 可觀測性:完善的日志審計(jì)、監(jiān)控告警、統(tǒng)計(jì)分析能力

1.3 技術(shù)架構(gòu)約束

  • 運(yùn)行時(shí)框架:Spring Boot 3.5.6 + Java 17
  • 持久層:MyBatis Plus 3.5.9 + MySQL 8.0
  • 緩存層:Redis(分布式緩存) + Caffeine(本地緩存)
  • 分布式鎖:Redisson 3.37.0
  • 消息隊(duì)列:RabbitMQ(異步審核通知)
  • 鑒權(quán)認(rèn)證:JWT + Spring Security

二、核心設(shè)計(jì)理念

2.1 設(shè)計(jì)原則

  1. 性能優(yōu)先:采用高效的 DFA(Deterministic Finite Automaton)算法 + Trie 樹數(shù)據(jù)結(jié)構(gòu)
  2. 降級保護(hù):檢測失敗時(shí)不阻塞業(yè)務(wù),允許配置降級策略
  3. 緩存分層:本地緩存(Caffeine)+ 分布式緩存(Redis)+ 數(shù)據(jù)庫(MySQL)三級存儲
  4. 異步處理:敏感內(nèi)容標(biāo)記審核場景使用 MQ 異步通知
  5. 可插拔策略:支持多種處理策略,通過配置動態(tài)切換

2.2 系統(tǒng)邊界

包含功能

  • 敏感詞檢測(精確匹配、模糊匹配)
  • 多種處理策略(攔截、替換、標(biāo)記審核)
  • 敏感詞庫管理(增刪改查、熱更新)
  • 白名單機(jī)制
  • 日志審計(jì)與監(jiān)控

不包含功能

  • 人工審核流程(可通過 MQ 對接)
  • 圖片/視頻內(nèi)容識別(需對接 AI 服務(wù))
  • 自然語言理解(語義分析)

三、整體技術(shù)架構(gòu)

3.1 組件劃分

com.video.sensitive/
├── annotation/                 # 注解層
│   └── SensitiveCheck.java    # 敏感詞檢測注解
├── aspect/                     # AOP切面層
│   └── SensitiveCheckAspect.java
├── core/                       # 核心引擎
│   ├── SensitiveWordFilter.java      # 敏感詞過濾器接口
│   ├── DfaSensitiveWordFilter.java   # DFA算法實(shí)現(xiàn)
│   ├── SensitiveWordTrie.java        # Trie樹結(jié)構(gòu)
│   └── SensitiveWordMatcher.java     # 匹配器
├── strategy/                   # 處理策略
│   ├── SensitiveStrategy.java        # 策略接口
│   ├── RejectStrategy.java           # 拒絕策略
│   ├── ReplaceStrategy.java          # 替換策略
│   └── ReviewStrategy.java           # 人工審核標(biāo)記策略
├── cache/                      # 緩存管理
│   ├── SensitiveWordCacheManager.java
│   └── WordCacheWarmer.java          # 緩存預(yù)熱
├── repository/                 # 敏感詞庫管理
│   ├── SensitiveWordRepository.java
│   └── WhitelistRepository.java
├── entity/                     # 實(shí)體類
│   ├── SensitiveWord.java           # 敏感詞實(shí)體
│   ├── SensitiveLog.java            # 審計(jì)日志實(shí)體
│   └── Whitelist.java               # 白名單實(shí)體
├── mapper/                     # MyBatis Mapper
│   ├── SensitiveWordMapper.java
│   ├── SensitiveLogMapper.java
│   └── WhitelistMapper.java
├── service/                    # 業(yè)務(wù)服務(wù)
│   ├── SensitiveWordService.java    # 敏感詞管理服務(wù)
│   └── SensitiveCheckService.java   # 檢測服務(wù)
├── controller/                 # 管理接口
│   └── SensitiveWordController.java
├── config/                     # 配置類
│   ├── SensitiveConfig.java
│   └── SensitiveProperties.java
├── enums/                      # 枚舉類
│   ├── SensitiveStrategy.java       # 處理策略枚舉
│   ├── SensitiveLevel.java          # 敏感等級枚舉
│   └── MatchType.java               # 匹配類型枚舉
├── exception/                  # 異常類
│   ├── SensitiveWordException.java
│   └── SensitiveContentException.java
└── monitor/                    # 監(jiān)控與日志
    ├── SensitiveMonitor.java        # 監(jiān)控指標(biāo)
    └── SensitiveAuditLogger.java    # 審計(jì)日志

3.2 核心流程時(shí)序圖

用戶 -> Controller -> @SensitiveCheck -> SensitiveCheckAspect
                                             ↓
                         [1] 獲取待檢測文本 + 注解配置
                                             ↓
                         [2] SensitiveWordCacheManager.getWordTrie()
                                             ↓ (緩存未命中)
                         [3] Redis 獲取敏感詞列表
                                             ↓ (Redis未命中)
                         [4] MySQL 查詢敏感詞表
                                             ↓
                         [5] 構(gòu)建 Trie 樹并緩存到 Caffeine + Redis
                                             ↓
                         [6] DfaSensitiveWordFilter.filter(text, wordTrie)
                                             ↓
                         [7] 返回檢測結(jié)果(匹配詞列表 + 位置)
                                             ↓
                     根據(jù)策略處理:
                     - REJECT: 拋出異常,攔截請求
                     - REPLACE: 替換為 *** 并繼續(xù)執(zhí)行
                     - REVIEW: 標(biāo)記待審核,發(fā)送 MQ 消息
                                             ↓
                         [8] 記錄審計(jì)日志(異步)
                                             ↓
                         [9] 更新監(jiān)控指標(biāo)
                                             ↓
                         返回結(jié)果給業(yè)務(wù)層

3.3 三級緩存架構(gòu)

┌─────────────────────────────────────────────────────────┐
│                    檢測請求                                       │
└────────────────────────┬────────────────────────────────┘
                         │
                         │  L1: Caffeine 本地緩存
                         │  - 過期時(shí)間:10分鐘
                         │  - 命中率:> 95%
                         │  - RT:< 1ms
                         │
                    [命中] │ [未命中]
                         ↓
                         │  L2: Redis 分布式緩存
                         │  - 過期時(shí)間:1小時(shí)
                         │  - 命中率:> 99%
                         │  - RT:< 3ms
                         │
                    [命中] │ [未命中]
                         ↓
                         │  L3: MySQL 數(shù)據(jù)庫
                         │  - 持久化存儲
                         │  - 用于緩存重建
                         │  - RT:< 10ms
                         │
                         ↓
                    返回 Trie 樹

四、核心模塊設(shè)計(jì)

4.1 敏感詞注解@SensitiveCheck

package com.video.sensitive.annotation;

import com.video.sensitive.enums.MatchType;
import com.video.sensitive.enums.SensitiveStrategyType;
import java.lang.annotation.*;

/**
 * 敏感詞檢測注解
 * 用于標(biāo)記需要進(jìn)行敏感詞檢測的方法參數(shù)或返回值
 */
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface SensitiveCheck {
    
    /**
     * 需要檢測的參數(shù)名稱(支持SpEL表達(dá)式)
     * 示例:"#dto.content" 或 "#comment.content"
     */
    String[] fields() default {};
    
    /**
     * 處理策略
     * - REJECT: 直接拒絕請求(默認(rèn))
     * - REPLACE: 替換敏感詞為 ***
     * - REVIEW: 標(biāo)記為待人工審核
     */
    SensitiveStrategyType strategy() default SensitiveStrategyType.REJECT;
    
    /**
     * 匹配類型
     * - EXACT: 精確匹配(默認(rèn))
     * - FUZZY: 模糊匹配(支持變體、拆分、諧音)
     */
    MatchType matchType() default MatchType.EXACT;
    
    /**
     * 是否啟用白名單
     */
    boolean useWhitelist() default true;
    
    /**
     * 業(yè)務(wù)場景描述(用于日志和監(jiān)控)
     */
    String scene() default "";
    
    /**
     * 是否啟用降級保護(hù)
     * 當(dāng)檢測服務(wù)異常時(shí),是否允許請求通過
     */
    boolean enableFallback() default true;
    
    /**
     * 超時(shí)時(shí)間(毫秒)
     * 超過此時(shí)間未完成檢測,觸發(fā)降級
     */
    long timeout() default 100;
}

4.2 DFA 算法實(shí)現(xiàn)

4.2.1 Trie 樹數(shù)據(jù)結(jié)構(gòu)

package com.video.sensitive.core;

import java.util.HashMap;
import java.util.Map;

/**
 * 敏感詞 Trie 樹節(jié)點(diǎn)
 */
public class SensitiveWordTrie {
    
    /**
     * 根節(jié)點(diǎn)
     */
    private final TrieNode root = new TrieNode();
    
    /**
     * Trie 樹節(jié)點(diǎn)
     */
    private static class TrieNode {
        /**
         * 子節(jié)點(diǎn) Map(字符 -> 子節(jié)點(diǎn))
         */
        private Map<Character, TrieNode> children = new HashMap<>();
        
        /**
         * 是否為敏感詞結(jié)尾
         */
        private boolean isEnd = false;
        
        /**
         * 敏感詞等級(1-低,2-中,3-高)
         */
        private int level = 1;
        
        /**
         * 敏感詞原始文本(用于日志記錄)
         */
        private String word;
    }
    
    /**
     * 添加敏感詞到 Trie 樹
     * 
     * @param word 敏感詞
     * @param level 敏感等級
     */
    public void addWord(String word, int level) {
        if (word == null || word.isEmpty()) {
            return;
        }
        
        TrieNode node = root;
        for (char c : word.toCharArray()) {
            // 轉(zhuǎn)為小寫統(tǒng)一處理
            c = Character.toLowerCase(c);
            node = node.children.computeIfAbsent(c, k -> new TrieNode());
        }
        node.isEnd = true;
        node.level = level;
        node.word = word;
    }
    
    /**
     * 檢測文本中的敏感詞(DFA算法)
     * 
     * @param text 待檢測文本
     * @return 匹配結(jié)果列表
     */
    public List<MatchResult> match(String text) {
        if (text == null || text.isEmpty()) {
            return Collections.emptyList();
        }
        
        List<MatchResult> results = new ArrayList<>();
        int length = text.length();
        
        for (int i = 0; i < length; i++) {
            int matchLength = 0;
            TrieNode node = root;
            
            // DFA 狀態(tài)機(jī)匹配
            for (int j = i; j < length && node != null; j++) {
                char c = Character.toLowerCase(text.charAt(j));
                node = node.children.get(c);
                
                if (node != null) {
                    matchLength++;
                    // 找到完整敏感詞
                    if (node.isEnd) {
                        MatchResult result = new MatchResult();
                        result.setWord(node.word);
                        result.setStartIndex(i);
                        result.setEndIndex(j + 1);
                        result.setLevel(node.level);
                        result.setMatchedText(text.substring(i, j + 1));
                        results.add(result);
                        break;
                    }
                }
            }
        }
        
        return results;
    }
    
    /**
     * 匹配結(jié)果
     */
    @Data
    public static class MatchResult {
        private String word;           // 原始敏感詞
        private String matchedText;    // 匹配到的文本
        private int startIndex;        // 開始位置
        private int endIndex;          // 結(jié)束位置
        private int level;             // 敏感等級
    }
}

4.2.2 敏感詞過濾器實(shí)現(xiàn)

package com.video.sensitive.core;

import com.video.sensitive.enums.MatchType;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Set;

/**
 * DFA 算法實(shí)現(xiàn)的敏感詞過濾器
 */
@Slf4j
@Component
public class DfaSensitiveWordFilter implements SensitiveWordFilter {
    
    @Override
    public FilterResult filter(String text, SensitiveWordTrie wordTrie, 
                               MatchType matchType, Set<String> whitelist) {
        if (text == null || text.isEmpty()) {
            return FilterResult.clean();
        }
        
        // 1. 執(zhí)行敏感詞匹配
        List<SensitiveWordTrie.MatchResult> matches = wordTrie.match(text);
        
        // 2. 過濾白名單
        if (whitelist != null && !whitelist.isEmpty()) {
            matches = matches.stream()
                    .filter(m -> !whitelist.contains(m.getWord()))
                    .collect(Collectors.toList());
        }
        
        // 3. 模糊匹配增強(qiáng)(可選)
        if (matchType == MatchType.FUZZY && !matches.isEmpty()) {
            matches = enhanceFuzzyMatch(text, matches);
        }
        
        // 4. 構(gòu)建結(jié)果
        FilterResult result = new FilterResult();
        result.setHasSensitiveWord(!matches.isEmpty());
        result.setMatches(matches);
        result.setOriginalText(text);
        
        if (!matches.isEmpty()) {
            result.setFilteredText(replaceMatches(text, matches));
            result.setHighestLevel(matches.stream()
                    .mapToInt(SensitiveWordTrie.MatchResult::getLevel)
                    .max()
                    .orElse(1));
        } else {
            result.setFilteredText(text);
            result.setHighestLevel(0);
        }
        
        return result;
    }
    
    /**
     * 替換敏感詞為 ***
     */
    private String replaceMatches(String text, List<SensitiveWordTrie.MatchResult> matches) {
        StringBuilder sb = new StringBuilder(text);
        
        // 從后往前替換,避免索引位移
        matches.stream()
                .sorted((a, b) -> Integer.compare(b.getStartIndex(), a.getStartIndex()))
                .forEach(match -> {
                    int length = match.getEndIndex() - match.getStartIndex();
                    String replacement = "*".repeat(length);
                    sb.replace(match.getStartIndex(), match.getEndIndex(), replacement);
                });
        
        return sb.toString();
    }
    
    /**
     * 模糊匹配增強(qiáng)
     * 處理變體詞(如:法輪功 -> 法_輪_功、falungong)
     */
    private List<SensitiveWordTrie.MatchResult> enhanceFuzzyMatch(
            String text, List<SensitiveWordTrie.MatchResult> exactMatches) {
        // TODO: 實(shí)現(xiàn)模糊匹配邏輯
        // 1. 去除特殊符號后重新匹配
        // 2. 諧音轉(zhuǎn)換后匹配
        // 3. 拼音匹配
        return exactMatches;
    }
    
    /**
     * 過濾結(jié)果
     */
    @Data
    public static class FilterResult {
        private boolean hasSensitiveWord;               // 是否包含敏感詞
        private String originalText;                    // 原始文本
        private String filteredText;                    // 過濾后文本
        private List<SensitiveWordTrie.MatchResult> matches;  // 匹配結(jié)果列表
        private int highestLevel;                       // 最高敏感等級
        
        public static FilterResult clean() {
            FilterResult result = new FilterResult();
            result.setHasSensitiveWord(false);
            result.setMatches(Collections.emptyList());
            result.setHighestLevel(0);
            return result;
        }
    }
}

4.3 緩存管理器

package com.video.sensitive.cache;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.video.sensitive.core.SensitiveWordTrie;
import com.video.sensitive.entity.SensitiveWord;
import com.video.sensitive.mapper.SensitiveWordMapper;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 敏感詞緩存管理器(三級緩存)
 */
@Slf4j
@Component
public class SensitiveWordCacheManager {
    
    private static final String REDIS_KEY_WORDS = "sensitive:words:all";
    private static final String REDIS_KEY_WHITELIST = "sensitive:whitelist:all";
    private static final String REDIS_KEY_VERSION = "sensitive:version";
    
    @Resource
    private SensitiveWordMapper sensitiveWordMapper;
    
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    
    /**
     * L1: Caffeine 本地緩存 Trie 樹
     * 緩存時(shí)間:10分鐘,最大 1000 個(gè) key
     */
    private final Cache<String, SensitiveWordTrie> trieCache = Caffeine.newBuilder()
            .expireAfterWrite(10, TimeUnit.MINUTES)
            .maximumSize(1000)
            .build();
    
    /**
     * L1: 白名單本地緩存
     */
    private final Cache<String, Set<String>> whitelistCache = Caffeine.newBuilder()
            .expireAfterWrite(10, TimeUnit.MINUTES)
            .maximumSize(100)
            .build();
    
    /**
     * 應(yīng)用啟動時(shí)預(yù)熱緩存
     */
    @PostConstruct
    public void warmUp() {
        log.info("開始預(yù)熱敏感詞緩存...");
        try {
            // 預(yù)加載敏感詞 Trie 樹
            getWordTrie();
            // 預(yù)加載白名單
            getWhitelist();
            log.info("敏感詞緩存預(yù)熱完成");
        } catch (Exception e) {
            log.error("敏感詞緩存預(yù)熱失敗", e);
        }
    }
    
    /**
     * 獲取敏感詞 Trie 樹(三級緩存)
     */
    public SensitiveWordTrie getWordTrie() {
        String cacheKey = "default";
        
        // L1: 本地緩存
        SensitiveWordTrie trie = trieCache.getIfPresent(cacheKey);
        if (trie != null) {
            log.debug("命中 Caffeine 緩存");
            return trie;
        }
        
        // L2: Redis 緩存
        try {
            Set<String> redisWords = stringRedisTemplate.opsForSet().members(REDIS_KEY_WORDS);
            if (redisWords != null && !redisWords.isEmpty()) {
                log.debug("命中 Redis 緩存,詞庫大小: {}", redisWords.size());
                trie = buildTrieFromWords(redisWords);
                trieCache.put(cacheKey, trie);
                return trie;
            }
        } catch (Exception e) {
            log.warn("從 Redis 加載敏感詞失敗,降級到數(shù)據(jù)庫", e);
        }
        
        // L3: MySQL 數(shù)據(jù)庫
        List<SensitiveWord> words = sensitiveWordMapper.selectList(
                new LambdaQueryWrapper<SensitiveWord>()
                        .eq(SensitiveWord::getStatus, 1)
                        .eq(SensitiveWord::getIsDeleted, 0)
        );
        
        log.info("從數(shù)據(jù)庫加載敏感詞,數(shù)量: {}", words.size());
        
        // 構(gòu)建 Trie 樹
        trie = new SensitiveWordTrie();
        for (SensitiveWord word : words) {
            trie.addWord(word.getWord(), word.getLevel());
        }
        
        // 回寫緩存
        trieCache.put(cacheKey, trie);
        
        // 回寫 Redis
        try {
            Set<String> wordSet = words.stream()
                    .map(SensitiveWord::getWord)
                    .collect(Collectors.toSet());
            stringRedisTemplate.opsForSet().add(REDIS_KEY_WORDS, wordSet.toArray(new String[0]));
            stringRedisTemplate.expire(REDIS_KEY_WORDS, 1, TimeUnit.HOURS);
        } catch (Exception e) {
            log.warn("回寫 Redis 緩存失敗", e);
        }
        
        return trie;
    }
    
    /**
     * 獲取白名單(三級緩存)
     */
    public Set<String> getWhitelist() {
        String cacheKey = "default";
        
        // L1: 本地緩存
        Set<String> whitelist = whitelistCache.getIfPresent(cacheKey);
        if (whitelist != null) {
            return whitelist;
        }
        
        // L2: Redis 緩存
        try {
            Set<String> redisWhitelist = stringRedisTemplate.opsForSet().members(REDIS_KEY_WHITELIST);
            if (redisWhitelist != null && !redisWhitelist.isEmpty()) {
                whitelistCache.put(cacheKey, redisWhitelist);
                return redisWhitelist;
            }
        } catch (Exception e) {
            log.warn("從 Redis 加載白名單失敗", e);
        }
        
        // L3: 數(shù)據(jù)庫
        List<Whitelist> list = whitelistMapper.selectList(
                new LambdaQueryWrapper<Whitelist>()
                        .eq(Whitelist::getStatus, 1)
        );
        
        whitelist = list.stream()
                .map(Whitelist::getWord)
                .collect(Collectors.toSet());
        
        // 回寫緩存
        whitelistCache.put(cacheKey, whitelist);
        try {
            stringRedisTemplate.opsForSet().add(REDIS_KEY_WHITELIST, whitelist.toArray(new String[0]));
            stringRedisTemplate.expire(REDIS_KEY_WHITELIST, 1, TimeUnit.HOURS);
        } catch (Exception e) {
            log.warn("回寫白名單 Redis 緩存失敗", e);
        }
        
        return whitelist;
    }
    
    /**
     * 清除所有緩存(用于敏感詞更新后刷新)
     */
    public void clearCache() {
        log.info("清除敏感詞緩存");
        trieCache.invalidateAll();
        whitelistCache.invalidateAll();
        
        try {
            stringRedisTemplate.delete(REDIS_KEY_WORDS);
            stringRedisTemplate.delete(REDIS_KEY_WHITELIST);
            // 版本號+1,通知其他節(jié)點(diǎn)刷新
            stringRedisTemplate.opsForValue().increment(REDIS_KEY_VERSION);
        } catch (Exception e) {
            log.error("清除 Redis 緩存失敗", e);
        }
    }
    
    /**
     * 從詞列表構(gòu)建 Trie 樹
     */
    private SensitiveWordTrie buildTrieFromWords(Set<String> words) {
        SensitiveWordTrie trie = new SensitiveWordTrie();
        for (String word : words) {
            trie.addWord(word, 1); // 從 Redis 加載默認(rèn)等級為 1
        }
        return trie;
    }
}

4.4 AOP 切面實(shí)現(xiàn)

package com.video.sensitive.aspect;

import com.video.common.Result;
import com.video.sensitive.annotation.SensitiveCheck;
import com.video.sensitive.cache.SensitiveWordCacheManager;
import com.video.sensitive.core.DfaSensitiveWordFilter;
import com.video.sensitive.core.SensitiveWordTrie;
import com.video.sensitive.enums.SensitiveStrategyType;
import com.video.sensitive.exception.SensitiveContentException;
import com.video.sensitive.monitor.SensitiveAuditLogger;
import com.video.sensitive.monitor.SensitiveMonitor;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
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.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * 敏感詞檢測切面
 */
@Slf4j
@Aspect
@Component
public class SensitiveCheckAspect {
    
    @Resource
    private DfaSensitiveWordFilter sensitiveWordFilter;
    
    @Resource
    private SensitiveWordCacheManager cacheManager;
    
    @Resource
    private SensitiveAuditLogger auditLogger;
    
    @Resource
    private SensitiveMonitor monitor;
    
    private final ExpressionParser parser = new SpelExpressionParser();
    
    @Around("@annotation(com.video.sensitive.annotation.SensitiveCheck)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        SensitiveCheck annotation = method.getAnnotation(SensitiveCheck.class);
        
        try {
            // 1. 提取待檢測文本
            String[] fields = annotation.fields();
            if (fields.length == 0) {
                return joinPoint.proceed();
            }
            
            Object[] args = joinPoint.getArgs();
            String[] paramNames = signature.getParameterNames();
            
            // 2. 使用 SpEL 提取字段值
            StandardEvaluationContext context = new StandardEvaluationContext();
            for (int i = 0; i < paramNames.length; i++) {
                context.setVariable(paramNames[i], args[i]);
            }
            
            // 3. 檢測每個(gè)字段
            for (String field : fields) {
                Expression expression = parser.parseExpression(field);
                Object value = expression.getValue(context);
                
                if (value instanceof String) {
                    String text = (String) value;
                    checkText(text, annotation, method.getName());
                }
            }
            
            // 4. 執(zhí)行業(yè)務(wù)邏輯
            Object result = joinPoint.proceed();
            
            // 5. 記錄監(jiān)控指標(biāo)
            long cost = System.currentTimeMillis() - startTime;
            monitor.recordCheckSuccess(annotation.scene(), cost);
            
            return result;
            
        } catch (SensitiveContentException e) {
            // 6. 敏感詞檢測失敗
            long cost = System.currentTimeMillis() - startTime;
            monitor.recordCheckReject(annotation.scene(), cost);
            throw e;
            
        } catch (Exception e) {
            // 7. 系統(tǒng)異常,觸發(fā)降級
            long cost = System.currentTimeMillis() - startTime;
            monitor.recordCheckError(annotation.scene(), cost);
            
            if (annotation.enableFallback()) {
                log.warn("敏感詞檢測異常,觸發(fā)降級保護(hù),允許請求通過", e);
                return joinPoint.proceed();
            } else {
                throw e;
            }
        }
    }
    
    /**
     * 檢測文本
     */
    private void checkText(String text, SensitiveCheck annotation, String methodName) {
        if (text == null || text.trim().isEmpty()) {
            return;
        }
        
        // 1. 獲取敏感詞 Trie 樹
        SensitiveWordTrie wordTrie = cacheManager.getWordTrie();
        
        // 2. 獲取白名單
        Set<String> whitelist = annotation.useWhitelist() ? 
                cacheManager.getWhitelist() : null;
        
        // 3. 執(zhí)行過濾
        DfaSensitiveWordFilter.FilterResult result = sensitiveWordFilter.filter(
                text, wordTrie, annotation.matchType(), whitelist);
        
        // 4. 處理結(jié)果
        if (result.isHasSensitiveWord()) {
            // 記錄審計(jì)日志
            auditLogger.logSensitive(
                    annotation.scene(),
                    methodName,
                    text,
                    result.getMatches(),
                    annotation.strategy()
            );
            
            // 根據(jù)策略處理
            handleByStrategy(annotation.strategy(), text, result);
        }
    }
    
    /**
     * 根據(jù)策略處理敏感內(nèi)容
     */
    private void handleByStrategy(SensitiveStrategyType strategy, 
                                   String text, 
                                   DfaSensitiveWordFilter.FilterResult result) {
        switch (strategy) {
            case REJECT:
                // 直接拒絕
                throw new SensitiveContentException(
                        "內(nèi)容包含敏感詞,禁止發(fā)布",
                        result.getMatches()
                );
                
            case REPLACE:
                // 替換敏感詞(修改原對象)
                // 注意:需要通過反射修改原始對象的字段值
                log.info("替換敏感詞: {} -> {}", text, result.getFilteredText());
                break;
                
            case REVIEW:
                // 標(biāo)記待審核,發(fā)送 MQ 消息
                log.info("標(biāo)記內(nèi)容待審核: {}", text);
                // TODO: 發(fā)送 RabbitMQ 消息給審核系統(tǒng)
                break;
                
            default:
                throw new IllegalArgumentException("未知策略: " + strategy);
        }
    }
}

五、數(shù)據(jù)庫設(shè)計(jì)

5.1 敏感詞表

CREATE TABLE `a_sensitive_word` (
  `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主鍵ID',
  `word` VARCHAR(200) NOT NULL COMMENT '敏感詞',
  `level` TINYINT NOT NULL DEFAULT 1 COMMENT '敏感等級:1-低,2-中,3-高',
  `category` VARCHAR(50) DEFAULT NULL COMMENT '分類:政治、色情、暴力、廣告等',
  `status` TINYINT NOT NULL DEFAULT 1 COMMENT '狀態(tài):0-禁用,1-啟用',
  `remark` VARCHAR(500) DEFAULT NULL COMMENT '備注說明',
  `create_user` BIGINT DEFAULT NULL COMMENT '創(chuàng)建人',
  `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創(chuàng)建時(shí)間',
  `update_user` BIGINT DEFAULT NULL COMMENT '修改人',
  `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改時(shí)間',
  `is_deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '是否刪除:0-未刪除,1-已刪除',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_word` (`word`),
  KEY `idx_level` (`level`),
  KEY `idx_category` (`category`),
  KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='敏感詞表';

5.2 白名單表

CREATE TABLE `a_sensitive_whitelist` (
  `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主鍵ID',
  `word` VARCHAR(200) NOT NULL COMMENT '白名單詞匯',
  `status` TINYINT NOT NULL DEFAULT 1 COMMENT '狀態(tài):0-禁用,1-啟用',
  `remark` VARCHAR(500) DEFAULT NULL COMMENT '備注說明',
  `create_user` BIGINT DEFAULT NULL COMMENT '創(chuàng)建人',
  `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創(chuàng)建時(shí)間',
  `update_user` BIGINT DEFAULT NULL COMMENT '修改人',
  `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改時(shí)間',
  `is_deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '是否刪除:0-未刪除,1-已刪除',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_word` (`word`),
  KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='敏感詞白名單';

5.3 審計(jì)日志表

CREATE TABLE `a_sensitive_log` (
  `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主鍵ID',
  `user_id` BIGINT DEFAULT NULL COMMENT '用戶ID',
  `scene` VARCHAR(50) NOT NULL COMMENT '業(yè)務(wù)場景',
  `method_name` VARCHAR(200) DEFAULT NULL COMMENT '方法名',
  `original_text` TEXT COMMENT '原始文本',
  `matched_words` VARCHAR(500) DEFAULT NULL COMMENT '匹配的敏感詞(JSON數(shù)組)',
  `strategy` VARCHAR(20) NOT NULL COMMENT '處理策略',
  `result` VARCHAR(20) NOT NULL COMMENT '處理結(jié)果:PASS-通過,REJECT-拒絕,REVIEW-審核',
  `ip` VARCHAR(50) DEFAULT NULL COMMENT '客戶端IP',
  `user_agent` VARCHAR(500) DEFAULT NULL COMMENT '客戶端UA',
  `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創(chuàng)建時(shí)間',
  PRIMARY KEY (`id`),
  KEY `idx_user_id` (`user_id`),
  KEY `idx_scene` (`scene`),
  KEY `idx_create_time` (`create_time`),
  KEY `idx_result` (`result`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='敏感詞審計(jì)日志';

-- 按月分區(qū)
ALTER TABLE `a_sensitive_log` PARTITION BY RANGE (TO_DAYS(`create_time`)) (
    PARTITION p202601 VALUES LESS THAN (TO_DAYS('2026-02-01')),
    PARTITION p202602 VALUES LESS THAN (TO_DAYS('2026-03-01')),
    PARTITION p202603 VALUES LESS THAN (TO_DAYS('2026-04-01')),
    PARTITION p_max VALUES LESS THAN MAXVALUE
);

六、使用示例

6.1 評論發(fā)布場景

@PostMapping("/add")
@Operation(summary = "發(fā)表評論")
@SensitiveCheck(
    fields = {"#dto.content"},
    strategy = SensitiveStrategyType.REJECT,
    matchType = MatchType.EXACT,
    scene = "comment_publish",
    enableFallback = true,
    timeout = 100
)
public Result<CommentOperationVO> addComment(
        @RequestHeader("userId") Long userId,
        @RequestBody CommentDTO dto) {
    VideoComment comment = videoCommentService.addComment(userId, dto);
    return Result.success(CommentOperationVO.of(comment));
}

6.2 私信發(fā)送場景

@PostMapping("/send")
@SensitiveCheck(
    fields = {"#message.content"},
    strategy = SensitiveStrategyType.REVIEW,  // 私信使用人工審核
    matchType = MatchType.FUZZY,               // 使用模糊匹配
    scene = "private_message",
    enableFallback = false                      // 不允許降級
)
public Result<Void> sendMessage(
        @RequestHeader("userId") Long userId,
        @RequestBody PrivateMessage message) {
    privateMessageService.send(userId, message);
    return Result.success();
}

6.3 劇情評論場景

@PostMapping("/add")
@Operation(summary = "發(fā)表劇情評論")
@SensitiveCheck(
    fields = {"#dto.content"},
    strategy = SensitiveStrategyType.REJECT,
    scene = "story_comment_publish"
)
public Result<StoryComment> addComment(
        @RequestParam Long userId,
        @RequestBody CommentDTO dto) {
    StoryComment comment = storyCommentService.addComment(userId, dto);
    return Result.success(comment);
}

七、敏感詞管理接口

7.1 管理接口設(shè)計(jì)

@RestController
@RequestMapping("/api/admin/sensitive")
@Tag(name = "敏感詞管理", description = "敏感詞庫管理接口(需管理員權(quán)限)")
public class SensitiveWordController {
    
    @Resource
    private SensitiveWordService sensitiveWordService;
    
    /**
     * 添加敏感詞
     */
    @PostMapping("/word/add")
    @PreAuthorize("hasAuthority('ROLE_ADMIN')")
    public Result<Void> addWord(@RequestBody SensitiveWordDTO dto) {
        sensitiveWordService.addWord(dto);
        return Result.success();
    }
    
    /**
     * 批量導(dǎo)入敏感詞
     */
    @PostMapping("/word/batch/import")
    @PreAuthorize("hasAuthority('ROLE_ADMIN')")
    public Result<ImportResult> batchImport(@RequestBody List<SensitiveWordDTO> words) {
        ImportResult result = sensitiveWordService.batchImport(words);
        return Result.success(result);
    }
    
    /**
     * 刪除敏感詞
     */
    @DeleteMapping("/word/{id}")
    @PreAuthorize("hasAuthority('ROLE_ADMIN')")
    public Result<Void> deleteWord(@PathVariable Long id) {
        sensitiveWordService.deleteWord(id);
        return Result.success();
    }
    
    /**
     * 更新敏感詞
     */
    @PutMapping("/word/{id}")
    @PreAuthorize("hasAuthority('ROLE_ADMIN')")
    public Result<Void> updateWord(@PathVariable Long id, @RequestBody SensitiveWordDTO dto) {
        sensitiveWordService.updateWord(id, dto);
        return Result.success();
    }
    
    /**
     * 刷新緩存(熱更新)
     */
    @PostMapping("/cache/refresh")
    @PreAuthorize("hasAuthority('ROLE_ADMIN')")
    public Result<Void> refreshCache() {
        sensitiveWordService.refreshCache();
        return Result.success();
    }
    
    /**
     * 分頁查詢敏感詞
     */
    @GetMapping("/word/page")
    @PreAuthorize("hasAuthority('ROLE_ADMIN')")
    public Result<IPage<SensitiveWord>> getWordPage(
            @RequestParam(defaultValue = "1") int pageNum,
            @RequestParam(defaultValue = "20") int pageSize,
            @RequestParam(required = false) String keyword) {
        IPage<SensitiveWord> page = sensitiveWordService.getWordPage(pageNum, pageSize, keyword);
        return Result.success(page);
    }
    
    /**
     * 測試文本檢測
     */
    @PostMapping("/test")
    @PreAuthorize("hasAuthority('ROLE_ADMIN')")
    public Result<FilterResult> testCheck(@RequestBody TestRequest request) {
        FilterResult result = sensitiveWordService.testCheck(request.getText());
        return Result.success(result);
    }
}

八、性能優(yōu)化方案

8.1 性能設(shè)計(jì)考量

優(yōu)化點(diǎn)具體措施預(yù)期效果
算法優(yōu)化使用 DFA + Trie 樹,時(shí)間復(fù)雜度 O(n)單次檢測 < 3ms
緩存分層Caffeine(L1)+ Redis(L2)+ MySQL(L3)緩存命中率 > 99%
預(yù)熱機(jī)制應(yīng)用啟動時(shí)預(yù)加載敏感詞庫首次請求無延遲
異步日志審計(jì)日志異步寫入,不阻塞主流程RT 降低 80%
降級保護(hù)檢測超時(shí)/異常時(shí)允許請求通過可用性 99.9%
批量操作批量導(dǎo)入/更新使用事務(wù)導(dǎo)入速度提升 10 倍

8.2 壓測方案

8.2.1 壓測場景

場景 A:高并發(fā)評論發(fā)布

  • 目標(biāo) QPS:15,000
  • 敏感詞比例:10%
  • 期望 RT:< 10ms(P95)

場景 B:敏感詞命中測試

  • 目標(biāo) QPS:10,000
  • 敏感詞比例:100%
  • 期望拒絕率:100%

8.2.2 性能指標(biāo)

性能基線:
  QPS: ≥ 15,000
  響應(yīng)時(shí)間:
    P50: < 5ms
    P95: < 10ms
    P99: < 20ms
  緩存命中率: ≥ 99%
  錯(cuò)誤率: < 0.1%
  可用性: ≥ 99.9%

九、監(jiān)控與告警

9.1 監(jiān)控指標(biāo)

@Component
public class SensitiveMonitor {
    
    @Resource
    private MeterRegistry meterRegistry;
    
    /**
     * 記錄檢測成功
     */
    public void recordCheckSuccess(String scene, long costMs) {
        Counter.builder("sensitive.check.success")
                .tag("scene", scene)
                .register(meterRegistry)
                .increment();
        
        Timer.builder("sensitive.check.latency")
                .tag("scene", scene)
                .register(meterRegistry)
                .record(costMs, TimeUnit.MILLISECONDS);
    }
    
    /**
     * 記錄檢測拒絕
     */
    public void recordCheckReject(String scene, long costMs) {
        Counter.builder("sensitive.check.reject")
                .tag("scene", scene)
                .register(meterRegistry)
                .increment();
    }
    
    /**
     * 記錄檢測異常
     */
    public void recordCheckError(String scene, long costMs) {
        Counter.builder("sensitive.check.error")
                .tag("scene", scene)
                .register(meterRegistry)
                .increment();
    }
}

9.2 告警規(guī)則

指標(biāo)閾值級別處理方式
檢測錯(cuò)誤率> 1%P1短信 + 電話
平均響應(yīng)時(shí)間> 50msP2短信
緩存命中率< 95%P3郵件
敏感詞攔截?cái)?shù)> 1000/minP3郵件

十、安全設(shè)計(jì)

10.1 權(quán)限控制

  • 敏感詞管理接口:僅限 ROLE_ADMIN 角色訪問
  • 日志查詢接口:僅限 ROLE_AUDITOR 角色訪問
  • 配置變更:需二次確認(rèn) + 審計(jì)日志

10.2 數(shù)據(jù)安全

  • 審計(jì)日志脫敏:原始文本存儲時(shí)進(jìn)行加密或脫敏
  • 敏感詞加密:特別敏感的詞庫可加密存儲
  • 訪問控制:Redis/MySQL 訪問需白名單 + 密碼認(rèn)證

10.3 防刷機(jī)制

  • 接口限流:管理接口使用 @RateLimiter 注解
  • 異常檢測:短時(shí)間大量敏感內(nèi)容觸發(fā)時(shí)封禁用戶

十一、部署方案

11.1 配置文件

# application.yml
sensitive:
  enabled: true                    # 是否啟用敏感詞檢測
  fallback-enabled: true           # 是否啟用降級保護(hù)
  default-strategy: REJECT         # 默認(rèn)處理策略
  cache:
    caffeine:
      expire-minutes: 10           # 本地緩存過期時(shí)間
      maximum-size: 1000           # 本地緩存最大數(shù)量
    redis:
      expire-hours: 1              # Redis 緩存過期時(shí)間
  performance:
    timeout-ms: 100                # 檢測超時(shí)時(shí)間
    async-log: true                # 異步寫入審計(jì)日志
  match:
    fuzzy-enabled: false           # 是否啟用模糊匹配
    whitelist-enabled: true        # 是否啟用白名單

11.2 初始化腳本

-- 插入測試敏感詞
INSERT INTO `a_sensitive_word` (`word`, `level`, `category`, `status`) VALUES
('測試敏感詞', 1, '測試', 1),
('法輪功', 3, '政治', 1),
('色情', 2, '色情', 1);

-- 插入白名單
INSERT INTO `a_sensitive_whitelist` (`word`, `status`) VALUES
('正常詞匯', 1);

十二、總結(jié)與最佳實(shí)踐

12.1 核心優(yōu)勢

  1. 高性能:DFA + Trie 樹算法,O(n) 時(shí)間復(fù)雜度,支持 15,000+ QPS
  2. 高可用:三級緩存 + 降級保護(hù),可用性 ≥ 99.9%
  3. 低侵入:注解驅(qū)動,業(yè)務(wù)代碼改動最小
  4. 易維護(hù):熱更新敏感詞庫,無需重啟服務(wù)
  5. 可擴(kuò)展:支持多種策略、模糊匹配、白名單等擴(kuò)展能力

12.2 最佳實(shí)踐

敏感詞庫管理

  • 定期更新敏感詞庫,及時(shí)響應(yīng)熱點(diǎn)事件
  • 對敏感詞按等級分類,區(qū)別對待
  • 建立白名單機(jī)制,避免誤殺正常內(nèi)容

性能優(yōu)化

  • 預(yù)熱緩存,避免冷啟動影響
  • 使用異步日志,避免阻塞主流程
  • 合理設(shè)置緩存過期時(shí)間,平衡命中率與實(shí)時(shí)性

監(jiān)控告警

  • 建立完善的監(jiān)控指標(biāo)體系
  • 設(shè)置合理的告警閾值
  • 定期review審計(jì)日志,發(fā)現(xiàn)異常模式

安全防護(hù)

  • 敏感詞管理接口嚴(yán)格權(quán)限控制
  • 審計(jì)日志定期歸檔,防止數(shù)據(jù)泄露
  • 建立防刷機(jī)制,防止惡意攻擊

12.3 注意事項(xiàng)

  1. 降級策略:務(wù)必配置降級保護(hù),避免因檢測服務(wù)異常影響核心業(yè)務(wù)
  2. 緩存一致性:多實(shí)例部署時(shí),通過 Redis 版本號通知其他節(jié)點(diǎn)刷新緩存
  3. 日志存儲:審計(jì)日志量大,建議按月分區(qū)或使用 ES 存儲
  4. 性能測試:上線前必須進(jìn)行壓測,驗(yàn)證性能指標(biāo)

12.4 常見問題 FAQ

Q1: DFA 算法的時(shí)間復(fù)雜度是多少?

A: DFA 算法的時(shí)間復(fù)雜度為 O(n),其中 n 是文本長度。無論敏感詞庫有多大,檢測時(shí)間只與文本長度相關(guān)。

Q2: 如何處理敏感詞的變體(如拆分、諧音)?

A: 可以啟用模糊匹配模式(matchType = MatchType.FUZZY),在過濾器中實(shí)現(xiàn):

  • 去除特殊符號后重新匹配
  • 諧音轉(zhuǎn)換后匹配
  • 拼音匹配

Q3: 如何保證多節(jié)點(diǎn)緩存一致性?

A: 通過 Redis 版本號機(jī)制:

  1. 敏感詞更新時(shí),版本號+1
  2. 各節(jié)點(diǎn)定時(shí)檢查版本號
  3. 發(fā)現(xiàn)版本不一致時(shí),清空本地緩存

Q4: REPLACE 策略如何修改原始對象?

A: 需要通過反射修改原始對象的字段值,示例代碼:

Field field = dto.getClass().getDeclaredField("content");
field.setAccessible(true);
field.set(dto, result.getFilteredText());

Q5: 如何防止敏感詞庫被惡意獲取?

A:

  • 管理接口嚴(yán)格權(quán)限控制
  • 不提供批量導(dǎo)出功能
  • 測試接口加頻率限制
  • 審計(jì)日志記錄所有訪問

以上就是SpringBoot實(shí)現(xiàn)企業(yè)級敏感詞攔截檢查系統(tǒng)的設(shè)計(jì)方案的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot敏感詞攔截檢查系統(tǒng)的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java將Exception信息轉(zhuǎn)為String字符串的方法

    Java將Exception信息轉(zhuǎn)為String字符串的方法

    今天小編就為大家分享一篇Java將Exception信息轉(zhuǎn)為String字符串的方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-10-10
  • 值得收藏的SpringBoot 實(shí)用的小技巧

    值得收藏的SpringBoot 實(shí)用的小技巧

    最近分享的一些源碼、框架設(shè)計(jì)的東西。我發(fā)現(xiàn)大家熱情不是特別高,想想大多數(shù)應(yīng)該還是正兒八經(jīng)寫代碼的居多;這次就分享一點(diǎn)接地氣的: SpringBoot 使用中的一些小技巧 ,需要的朋友可以參考下
    2018-10-10
  • Java?數(shù)據(jù)結(jié)構(gòu)與算法系列精講之隊(duì)列

    Java?數(shù)據(jù)結(jié)構(gòu)與算法系列精講之隊(duì)列

    這篇文章主要介紹了Java隊(duì)列數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn),隊(duì)列是一種特殊的線性表,只允許在表的隊(duì)頭進(jìn)行刪除操作,在表的后端進(jìn)行插入操作,隊(duì)列是一個(gè)有序表先進(jìn)先出,想了解更多相關(guān)資料的小伙伴可以參考下面文章的詳細(xì)內(nèi)容
    2022-02-02
  • feign調(diào)用返回object類型轉(zhuǎn)換方式

    feign調(diào)用返回object類型轉(zhuǎn)換方式

    這篇文章主要介紹了feign調(diào)用返回object類型轉(zhuǎn)換方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • Hadoop中的壓縮與解壓縮案例詳解

    Hadoop中的壓縮與解壓縮案例詳解

    壓縮就是通過某種技術(shù)(算法)把原始文件變下,相應(yīng)的解壓就是把壓縮后的文件變成原始文件,本文給大家分享Hadoop中的壓縮知識,感興趣的朋友跟隨小編一起看看吧
    2021-12-12
  • Spring依賴注入(DI)兩種方式的示例詳解

    Spring依賴注入(DI)兩種方式的示例詳解

    這篇文章主要介紹了Spring依賴注入(DI)的兩種方式:setter注入和構(gòu)造器注入。文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下
    2022-06-06
  • JAVA強(qiáng)密碼校驗(yàn)算法示例代碼

    JAVA強(qiáng)密碼校驗(yàn)算法示例代碼

    最近系統(tǒng)需要做用戶密碼升級,增加強(qiáng)密碼校驗(yàn),密碼長度,復(fù)雜度等等,這篇文章主要介紹了JAVA強(qiáng)密碼校驗(yàn)算法的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-07-07
  • mac下idea的svn密碼記不住的問題及處理方法

    mac下idea的svn密碼記不住的問題及處理方法

    這篇文章主要介紹了mac下idea的svn密碼記不住的問題及處理方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-09-09
  • Java二叉搜索樹遍歷操作詳解【前序、中序、后序、層次、廣度優(yōu)先遍歷】

    Java二叉搜索樹遍歷操作詳解【前序、中序、后序、層次、廣度優(yōu)先遍歷】

    這篇文章主要介紹了Java二叉搜索樹遍歷操作,結(jié)合實(shí)例形式詳細(xì)分析了Java二叉搜索樹前序、中序、后序、層次、廣度優(yōu)先遍歷等相關(guān)原理與操作技巧,需要的朋友可以參考下
    2020-03-03
  • Java多線程學(xué)習(xí)筆記

    Java多線程學(xué)習(xí)筆記

    常用的實(shí)現(xiàn)多線程的兩種方式:Thread和Runnable。之所以說是“常用”,是因?yàn)樵贘ava 5后可以通過java.util.concurrent包中的線程池來實(shí)現(xiàn)多線程
    2021-09-09

最新評論

揭东县| 中西区| 嵊州市| 南丰县| 滨州市| 额尔古纳市| 临湘市| 庄河市| 通城县| 石景山区| 东丰县| 黄石市| 青海省| 达孜县| 错那县| 南部县| 长汀县| 桂阳县| 师宗县| 高尔夫| 通榆县| 茂名市| 长沙市| 大悟县| 韶关市| 盐山县| 江北区| 永新县| 嵊州市| 五指山市| 丘北县| 余干县| 宕昌县| 西乌珠穆沁旗| 宜宾市| 靖州| 吉林市| 炎陵县| 灵丘县| 大安市| 浦城县|