diff --git a/CHANGE_LOG.md b/CHANGE_LOG.md index 10e9272..7c5990e 100644 --- a/CHANGE_LOG.md +++ b/CHANGE_LOG.md @@ -106,3 +106,9 @@ | 序号 | 变更类型 | 说明 | 时间 | 备注 | |:---|:---|:---|:---|:--| | 1 | A | 开发样式配置特性 | 2021-5-31 20:51:58 | | + +# release_0.0.15 + +| 序号 | 变更类型 | 说明 | 时间 | 备注 | +|:---|:---|:---|:---|:--| +| 1 | A | 优化 init 方式 | 2021-7-16 20:51:58 | | diff --git a/README.md b/README.md index 3cb6a1b..0517ed0 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,8 @@ - 支持用户自定义敏感词和白名单 +- 支持数据的数据动态更新,实时生效 + ## 变更日志 [CHANGE_LOG.md](https://github.com/houbb/sensitive-word/blob/master/doc/CHANGE_LOG.md) @@ -56,7 +58,7 @@ com.github.houbb sensitive-word - 0.0.14 + 0.0.15 ``` @@ -391,6 +393,88 @@ Assert.assertEquals("[我的自定义敏感词]", wordBs.findAll(text).toString( 这里都是同时使用了系统默认配置,和自定义的配置。 +# spring 整合 + +## 背景 + +实际使用中,比如可以在页面配置修改,然后实时生效。 + +数据存储在数据库中,下面是一个伪代码的例子,可以参考 [SpringSensitiveWordConfig.java]() + +要求,版本 v0.0.15 及其以上。 + +## 自定义数据源 + +简化伪代码如下,数据的源头为数据库。 + +MyDdWordAllow 和 MyDdWordDeny 是基于数据库为源头的自定义实现类。 + +```java +@Configuration +public class SpringSensitiveWordConfig { + + @Autowired + private MyDdWordAllow myDdWordAllow; + + @Autowired + private MyDdWordDeny myDdWordDeny; + + /** + * 初始化引导类 + * @return 初始化引导类 + * @since 1.0.0 + */ + @Bean + public SensitiveWordBs sensitiveWordBs() { + SensitiveWordBs sensitiveWordBs = SensitiveWordBs.newInstance() + .wordAllow(WordAllows.chains(WordAllows.system(), myDdWordAllow)) + .wordDeny(myDdWordDeny) + // 各种其他配置 + .init(); + + return sensitiveWordBs; + } + +} +``` + +敏感词库的初始化较为耗时,建议程序启动时做一次 init 初始化。 + +## 动态变更 + +为了保证敏感词修改可以实时生效且保证接口的尽可能简化,此处没有新增 add/remove 的方法。 + +而是在调用 `sensitiveWordBs.init()` 的时候,根据 IWordDeny+IWordAllow 重新构建敏感词库。 + +因为初始化可能耗时较长(秒级别),所有优化为 init 未完成时**不影响旧的词库功能,完成后以新的为准**。 + +```java +@Component +public class SensitiveWordService { + + @Autowired + private SensitiveWordBs sensitiveWordBs; + + /** + * 更新词库 + * + * 每次数据库的信息发生变化之后,首先调用更新数据库敏感词库的方法。 + * 如果需要生效,则调用这个方法。 + * + * 说明:重新初始化不影响旧的方法使用。初始化完成后,会以新的为准。 + */ + public void refresh() { + // 每次数据库的信息发生变化之后,首先调用更新数据库敏感词库的方法,然后调用这个方法。 + sensitiveWordBs.init(); + } + +} +``` + +如上,你可以在数据库词库发生变更时,需要词库生效,主动触发一次初始化 `sensitiveWordBs.init();`。 + +其他使用保持不变,无需重启应用。 + # 后期 road-map - 停顿词 diff --git a/pom.xml b/pom.xml index c2ca142..1a6c3b9 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.github.houbb sensitive-word - 0.0.14 + 0.0.15 diff --git a/release.bat b/release.bat index 629c269..43b1415 100644 --- a/release.bat +++ b/release.bat @@ -10,9 +10,9 @@ ECHO "============================= RELEASE START..." :: 版本号信息(需要手动指定) :::: 旧版本名称 -SET version=0.0.14 +SET version=0.0.15 :::: 新版本名称 -SET newVersion=0.0.15 +SET newVersion=0.0.16 :::: 组织名称 SET groupName=com.github.houbb :::: 项目名称 diff --git a/src/main/java/com/github/houbb/sensitive/word/bs/SensitiveWordBs.java b/src/main/java/com/github/houbb/sensitive/word/bs/SensitiveWordBs.java index 75d3aaa..a8f0863 100644 --- a/src/main/java/com/github/houbb/sensitive/word/bs/SensitiveWordBs.java +++ b/src/main/java/com/github/houbb/sensitive/word/bs/SensitiveWordBs.java @@ -68,7 +68,10 @@ public class SensitiveWordBs { List results = CollectionUtil.difference(denyList, allowList); // 初始化 DFA 信息 - sensitiveWordMap = new SensitiveWordMap(); + if(sensitiveWordMap == null) { + sensitiveWordMap = new SensitiveWordMap(); + } + // 便于可以多次初始化 sensitiveWordMap.initWordMap(results); } @@ -321,8 +324,13 @@ public class SensitiveWordBs { * @since 0.0.13 */ private void statusCheck(){ + //DLC if(sensitiveWordMap == null) { - this.init(); + synchronized (this) { + if(sensitiveWordMap == null) { + this.init(); + } + } } } diff --git a/src/main/java/com/github/houbb/sensitive/word/support/map/SensitiveWordMap.java b/src/main/java/com/github/houbb/sensitive/word/support/map/SensitiveWordMap.java index 35e5295..e42bfef 100644 --- a/src/main/java/com/github/houbb/sensitive/word/support/map/SensitiveWordMap.java +++ b/src/main/java/com/github/houbb/sensitive/word/support/map/SensitiveWordMap.java @@ -50,15 +50,10 @@ public class SensitiveWordMap implements IWordMap { */ @Override @SuppressWarnings("unchecked") - public void initWordMap(Collection collection) { - // 避免重复加载 - if (MapUtil.isNotEmpty(innerWordMap)) { - return; - } - + public synchronized void initWordMap(Collection collection) { long startTime = System.currentTimeMillis(); // 避免扩容带来的消耗 - innerWordMap = new HashMap(collection.size()); + Map newInnerWordMap = new HashMap(collection.size()); for (String key : collection) { if (StringUtil.isEmpty(key)) { @@ -70,7 +65,7 @@ public class SensitiveWordMap implements IWordMap { final int size = chars.length; // 每一个新词的循环,直接将结果设置为当前 map,所有变化都会体现在结果的 map 中 - Map currentMap = innerWordMap; + Map currentMap = newInnerWordMap; for (int i = 0; i < size; i++) { // 截取敏感词当中的字,在敏感词库中字为HashMap对象的Key键值 @@ -101,6 +96,9 @@ public class SensitiveWordMap implements IWordMap { } } + // 最后更新为新的 map,保证更新过程中旧的数据可用 + this.innerWordMap = newInnerWordMap; + long endTime = System.currentTimeMillis(); System.out.println("Init sensitive word map end! Cost time: " + (endTime - startTime) + "ms"); } diff --git a/src/test/java/com/github/houbb/sensitive/word/spring/SpringSensitiveWordConfig.java b/src/test/java/com/github/houbb/sensitive/word/spring/SpringSensitiveWordConfig.java new file mode 100644 index 0000000..85ca8e4 --- /dev/null +++ b/src/test/java/com/github/houbb/sensitive/word/spring/SpringSensitiveWordConfig.java @@ -0,0 +1,40 @@ +package com.github.houbb.sensitive.word.spring; + +import com.github.houbb.sensitive.word.bs.SensitiveWordBs; +import com.github.houbb.sensitive.word.spring.annotation.Autowired; +import com.github.houbb.sensitive.word.spring.annotation.Bean; +import com.github.houbb.sensitive.word.spring.annotation.Configuration; +import com.github.houbb.sensitive.word.spring.database.MyDdWordAllow; +import com.github.houbb.sensitive.word.spring.database.MyDdWordDeny; +import com.github.houbb.sensitive.word.support.allow.WordAllows; + +/** + * @author binbin.hou + * @since 1.0.0 + */ +@Configuration +public class SpringSensitiveWordConfig { + + @Autowired + private MyDdWordAllow myDdWordAllow; + + @Autowired + private MyDdWordDeny myDdWordDeny; + + /** + * 初始化引导类 + * @return 初始化引导类 + * @since 1.0.0 + */ + @Bean + public SensitiveWordBs sensitiveWordBs() { + SensitiveWordBs sensitiveWordBs = SensitiveWordBs.newInstance() + .wordAllow(WordAllows.chains(WordAllows.system(), myDdWordAllow)) + .wordDeny(myDdWordDeny) + // 各种其他配置 + .init(); + + return sensitiveWordBs; + } + +} diff --git a/src/test/java/com/github/houbb/sensitive/word/spring/annotation/Autowired.java b/src/test/java/com/github/houbb/sensitive/word/spring/annotation/Autowired.java new file mode 100644 index 0000000..7c9f840 --- /dev/null +++ b/src/test/java/com/github/houbb/sensitive/word/spring/annotation/Autowired.java @@ -0,0 +1,18 @@ +package com.github.houbb.sensitive.word.spring.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author binbin.hou + * @since 1.0.0 + */ +@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface Autowired { + + boolean required() default true; + +} diff --git a/src/test/java/com/github/houbb/sensitive/word/spring/annotation/Bean.java b/src/test/java/com/github/houbb/sensitive/word/spring/annotation/Bean.java new file mode 100644 index 0000000..ac199d0 --- /dev/null +++ b/src/test/java/com/github/houbb/sensitive/word/spring/annotation/Bean.java @@ -0,0 +1,24 @@ +package com.github.houbb.sensitive.word.spring.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author binbin.hou + * @since 1.0.0 + */ +@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface Bean { + + String[] value() default {}; + + String[] name() default {}; + + String initMethod() default ""; + + String destroyMethod() default ""; + +} diff --git a/src/test/java/com/github/houbb/sensitive/word/spring/annotation/Component.java b/src/test/java/com/github/houbb/sensitive/word/spring/annotation/Component.java new file mode 100644 index 0000000..e857531 --- /dev/null +++ b/src/test/java/com/github/houbb/sensitive/word/spring/annotation/Component.java @@ -0,0 +1,15 @@ +package com.github.houbb.sensitive.word.spring.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author binbin.hou + * @since 1.0.0 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface Component { +} diff --git a/src/test/java/com/github/houbb/sensitive/word/spring/annotation/Configuration.java b/src/test/java/com/github/houbb/sensitive/word/spring/annotation/Configuration.java new file mode 100644 index 0000000..3db1ce9 --- /dev/null +++ b/src/test/java/com/github/houbb/sensitive/word/spring/annotation/Configuration.java @@ -0,0 +1,31 @@ +package com.github.houbb.sensitive.word.spring.annotation; + +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.annotation.*; + + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Component +public @interface Configuration { + + String value() default ""; + +} + diff --git a/src/test/java/com/github/houbb/sensitive/word/spring/database/MyDdWordAllow.java b/src/test/java/com/github/houbb/sensitive/word/spring/database/MyDdWordAllow.java new file mode 100644 index 0000000..473e9c6 --- /dev/null +++ b/src/test/java/com/github/houbb/sensitive/word/spring/database/MyDdWordAllow.java @@ -0,0 +1,22 @@ +package com.github.houbb.sensitive.word.spring.database; + +import com.github.houbb.sensitive.word.api.IWordAllow; +import com.github.houbb.sensitive.word.spring.annotation.Component; + +import java.util.Arrays; +import java.util.List; + +/** + * @author binbin.hou + * @since 1.0.0 + */ +@Component +public class MyDdWordAllow implements IWordAllow { + + @Override + public List allow() { + // 数据库查询 + return Arrays.asList("学习"); + } + +} diff --git a/src/test/java/com/github/houbb/sensitive/word/spring/database/MyDdWordDeny.java b/src/test/java/com/github/houbb/sensitive/word/spring/database/MyDdWordDeny.java new file mode 100644 index 0000000..4163e1b --- /dev/null +++ b/src/test/java/com/github/houbb/sensitive/word/spring/database/MyDdWordDeny.java @@ -0,0 +1,22 @@ +package com.github.houbb.sensitive.word.spring.database; + +import com.github.houbb.sensitive.word.api.IWordDeny; +import com.github.houbb.sensitive.word.spring.annotation.Component; + +import java.util.Arrays; +import java.util.List; + +/** + * @author binbin.hou + * @since 1.0.0 + */ +@Component +public class MyDdWordDeny implements IWordDeny { + + @Override + public List deny() { + // 数据库返回的各种信息 + return Arrays.asList("广告"); + } + +} diff --git a/src/test/java/com/github/houbb/sensitive/word/spring/service/SensitiveWordService.java b/src/test/java/com/github/houbb/sensitive/word/spring/service/SensitiveWordService.java new file mode 100644 index 0000000..f34a675 --- /dev/null +++ b/src/test/java/com/github/houbb/sensitive/word/spring/service/SensitiveWordService.java @@ -0,0 +1,41 @@ +package com.github.houbb.sensitive.word.spring.service; + +import com.github.houbb.sensitive.word.bs.SensitiveWordBs; +import com.github.houbb.sensitive.word.spring.annotation.Autowired; +import com.github.houbb.sensitive.word.spring.annotation.Component; + +/** + * @author binbin.hou + * @since 1.0.0 + */ +@Component +public class SensitiveWordService { + + @Autowired + private SensitiveWordBs sensitiveWordBs; + + /** + * 更新词库 + * + * 每次数据库的信息发生变化之后,首先调用更新数据库敏感词库的方法。 + * 如果需要生效,则调用这个方法。 + * + * 说明:重新初始化不影响旧的方法使用。初始化完成后,会以新的为准。 + */ + public void refresh() { + // 每次数据库的信息发生变化之后,首先调用更新数据库敏感词库的方法,然后调用这个方法。 + sensitiveWordBs.init(); + } + + /** + * 是否包含 + * + * 可以重新封装,也可以直接使用 sensitiveWordBs + * @param word 单词 + * @return 结果 + */ + public boolean contains(String word){ + return sensitiveWordBs.contains(word); + } + +}