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);
+ }
+
+}