# java-forge
**Repository Path**: willchan-tech/java-forge
## Basic Information
- **Project Name**: java-forge
- **Description**: Java 轻量级增强框架
- **Primary Language**: Unknown
- **License**: Apache-2.0
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 0
- **Created**: 2026-05-05
- **Last Updated**: 2026-05-08
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 🚀 Java-Forge
> 一个基于 Spring Boot Starter 的轻量级增强框架,聚焦于提升后端系统的**可扩展性、可维护性与工程效率**
---
## 📌 项目简介
**Java-Forge** 是一个个人开发的模块化框架,基于 Spring Boot Starter 自动装配机制构建,旨在解决中小型项目中常见的工程问题。目前包含 5 大模块:
- **配置动态化**
- **日志安全(脱敏)**
- **接口限流**
- **任务调度**
- **常用设计模式**
项目强调:
- **低侵入性**
- **高扩展性**
- **开箱即用**
---
## 🧱 核心模块
### 1️⃣ 动态配置中心(Dynamic Config Center)
支持在**不重启应用的情况下动态修改配置**
**特性:**
- 支持配置热更新
- 基于 `BeanPostProcessor` 实现自动注入
- 支持自定义配置源扩展
**使用场景:**
- 动态开关(Feature Toggle)
- 灰度发布
- 运行时参数调整
---
### 2️⃣ 日志脱敏(Log Masking)
对敏感信息进行自动脱敏处理,避免日志泄露风险。支持 2 种日志框架, 4 种脱敏类型,可覆盖脱敏规则,也可扩展自定义类型
**日志类型:**
- logback
- log4j2
**脱敏类型:**
- 手机号
- 邮箱
- 身份证
- 护照
**示例:**
```text
手机号:13812345678 → 脱敏后:138****5678
邮箱:zhangsan88@qq.com → 脱敏后:z***@qq.com
身份证:440103199001011234 → 脱敏后: 4401**********1234
护照:E12345678 → 脱敏后: E****5678
```
**特点:**
- 基于正则 + 可扩展策略
- 可覆盖默认策略
- 支持边界污染防护
---
### 3️⃣ 限流模块(Rate Limiter)
用于保护系统接口,防止高并发下的流量冲击或恶意攻击
**支持策略:**
- 全局限流
- IP 限流(黑名单机制)
- 自定义 Key 限流(黑名单机制)
**特点:**
- 使用简单,仅需几个注解和参数设置。
- 插拔式的限流策略,可任意搭配策略一起使用,灵活性强
- 易扩展新的限流维度
---
### 4️⃣ 任务调度系统(Task Scheduler)
轻量级任务调度实现,支持**动态任务管理**
**能力:**
- 动态注册任务
- 动态更新任务 cron 表达式
- 动态删除任务
**设计优势:**
- 全量对齐任务
- 解耦任务类型与执行逻辑
- 支持灵活扩展任务类型
---
### 5️⃣ 设计模式基础框架(Design Patterns)
提供多种设计模式基础框架,包含三种责任链模式,一种规则树模式
**使用场景:**
- 项目流程解耦,扩展
- 增强项目代码架构的弹性
- 协调代码规范管理
---
## 📦 快速开始
### 1. 引入依赖
项目实现了标准 BOM(Bill of Materials)依赖导入,可以先导入 bom 包,统一版本管理
```xml
io.github.willchantech.forge
java-forge-bom
1.0.0
```
然后,按需导入其他功能模块(省略 version)
```xml
io.github.willchantech.forge
java-forge-starter-dynamic-config-center
io.github.willchantech.forge
java-forge-starter-design-framework
io.github.willchantech.forge
java-forge-starter-rate-limiter
io.github.willchantech.forge
java-forge-starter-log-masking
io.github.willchantech.forge
java-forge-starter-task-job
```
---
### 🟥 动态配置中心(Dynamic Config Center)
#### Spring Boot yml 配置文件:
```yaml
java-forge:
dynamic-config:
# 不同的项目,system 应该不同。这是用来区分不同的消息主题上下文,避免冲突。
system: [system name]
redis:
# redis host
host: [your host]
# redis port
port: [your port]
```
然后,在需要被动态配置的类(spring 注册类)字段上面添加注解 @DCCConfig:
```
@DCCValue("toggle:open") // [key]:[value] 格式,key 可以随意填写,只要不冲突即可。
private String toggle;
```
这样的话,当项目启动后,字符串字段 toggle 会被注入 "open" 值。
#### 动态修改值:
在项目不重启的情况下动态修改配置,需要给 redis 发送主题消息:
```java
@Resource
private RTopic dynamicConfigCenterRedisTopic;
@Test
public void dynamicConfig() {
// 推送
dynamicConfigCenterRedisTopic.publish(new AttributeVO("toggle", "off"));
}
```
---
### 🟥 日志脱敏(Log Masking)
#### 首先,在 Spring Boot yml 配置文件中启用 “脱敏” 开关:
```yaml
java-forge:
# 日志脱敏配置,默认所有的属性值是 false(关闭),需要手动开启。
log-mask:
enabled: true # 总开关
phone: true
email: true
idCard: true
passport: true
```
#### 接入 logback 日志框架:
logback 的 xml 配置文件中添加一个 conversionRule 标签:
```xml
```
然后,在 pattern 标签中将日志变量替换成 %mask,例如:
```xml
%d{yy-MM-dd.HH:mm:ss.SSS} [%-16t] %-5p %-22c{0}%X{ServiceId} -%X{trace-id} %mask%n
```
#### 接入 log4j2 日志框架:
log4j2 的 xml 配置文件:
```xml
```
#### 重写脱敏策略:
log-mask 模块提供了 4 种默认脱敏策略,你可以重写其中的策略。分别有 4 个类:AbstractEmailMaskRule, AbstractIdCardMaskRule, AbstractPhoneMaskRule, AbstractPassportMaskRule。例如:
```java
@Component
public class MyEmailMaskRule extends AbstractEmailMaskRule {
/**
* 日志脱敏规则
* @param text 日志内容(不保证是原生日志,有可能是经过其他脱敏策略过滤后的日志)
* @return 脱敏后的日志内容
*/
@Override
public String mask(String text) {
return text.replaceAll(
"([a-zA-Z0-9._%+-]{1})[a-zA-Z0-9._%+-]*(@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,})",
"$1呼呼呼$2"
);
}
}
```
你也可以重写 supports 方法,这样的话,脱敏开关就会由你的代码来控制,而不是来自配置文件。
```java
@Component
public class MyEmailMaskRule extends AbstractEmailMaskRule {
private boolean toggle; // true 表示开启,false 表示关闭
/**
* 日志脱敏规则
* @param text 日志内容(不保证是原生日志,有可能是经过其他脱敏策略过滤后的日志)
* @return 脱敏后的日志内容
*/
@Override
public String mask(String text) {
return text.replaceAll(
"([a-zA-Z0-9._%+-]{1})[a-zA-Z0-9._%+-]*(@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,})",
"$1呼呼呼$2"
);
}
/**
* 脱敏开关是否开启。若不重写 supports 方法,则脱敏开关由配置文件控制。
* @param flags 所有脱敏开关(来自配置文件)的位运算值之和
* @return true 启用,false 禁用
*/
@Override
public boolean supports(long flags) { // flags 是所有脱敏开关(来自配置文件)的位运算值之和
return toggle;
}
}
```
#### 扩展脱敏策略:
如果你想定义一个全新的脱敏策略,你可以实现 MaskRule 接口。例如:
```java
public class MyMaskRule implements MaskRule {
private boolean toggle; // true 表示开启,false 表示关闭
/**
* 日志脱敏规则
* @param text 日志内容(不保证是原生日志,有可能是经过其他脱敏策略过滤后的日志)
* @return 脱敏后的日志内容
*/
@Override
public String mask(String text) {
// 正则表达式
String regex = "";
return regex;
}
/**
* 脱敏开关是否开启。
* 可以不重写 supports 方法,默认是 true(开启)的。
* @param flags 所有脱敏开关(来自配置文件)的位运算值之和
* @return true 启用,false 禁用
*/
@Override
public boolean supports(long flags) {
return toggle;
}
}
```
日志开关配置也可以配合 **动态配置中心(Dynamic Config Center)** 一起使用。这样的话,你就能得到一个可以动态开启关闭的日志脱敏功能。
---
### 🟥 限流模块(Rate Limiter)
Rate Limiter 模块有三大限流策略:global rate limiter(全局限流),IP rate limiter (IP 限流),key rate limiter(自定义 key 限流)。它们的拦截顺序具有优先级别,全局限流最优先, 然后是 IP 限流,最后是自定义 key 限流。你可以只使用其中任意一个或任意数个限流策略组合,但优先级顺序不变。
**被某个限流策略触发了拦截,随即返回,不会继续执行后面的限流策略**。
只有全局限流策略需要 Spring Boot yml 配置:
```yaml
java-forge:
# 限流配置
rate-limiter:
global:
permits-per-second: 2 # 每秒允许的请求数
```
使用上很简单,Rate Limiter 模块提供了 3 个限流策略注解,@GlobalLimiterRule,@KeyLimiterRule,@RequestMapping。例如:
```java
@GlobalLimiterRule
@IPLimiterRule(permitsPerSecond = 2, blacklistCount = 2)
@KeyLimiterRule(key = "userId", permitsPerSecond = 2, blacklistCount = 2)
@RequestMapping(value = "draw", method = RequestMethod.GET)
public String draw(String userId) {
return "test";
}
```
- @GlobalLimiterRule 注解没有参数,统一配置在 Spring Boot yml 文件中,作用于所有的 @GlobalLimiterRule 注解。
- @IPLimiterRule 注解有独立参数。permitsPerSecond 表示每秒允许的请求数,超过阈值即被拦截。客户端 IP 作为黑名单上的记账名,被拦截一次记账 +1。blacklistCount 表示黑名单记账阈值,达到阈值就会进入黑名单,默认 24 小时后解封。
- @KeyLimiterRule 注解有独立参数。key 表示自定义 key,被检测的字段,作为黑名单上的记账名,被拦截一次记账 +1。permitsPerSecond 表示每秒允许的请求数,超过该阈值即被拦截。blacklistCount 表示当被记账(拦截)的次数达到该阈值时,就会进入黑名单,默认 24 小时后解封。
注意,当贴上 @KeyLimiterRule 注解后,被拦截的方法上如果有多个入参且第一个参数为字符串,则只取第一个参数用作匹配 key 的检测。此外,将会迭代所有的入参,直到找到匹配 key 的字段。
#### 被限流拦截后的处理:
当系统触发了限流拦截,将会向上抛出 RateLimiterException 异常对象。该对象提供了三种不同类型的错误码提示:
- type: GLOBAL_LIMIT(超频次访问)
- type: IP_LIMIT, code: 001(超频次访问) / 002(黑名单)
- type: KEY_LIMIT, code: 001(超频次访问) / 002(黑名单)
你可以通过拦截 RateLimiterException 异常来进行一个拦截后处理:
```java
@RestControllerAdvice
public class AppExceptionHandler {
@ExceptionHandler(RateLimiterException.class)
public ResponseEntity> handle(RateLimiterException e) {
// ip 类型
if (e.getType() == RateLimiterRuleType.IP) {
String msg = "";
if (StringUtils.equals(RateLimiterTreatmentType.BLACKLIST_LIMIT.getCode(), e.getCode())) {
msg = "IP访问过于频繁,请稍后再试。";
} else {
msg = "IP访问过于频繁。";
}
return ResponseEntity.status(429)
.body(msg);
}
// key 类型
if (e.getType() == RateLimiterRuleType.KEY) {
String msg = "";
if (StringUtils.equals(RateLimiterTreatmentType.BLACKLIST_LIMIT.getCode(), e.getCode())) {
msg = "用户请求过快,请稍后再试。";
} else {
msg = "用户请求过快。";
}
return ResponseEntity.status(429)
.body(msg);
}
// global 类型
return ResponseEntity.status(429)
.body("系统繁忙,请稍后再试。");
}
}
```
---
### 🟥 任务调度系统(Task Job)
当你有一些需要定时或规律性执行的代码,你就可以使用 Task Job 模块。以一个任务作为最小的执行单元,你可以同时提供多个不同执行时间的任务。Task Job 模块的特别之处在于,采取全量对齐的方式来定时监测所有任务的变动,因此你可以动态的变更,增加或删除任务。
#### Spring Boot yml 配置文件:
```yml
java-forge:
task-job:
enabled: true # 是否开启定时刷新任务
refresh-interval: 30000 # 刷新任务间隔(ms)
pool-size: 5 # 任务线程池大小
thread-name-prefix: "test-task-scheduler-" # 任务线程名称前缀
wait-for-tasks-to-complete-on-shutdown: true # 是否等待任务执行完成再关闭容器
await-termination-seconds: 30 # 容器关闭时,等待任务执行完成,超时(n 秒)则强制中断
```
#### 提供任务
你需要提供一个 TaskDataProvider(提供的数量不限), 该类需要实现 ITaskDataProvider 接口。TaskScheduleVO 类封装了任务信息,包括任务ID,任务描述,任务执行表达式,任务参数,任务逻辑,作为任务的一个单元。
queryAllValidTaskSchedule 方法需要返回一个 TaskScheduleVO 集合,Task Job 模块会定时刷新任务的变动。你可以修改 yml 配置文件里的 enabled 属性,启动或关闭定时检测。或者修改 refresh-interval 属性来改变任务刷新的间隔。
不同的任务的 ID 必须唯一。全量对齐任务是 Task Job 模块的最大特色。删除一个任务,意味着你需要把该任务从 List 中剔除;添加一个任务,直接往 List 中添加即可。修改一个任务,你需要把该任务从 List 中剔除,然后添加一个新的任务(具有新的 ID),这样才能被 Task Job 模块识别到任务列表的变更。也就是说,你无法去修改一个任务中的描述,任务参数,任务逻辑,因为修改这些不会被识别,但有一种情况例外,就是修改任务的执行表达式。
```java
@Service
public class TestTaskDataProvider01 implements ITaskDataProvider { // 你可以创建多个任务数据提供者
@Override
public List queryAllValidTaskSchedule() {
List tasks = new ArrayList<>();
// 任务1
TaskScheduleVO task1 = new TaskScheduleVO();
task1.setId(1L);
task1.setDescription("测试任务1 - 数据处理");
task1.setCronExpression("0/10 * * * * ?"); // 每10秒执行一次
task1.setTaskParam("{\"type\":\"data_process\",\"batch_size\":100}");
// 使用BiConsumer方式设置任务逻辑
BiConsumer task1Logic = (taskId, taskParam) -> {
try {
Thread.sleep(500); // 模拟任务执行时间
log.info("测试任务1执行完成");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.error("测试任务1执行被中断", e);
}
};
task1.buildTaskLogic(task1Logic);
tasks.add(task1);
// 任务2
TaskScheduleVO task2 = new TaskScheduleVO();
task2.setId(2L);
task2.setDescription("测试任务2 - 报表生成");
task2.setCronExpression("0/5 * * * * ?"); // 每5秒钟执行一次
task2.setTaskParam("{\"report_type\":\"daily\",\"format\":\"pdf\"}");
// 使用BiConsumer方式设置任务逻辑
BiConsumer task2Logic = (taskId, taskParam) -> {
try {
Thread.sleep(1000); // 模拟耗时操作
log.info("测试任务2执行完成");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.error("测试任务2执行被中断", e);
}
};
task2.buildTaskLogic(task2Logic);
tasks.add(task2);
return tasks;
}
}
```
#### 任务管理
TaskJobManger 类封装了任务管理功能。你可以通过 TaskJobManger 类来添加、移除、刷新、停止任务调度配置,并获取当前活跃任务数量。
```java
public interface ITaskJobManger {
/**
* 添加单个任务
* @param task 任务调度配置
* @return 是否添加成功
*/
boolean addTask(TaskScheduleVO task);
/**
* 移除单个任务
* @param taskId 任务ID
* @param force 是否强制移除
* @return 是否移除成功
*/
boolean removeTask(Long taskId, boolean force);
/**
* 刷新任务调度配置:
* 全量对齐 TaskDataProvider 提供的任务
*/
void refreshTasks();
/**
* 停止所有任务
*/
void stopAllTasks();
/**
* 获取当前活跃任务数量
* @return 活跃任务数量
*/
int getActiveTaskCount();
}
```
引入 TaskJobManger
```java
@Resource
private ITaskJobManager taskJobManager;
```