This commit is contained in:
kk
2026-07-07 18:01:21 +08:00
commit b259f94d4b
1088 changed files with 121778 additions and 0 deletions
+350
View File
@@ -0,0 +1,350 @@
# Blade LiteRule 规则引擎
BladeX框架的轻量级规则引擎,提供简单易用的规则执行和编排能力。
## 特性
- 支持顺序规则、分支规则和并行规则三种执行模式
- 提供流式API用于构建规则链
- 支持同步和异步执行
- 完整的生命周期管理和监控
- 基于Spring管理规则实例
- 线程安全的上下文管理
- 规则链缓存和预加载
- 循环依赖检测
## 快速开始
### 1. 添加依赖
```xml
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-starter-literule</artifactId>
<version>${blade.tool.version}</version>
</dependency>
```
### 2. 定义规则上下文
```java
import org.springblade.core.literule.core.RuleContextComponent;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class OrderContext extends RuleContextComponent {
private String orderId;
private BigDecimal amount;
private String paymentType;
private String status;
private Address shippingAddress;
// 其他业务字段...
}
```
### 3. 实现普通规则
```java
import org.springblade.core.literule.annotation.LiteRuleComponent;
@Slf4j
@LiteRuleComponent("orderValidateRule")
public class OrderValidateRule extends RuleComponent {
@Override
protected void process() throws Exception {
OrderContext context = getContextBean(OrderContext.class);
// 执行业务逻辑
if (StringUtils.isEmpty(context.getOrderId())) {
context.addError("订单ID不能为空");
return;
}
if (context.getAmount() == null || context.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
context.addError("订单金额无效");
return;
}
log.info("[规则1] 订单校验通过:{}", context.getOrderId());
}
}
```
### 4. 实现分支规则
```java
import org.springblade.core.literule.annotation.LiteRuleComponent;
@Slf4j
@LiteRuleComponent("paymentRouteRule")
public class PaymentRouteRule extends SwitchRuleComponent {
@Override
protected List<String> process() throws Exception {
OrderContext context = getContextBean(OrderContext.class);
String paymentType = context.getPaymentType();
log.info("[规则3] 支付路由选择:{}", paymentType);
// 根据支付类型返回不同的规则ID
if ("ALIPAY".equals(paymentType)) {
return Collections.singletonList("alipayRule");
} else if ("WECHAT".equals(paymentType)) {
return Collections.singletonList("wechatPayRule");
} else if ("BANK".equals(paymentType)) {
return Collections.singletonList("bankPayRule");
} else {
context.addError("不支持的支付方式:" + paymentType);
return Collections.emptyList();
}
}
}
```
### 5. 构建规则链
```java
@EngineComponent("orderChain")
public class OrderRuleBuilder implements RuleBuilder {
@Override
public RuleChain build() {
// 创建支付处理分支规则
RuleChain paymentRule = LiteRule.SWITCH("paymentRouteRule")
.TO("alipayRule", "wechatPayRule", "bankPayRule")
.build();
// 创建完整规则链
return LiteRule.THEN("orderValidateRule", "orderAmountRule")
.THEN(paymentRule)
.build();
}
}
```
### 6. 执行规则链
```java
import org.springblade.core.literule.provider.LiteRuleResponse;
@Autowired
private RuleEngineExecutor ruleEngine;
public void processOrder(OrderRequest request) {
// 构建上下文
OrderContext context = OrderContext.builder()
.orderId(request.getOrderId())
.amount(request.getAmount())
.paymentType(request.getPaymentType())
.shippingAddress(request.getAddress())
.build();
// 构建配置
RuleConfig config = RuleConfig.builder()
.enableTimeMonitor(true)
.printExecutionTime(true)
.enableLogging(true)
.build();
// 执行规则链
LiteRuleResponse<OrderContext> response = ruleEngine.execute(
"orderChain",
context,
config
);
// 处理响应
if (response.isSuccess()) {
log.info("规则执行成功,耗时: {}ms", response.getExecutionTime());
} else {
log.error("规则执行失败: {}", response.getMessage());
// 获取详细错误信息
context.getErrorMessages().forEach(error -> log.error("错误: {}", error));
}
}
```
### 7. 异步执行规则链
```java
@Autowired
private RuleEngineExecutor ruleEngine;
@Autowired
@Qualifier("ruleExecutorThreadPool")
private ThreadPoolExecutor threadPool;
public CompletableFuture<LiteRuleResponse<OrderContext>> processOrderAsync(OrderRequest request) {
// 构建上下文
OrderContext context = OrderContext.builder()
.orderId(request.getOrderId())
.amount(request.getAmount())
.paymentType(request.getPaymentType())
.build();
// 异步执行规则链
return ruleEngine.executeAsync(
"orderChain",
context,
threadPool
).thenApply(response -> {
if (response.isSuccess()) {
log.info("异步规则执行成功,耗时: {}ms", response.getExecutionTime());
} else {
log.error("异步规则执行失败: {}", response.getMessage());
}
return response;
});
}
```
## 核心概念
### 规则(Rule)
规则是业务逻辑的最小执行单元,分为两种类型:
- 普通规则(Rule): 执行固定的业务逻辑
- 分支规则(SwitchRule): 根据条件返回下一个要执行的规则列表
### 规则链(RuleChain)
规则链定义了规则的执行顺序,支持三种组合方式:
- THEN: 顺序执行一组规则
- SWITCH: 条件分支执行不同规则
- PARALLEL: 并行执行一组规则
### 规则上下文(RuleContext)
在规则执行过程中传递数据和状态的载体,包含:
- 上下文数据
- 错误信息
- 执行状态
- 执行时间统计
### 规则配置(RuleConfig)
控制规则执行的行为,包含:
- 是否启用执行时间监控
- 是否打印执行时间
- 是否启用日志
## 高级特性
### 1. 规则链缓存
规则引擎会自动缓存构建好的规则链,提高执行效率。缓存支持以下配置:
```yaml
literule:
cache:
enabled: true # 是否启用缓存
```
### 2. 规则预加载
在应用启动时预加载所有规则,提前发现配置错误,减少首次执行的延迟:
```yaml
literule:
preload:
enabled: true # 是否启用预加载
```
### 3. 并行执行配置
控制并行规则执行的行为:
```yaml
literule:
execution:
timeout: 30000 # 默认超时时间(毫秒)
enable-parallel: true # 是否启用并行执行
max-parallel-threads: 10 # 最大并行线程数
```
### 4. 循环依赖检测
规则引擎会自动检测规则链中的循环依赖,避免无限递归执行。
## 项目结构
```
blade-starter-literule/
├── annotation/ # 注解
│ ├── EngineComponent.java # 引擎组件注解
│ └── RuleComponent.java # 规则组件注解
├── builder/ # 规则构建
│ ├── LiteRule.java # 规则构建器
│ ├── RuleBuilder.java # 规则构建器接口
│ ├── RuleBuilderExecutor.java # 规则构建器执行器
│ └── chain/ # 规则链构建器
│ ├── AbstractRuleChain.java # 规则链抽象基类
│ ├── ParallelRuleChain.java # 并行规则链构建器
│ ├── RuleChain.java # 并行规则链接口
│ ├── SwitchRuleChain.java # 分支规则链构建器
│ └── ThenRuleChain.java # 顺序规则链构建器
├── config/ # 配置类
│ ├── AbstractComponentRegistrar.java # 组件注册器抽象基类
│ ├── EngineComponentRegistrar.java # 引擎组件注册器
│ ├── RuleComponentRegistrar.java # 规则组件注册器
│ ├── RuleEngineAutoConfiguration.java # 规则引擎自动配置
│ └── RuleEngineProperties.java # 规则引擎配置属性
├── context/ # 上下文管理
│ ├── RuleContextHolder.java # 规则上下文持有者
│ └── RuleContextManager.java # 规则上下文管理器
├── core/ # 核心实现
│ ├── AbstractBaseRule.java # 规则基类
│ ├── AbstractRule.java # 普通规则抽象实现
│ ├── AbstractRuleContext.java # 规则上下文抽象实现
│ └── AbstractSwitchRule.java # 分支规则抽象实现
├── engine/ # 规则引擎执行
│ ├── DefaultRuleEngineExecutor.java # 默认规则引擎执行器
│ ├── RuleEngineExecutor.java # 规则引擎执行器接口
│ └── RulePreloadRunner.java # 规则预加载服务
├── exception/ # 异常
│ └── RuleException.java # 规则异常
└── provider/ # 接口和模型
├── Rule.java # 规则接口
├── RuleConfig.java # 规则配置类
├── RuleContext.java # 规则上下文接口
├── RuleResponse.java # 规则响应类
└── SwitchRule.java # 分支规则接口
```
## 最佳实践
### 1. 规则设计原则
- **单一职责**:一个规则只做一件事,便于维护和测试
- **无状态**:规则不应保存状态,所有状态应通过上下文传递
- **幂等性**:多次执行结果一致,避免副作用
- **异常处理**:规则内部应处理异常,避免中断规则链执行
### 2. 规则链设计原则
- **层次清晰**:明确的执行顺序,避免复杂的嵌套
- **解耦合**:规则之间通过上下文交互,避免直接依赖
- **可维护**:便于添加、删除、修改规则,不影响整体流程
- **合理分组**:相关规则放在一起,便于理解和维护
### 3. 性能优化
- **合理使用缓存**:对于频繁执行的规则链,启用缓存
- **并行执行**:对于独立的规则,使用并行执行提高效率
- **异步执行**:对于耗时长的规则链,使用异步执行
- **资源管理**:及时清理上下文资源,避免内存泄漏
## 完整示例
参考 `org.springblade.test.literule.LiteRuleTest` 类,提供了完整的使用示例。
+35
View File
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>BladeX-Tool</artifactId>
<groupId>org.springblade</groupId>
<version>${revision}</version>
</parent>
<artifactId>blade-starter-literule</artifactId>
<name>${project.artifactId}</name>
<version>${project.parent.version}</version>
<packaging>jar</packaging>
<properties>
<module.name>org.springblade.blade.starter.literule</module.name>
</properties>
<dependencies>
<!-- Blade Core Tool -->
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-tool</artifactId>
</dependency>
<!-- Auto -->
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-auto</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,60 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.literule.annotation;
import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Component;
import java.lang.annotation.*;
/**
* 规则组件注解
* 用于标记规则组件,并指定规则ID
*
* @author BladeX
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface LiteRuleComponent {
/**
* 规则ID
*/
@AliasFor("id")
String value() default "";
/**
* 规则ID(与value属性等价)
*/
@AliasFor("value")
String id() default "";
/**
* 规则描述
*/
String name() default "";
}
@@ -0,0 +1,60 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.literule.annotation;
import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Component;
import java.lang.annotation.*;
/**
* 规则链构建器注解
* 用于标记规则链构建器,并指定规则链ID
*
* @author BladeX
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface RuleEngineComponent {
/**
* 规则链ID
*/
@AliasFor("id")
String value() default "";
/**
* 规则链ID(与value属性等价)
*/
@AliasFor("value")
String id() default "";
/**
* 规则链描述
*/
String name() default "";
}
@@ -0,0 +1,69 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.literule.builder;
import org.springblade.core.literule.builder.chain.WhenRuleChain;
import org.springblade.core.literule.builder.chain.SwitchRuleChain;
import org.springblade.core.literule.builder.chain.ThenRuleChain;
/**
* 规则构建器
* 提供流式API用于构建规则链
*
* @author BladeX
*/
public class LiteRule {
/**
* 创建顺序规则链
*
* @param ruleIds 规则ID数组
* @return 顺序规则链构建器
*/
public static ThenRuleChain THEN(String... ruleIds) {
return new ThenRuleChain().THEN(ruleIds);
}
/**
* 创建分支规则链
*
* @param conditionRuleId 条件规则ID
* @return 分支规则链构建器
*/
public static SwitchRuleChain SWITCH(String conditionRuleId) {
return new SwitchRuleChain(conditionRuleId);
}
/**
* 创建并行规则链
*
* @param ruleIds 规则ID数组
* @return 并行规则链构建器
*/
public static WhenRuleChain WHEN(String... ruleIds) {
return new WhenRuleChain().WHEN(ruleIds);
}
}
@@ -0,0 +1,43 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.literule.builder;
import org.springblade.core.literule.builder.chain.RuleChain;
/**
* 规则构建器接口
* 用于构建规则流程
*
* @author BladeX
*/
public interface RuleBuilder {
/**
* 构建规则流程
*
* @return 构建好的规则流程
*/
RuleChain build();
}
@@ -0,0 +1,101 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.literule.builder;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 规则构建器辅助类
* 提供执行栈管理等公共功能
*
* @author BladeX
*/
public class RuleBuilderExecutor {
/**
* 使用线程本地执行栈,初始化为线程安全的空集合
* LinkedHashSet能保持插入顺序,在循环依赖检测时更有用
*/
private static final ThreadLocal<Set<String>> EXECUTION_STACK = ThreadLocal.withInitial(() ->
Collections.synchronizedSet(new LinkedHashSet<>())
);
/**
* 记录规则ID的索引,用于优化循环依赖检测
*/
private static final Map<String, Integer> RULE_ID_INDICES = new ConcurrentHashMap<>();
private static final AtomicInteger NEXT_INDEX = new AtomicInteger(0);
/**
* 获取或创建规则ID对应的索引
*
* @param ruleId 规则ID
* @return 规则ID索引
*/
public static int getRuleIdIndex(String ruleId) {
return RULE_ID_INDICES.computeIfAbsent(ruleId, id -> NEXT_INDEX.getAndIncrement());
}
/**
* 获取执行栈
*
* @return 执行栈
*/
public static Set<String> getExecutionStack() {
return EXECUTION_STACK.get();
}
/**
* 清理执行栈
* 在规则执行完成后调用,避免ThreadLocal资源泄漏
*/
public static void clearExecutionStack() {
EXECUTION_STACK.remove();
}
/**
* 检查执行栈中是否包含指定的规则ID
* 使用线程安全方式检查
*
* @param ruleId 规则ID
* @return 是否存在循环依赖
*/
public static boolean hasCircularDependency(String ruleId) {
Set<String> stack = EXECUTION_STACK.get();
synchronized (stack) {
return stack.contains(ruleId);
}
}
private RuleBuilderExecutor() {
// 私有构造函数,防止实例化
}
}
@@ -0,0 +1,209 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.literule.builder.chain;
import lombok.Getter;
import org.springblade.core.literule.builder.RuleBuilderExecutor;
import org.springblade.core.literule.exception.RuleException;
import org.springblade.core.literule.provider.Rule;
import org.springblade.core.tool.utils.SpringUtil;
import org.springblade.core.tool.utils.StringUtil;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* 规则链抽象基类
* 封装规则链的共同特性和行为
*
* @author BladeX
*/
@Getter
public abstract class AbstractRuleChain {
/**
* 规则链ID
*/
protected String id;
/**
* 缓存已经检查过的路径,避免重复检测
*/
private static final Map<String, Boolean> CIRCULAR_DEPENDENCY_CACHE = new ConcurrentHashMap<>();
/**
* 构建规则链
*
* @return 规则实例
*/
public abstract RuleChain build();
/**
* 从Spring容器中获取规则实例
*
* @param ruleId 规则ID
* @return 规则实例
*/
protected Rule getRuleFromSpring(String ruleId) {
Rule rule = SpringUtil.getBean(ruleId, Rule.class);
if (rule == null) {
throw new RuleException("Rule not found: " + ruleId);
}
return rule;
}
/**
* 预加载规则实例
*
* @param ruleIds 规则ID集合
* @return 规则实例映射
*/
protected Map<String, Rule> preloadRules(Iterable<String> ruleIds) {
Map<String, Rule> ruleInstances = new HashMap<>();
for (String ruleId : ruleIds) {
Rule rule = SpringUtil.getBean(ruleId, Rule.class);
if (rule != null) {
ruleInstances.put(ruleId, rule);
}
}
return ruleInstances;
}
/**
* 检测循环依赖
* 使用RuleBuilderExecutor中的方法,确保线程安全
*
* @param ruleId 规则ID
* @throws RuleException 如果检测到循环依赖
*/
protected void checkCircularDependency(String ruleId) {
if (RuleBuilderExecutor.hasCircularDependency(ruleId)) {
// 构建依赖路径,帮助调试
String path = String.join("->", RuleBuilderExecutor.getExecutionStack()) + "->" + ruleId;
throw new RuleException("Circular dependency detected: " + path);
}
}
/**
* 检测全局循环依赖
* 用于跨线程检测
*
* @param ruleId 规则ID
* @param globalStack 全局执行栈
* @throws RuleException 如果检测到循环依赖
*/
protected void checkGlobalCircularDependency(String ruleId, Set<String> globalStack) {
if (globalStack.contains(ruleId)) {
throw new RuleException("Global circular dependency detected: " + ruleId);
}
// 构建路径签名
String pathSignature = String.join("->", globalStack) + "->" + ruleId;
// 检查缓存
if (CIRCULAR_DEPENDENCY_CACHE.containsKey(pathSignature)) {
// 这个路径已经检测过,不会有循环依赖
return;
}
// 将结果放入缓存
CIRCULAR_DEPENDENCY_CACHE.put(pathSignature, Boolean.TRUE);
}
/**
* 执行规则,并处理执行栈
* 使用同步块确保线程安全
*
* @param ruleId 规则ID
* @param rule 规则实例
* @throws Exception 执行过程中的异常
*/
protected void executeWithStackManagement(String ruleId, Rule rule) throws Exception {
checkCircularDependency(ruleId);
// 获取当前线程的执行栈
Set<String> executionStack = RuleBuilderExecutor.getExecutionStack();
// 加锁确保线程安全
synchronized (executionStack) {
try {
// 添加到执行栈
executionStack.add(ruleId);
// 执行规则
rule.execute();
} finally {
// 从执行栈中移除
executionStack.remove(ruleId);
}
}
}
/**
* 获取规则实例,优先从缓存中获取
*
* @param ruleId 规则ID
* @param ruleInstances 规则实例缓存
* @return 规则实例
*/
protected Rule getRuleInstance(String ruleId, Map<String, Rule> ruleInstances) {
Rule rule = ruleInstances.get(ruleId);
if (rule == null) {
rule = getRuleFromSpring(ruleId);
}
return rule;
}
/**
* 获取规则列表实例
*
* @param rules 规则列表
* @return 规则实例映射
*/
protected Map<String, Rule> getListRuleInstances(List<Object> rules) {
// 预先获取所有规则实例
Map<String, Rule> branchRuleInstances = new HashMap<>();
// 处理不同类型的规则
for (Object rule : rules) {
if (rule instanceof String ruleId) {
// 如果是规则ID,添加到预加载列表
Rule ruleBean = SpringUtil.getBean(ruleId, Rule.class);
if (ruleBean != null) {
branchRuleInstances.put(ruleId, ruleBean);
}
} else if (rule instanceof RuleChain ruleChain) {
// 如果是规则链,使用适配器后添加到规则链列表
String id = StringUtil.isNotBlank(ruleChain.id()) ? ruleChain.id() : ruleChain.getClass().getSimpleName();
branchRuleInstances.put(id, new RuleChainAdapter(ruleChain));
}
}
return branchRuleInstances;
}
}
@@ -0,0 +1,50 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.literule.builder.chain;
/**
* 规则链接口
* 所有规则链都应实现此接口
*
* @author BladeX
*/
public interface RuleChain {
/**
* 获取规则链ID
*
* @return 规则链ID
*/
default String id() {
return null;
}
/**
* 执行规则链
*
* @throws Exception 执行过程中可能抛出的异常
*/
void execute() throws Exception;
}
@@ -0,0 +1,54 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.literule.builder.chain;
import lombok.RequiredArgsConstructor;
import org.springblade.core.literule.provider.Rule;
/**
* 规则链适配器
* 将规则链包装为Rule接口的实现
*
* @author BladeX
*/
@RequiredArgsConstructor
public class RuleChainAdapter implements Rule {
/**
* 规则链
*/
private final RuleChain ruleChain;
/**
* 执行规则链
*
* @throws Exception 执行过程中可能抛出的异常
*/
@Override
public void execute() throws Exception {
ruleChain.execute();
}
}
@@ -0,0 +1,137 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.literule.builder.chain;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springblade.core.literule.builder.RuleBuilderExecutor;
import org.springblade.core.literule.exception.RuleException;
import org.springblade.core.literule.provider.Rule;
import org.springblade.core.literule.provider.SwitchRule;
import org.springblade.core.tool.utils.SpringUtil;
import java.util.*;
/**
* 分支规则链构建器
* 用于构建条件分支的规则链
*
* @author BladeX
*/
@Getter
public class SwitchRuleChain extends AbstractRuleChain {
private final String conditionRuleId;
private final List<Object> branchRules = new ArrayList<>();
/**
* 构造函数
*
* @param conditionRuleId 条件规则ID
*/
public SwitchRuleChain(String conditionRuleId) {
this.conditionRuleId = conditionRuleId;
}
/**
* 设置规则链ID
* @param id 规则链ID
* @return 构建器实例
*/
public SwitchRuleChain ID(String id) {
this.id = id;
return this;
}
/**
* 添加分支规则ID
*
* @param ruleIds 规则ID数组
* @return 构建器实例
*/
public SwitchRuleChain TO(Object... ruleIds) {
if (ruleIds != null) {
Collections.addAll(this.branchRules, ruleIds);
}
return this;
}
/**
* 构建规则链
*
* @return 规则实例
*/
@Override
public RuleChain build() {
// 预先获取条件规则实例
final SwitchRule conditionRule = SpringUtil.getBean(conditionRuleId, SwitchRule.class);
if (conditionRule == null) {
throw new RuleException("Condition rule not found: " + conditionRuleId);
}
// 预先获取所有分支规则实例
Map<String, Rule> branchRuleInstances = getListRuleInstances(branchRules);
return new SwitchRuleExecutor(conditionRuleId, conditionRule, branchRuleInstances);
}
/**
* 分支规则执行器内部类
*/
@RequiredArgsConstructor
public class SwitchRuleExecutor implements RuleChain {
private final String conditionRuleId;
private final SwitchRule conditionRule;
private final Map<String, Rule> branchRuleInstances;
@Override
public String id() {
return getId() == null ? conditionRuleId : getId();
}
@Override
public void execute() throws Exception {
try {
// 添加到执行栈
RuleBuilderExecutor.getExecutionStack().add(conditionRuleId);
// 执行条件规则
List<String> nextRules = conditionRule.execute();
// 执行分支规则
for (String ruleId : nextRules) {
// 使用基类方法获取规则实例并执行
Rule rule = getRuleInstance(ruleId, branchRuleInstances);
if (rule != null) {
executeWithStackManagement(ruleId, rule);
}
}
} finally {
// 从执行栈中移除
RuleBuilderExecutor.getExecutionStack().remove(conditionRuleId);
}
}
}
}
@@ -0,0 +1,120 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.literule.builder.chain;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springblade.core.literule.provider.Rule;
import org.springblade.core.tool.utils.StringUtil;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* 顺序规则链构建器
* 用于构建顺序执行的规则链
*
* @author BladeX
*/
@Getter
public class ThenRuleChain extends AbstractRuleChain {
private final List<Object> rules = new ArrayList<>();
/**
* 设置规则链ID
*
* @param id 规则链ID
* @return 构建器实例
*/
public ThenRuleChain ID(String id) {
this.id = id;
return this;
}
/**
* 添加规则
*
* @param ruleIds 规则ID数组
* @return 构建器实例
*/
public ThenRuleChain THEN(String... ruleIds) {
this.rules.addAll(Arrays.asList(ruleIds));
return this;
}
/**
* 添加规则链
*
* @param rule 规则实例
* @return 构建器实例
*/
public ThenRuleChain THEN(RuleChain rule) {
this.rules.add(rule);
return this;
}
/**
* 构建规则链
*
* @return 规则实例
*/
@Override
public RuleChain build() {
// 预先获取所有规则实例
Map<String, Rule> ruleInstances = getListRuleInstances(rules);
return new ThenRuleExecutor(ruleInstances);
}
/**
* 顺序规则执行器内部类
*/
@RequiredArgsConstructor
private class ThenRuleExecutor implements RuleChain {
private final Map<String, Rule> ruleInstances;
@Override
public String id() {
return getId();
}
@Override
public void execute() throws Exception {
// 依次执行所有规则
for (Object rule : rules) {
if (rule instanceof String ruleId) {
Rule ruleBean = getRuleInstance(ruleId, ruleInstances);
executeWithStackManagement(ruleId, ruleBean);
} else if (rule instanceof RuleChain ruleChain) {
String id = StringUtil.isNotBlank(ruleChain.id()) ? ruleChain.id() : ruleChain.getClass().getSimpleName();
Rule ruleBean = getRuleInstance(id, ruleInstances);
executeWithStackManagement(id, ruleBean);
}
}
}
}
}
@@ -0,0 +1,246 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.literule.builder.chain;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springblade.core.literule.builder.RuleBuilderExecutor;
import org.springblade.core.literule.context.RuleContextPropagator;
import org.springblade.core.literule.context.RuleContextManager;
import org.springblade.core.literule.exception.RuleException;
import org.springblade.core.literule.provider.Rule;
import org.springblade.core.literule.provider.RuleConfig;
import org.springblade.core.literule.provider.RuleContext;
import java.util.*;
import java.util.concurrent.*;
/**
* 并行规则链构建器
* 用于构建并行执行的规则链
*
* @author BladeX
*/
@Getter
public class WhenRuleChain extends AbstractRuleChain {
private final List<String> ruleIds = new ArrayList<>();
private Executor executor;
private int timeout = 30000; // 默认超时时间30秒
/**
* 设置规则链ID
* @param id 规则链ID
* @return 构建器实例
*/
public WhenRuleChain ID(String id) {
this.id = id;
return this;
}
/**
* 添加规则
*
* @param ruleIds 规则ID数组
* @return 构建器实例
*/
public WhenRuleChain WHEN(String... ruleIds) {
this.ruleIds.addAll(Arrays.asList(ruleIds));
return this;
}
/**
* 设置执行器
*
* @param executor 执行器
* @return 构建器实例
*/
public WhenRuleChain EXECUTOR(Executor executor) {
this.executor = executor;
return this;
}
/**
* 设置超时时间
*
* @param timeout 超时时间(毫秒)
* @return 构建器实例
*/
public WhenRuleChain timeout(int timeout) {
this.timeout = timeout;
return this;
}
/**
* 构建规则链
*
* @return 规则实例
*/
@Override
public RuleChain build() {
// 预先获取所有规则实例
final Map<String, Rule> ruleInstances = preloadRules(ruleIds);
return new ParallelRuleExecutor(ruleInstances);
}
/**
* 并行规则执行器内部类
*/
@RequiredArgsConstructor
private class ParallelRuleExecutor implements RuleChain {
private final Map<String, Rule> ruleInstances;
@Override
public String id() {
return getId();
}
@Override
public void execute() {
// 获取执行器
Executor exec = executor != null ? executor : ForkJoinPool.commonPool();
// 获取当前上下文和配置,用于传递给子线程
final RuleContext parentContext = RuleContextManager.getContext();
final RuleConfig parentConfig = RuleContextManager.getConfig();
// 获取超时时间,优先使用本地配置,否则使用全局配置
long actualTimeout = timeout > 0 ? timeout :
parentConfig != null ? parentConfig.getTimeout() : 30000;
// 并行执行所有规则
List<CompletableFuture<Void>> futures = new ArrayList<>();
List<Throwable> exceptions = Collections.synchronizedList(new ArrayList<>());
// 创建线程安全的全局执行栈,用于跨线程检测循环依赖
final Set<String> globalStack = ConcurrentHashMap.newKeySet();
// 将当前线程的执行栈复制到全局执行栈
globalStack.addAll(RuleBuilderExecutor.getExecutionStack());
for (String ruleId : ruleIds) {
// 检测全局循环依赖
checkGlobalCircularDependency(ruleId, globalStack);
// 获取规则实例
final Rule finalRule = getRuleInstance(ruleId, ruleInstances);
final String finalRuleId = ruleId;
// 添加到全局执行栈
globalStack.add(finalRuleId);
// 使用ContextPropagator包装任务,确保上下文正确传递
Runnable ruleTask = RuleContextPropagator.wrapRunnable(() -> {
try {
// 添加到线程本地执行栈
RuleBuilderExecutor.getExecutionStack().add(finalRuleId);
// 执行规则
finalRule.execute();
} catch (Throwable e) {
// 收集所有异常,包括Error
exceptions.add(e);
if (e instanceof Error) {
throw (Error) e;
} else {
throw new CompletionException(e);
}
} finally {
// 从线程本地执行栈中移除
RuleBuilderExecutor.getExecutionStack().remove(finalRuleId);
}
}, parentContext, parentConfig);
// 提交任务并配置超时
CompletableFuture<Void> future = CompletableFuture
.runAsync(ruleTask, exec)
.orTimeout(actualTimeout, TimeUnit.MILLISECONDS)
.exceptionally(ex -> {
// 处理超时和其他异常
Throwable cause = ex;
if (ex instanceof CompletionException && ex.getCause() != null) {
cause = ex.getCause();
}
// 添加额外的上下文信息
if (cause instanceof TimeoutException) {
String msg = String.format("Rule '%s' timed out after %d ms", finalRuleId, actualTimeout);
exceptions.add(new RuleException(msg, cause));
} else if (!exceptions.contains(cause)) {
// 只添加还未被添加的异常
exceptions.add(cause);
}
return null;
});
futures.add(future);
}
// 等待所有规则执行完成
try {
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
} catch (CompletionException e) {
// 如果有多个异常,抛出一个包含所有异常的复合异常
if (exceptions.size() > 1) {
RuleException compositeException = new RuleException(
String.format("%d rules failed in parallel execution", exceptions.size()));
for (Throwable exception : exceptions) {
compositeException.addSuppressed(exception instanceof Exception ?
exception : new RuleException(exception.getMessage(), exception));
}
throw compositeException;
} else if (exceptions.size() == 1) {
// 如果只有一个异常,直接抛出
Throwable ex = exceptions.get(0);
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
} else if (ex instanceof Error) {
throw (Error) ex;
} else if (ex instanceof Exception) {
throw new RuleException("Rule execution failed", (Exception) ex);
} else {
throw new RuleException("Unknown error: " + ex.getMessage(), ex);
}
}
throw e;
} finally {
// 清空全局执行栈,避免内存泄漏
globalStack.clear();
}
}
}
/**
* 检测全局循环依赖
*
* @param ruleId 规则ID
* @param globalStack 全局执行栈
* @throws RuleException 如果检测到循环依赖
*/
public void checkGlobalCircularDependency(String ruleId, Set<String> globalStack) {
if (globalStack.contains(ruleId)) {
throw new RuleException("Global circular dependency detected: " + ruleId);
}
}
}
@@ -0,0 +1,180 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.literule.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.lang.NonNull;
import org.springframework.util.ClassUtils;
import java.lang.annotation.Annotation;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* 组件注册器抽象基类
* 提供通用的组件扫描和注册功能
*
* @author BladeX
*/
@Slf4j
public abstract class AbstractComponentRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(@NonNull AnnotationMetadata importingClassMetadata, @NonNull BeanDefinitionRegistry registry) {
// 获取扫描包路径
Set<String> basePackages = getBasePackages(importingClassMetadata);
// 创建扫描器
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
scanner.addIncludeFilter(getTypeFilter());
// 扫描所有符合条件的类
for (String basePackage : basePackages) {
for (BeanDefinition beanDefinition : scanner.findCandidateComponents(basePackage)) {
try {
// 获取类
Class<?> clazz = Class.forName(beanDefinition.getBeanClassName());
// 检查类是否符合要求
if (isValidComponent(clazz)) {
// 获取Bean名称
String beanName = getBeanName(clazz);
// 检查Bean名称冲突
if (registry.containsBeanDefinition(beanName)) {
handleBeanNameConflict(registry, beanDefinition, clazz, beanName);
} else {
// 注册bean
registry.registerBeanDefinition(beanName, beanDefinition);
log.info("Registered {}: {} with name: {}",
getComponentType(), clazz.getName(), beanName);
}
} else {
log.warn("Class {} is annotated with {} but does not implement required interface",
clazz.getName(), getAnnotationType().getSimpleName());
}
} catch (ClassNotFoundException e) {
log.error("Failed to load class: {}", beanDefinition.getBeanClassName(), e);
}
}
}
}
/**
* 获取扫描包路径
*
* @param importingClassMetadata 导入类的元数据
* @return 扫描包路径集合
*/
protected Set<String> getBasePackages(AnnotationMetadata importingClassMetadata) {
// 获取@ComponentScan注解的属性
Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(
"org.springframework.context.annotation.ComponentScan");
Set<String> basePackages = new HashSet<>();
// 如果有@ComponentScan注解,获取其basePackages属性
if (annotationAttributes != null) {
String[] basePackagesArray = (String[]) annotationAttributes.get("basePackages");
if (basePackagesArray != null) {
Collections.addAll(basePackages, basePackagesArray);
}
}
// 如果没有指定basePackages,使用导入类所在的包
if (basePackages.isEmpty()) {
basePackages.add(ClassUtils.getPackageName(importingClassMetadata.getClassName()));
}
return basePackages;
}
/**
* 处理Bean名称冲突
*
* @param registry Bean定义注册器
* @param beanDefinition Bean定义
* @param clazz 类
* @param beanName Bean名称
*/
protected void handleBeanNameConflict(BeanDefinitionRegistry registry, BeanDefinition beanDefinition,
Class<?> clazz, String beanName) {
BeanDefinition existingBean = registry.getBeanDefinition(beanName);
String existingClassName = existingBean.getBeanClassName();
log.warn("Bean name conflict: {} is already registered for class {}. Skipping registration for class {}.",
beanName, existingClassName, clazz.getName());
// 使用类名作为后缀,避免冲突
String newBeanName = beanName + "_" + clazz.getSimpleName();
log.info("Registering with alternative name: {}", newBeanName);
registry.registerBeanDefinition(newBeanName, beanDefinition);
}
/**
* 获取类型过滤器
*
* @return 类型过滤器
*/
protected abstract TypeFilter getTypeFilter();
/**
* 检查类是否符合要求
*
* @param clazz 类
* @return 是否符合要求
*/
protected abstract boolean isValidComponent(Class<?> clazz);
/**
* 获取Bean名称
*
* @param clazz 类
* @return Bean名称
*/
protected abstract String getBeanName(Class<?> clazz);
/**
* 获取组件类型名称
*
* @return 组件类型名称
*/
protected abstract String getComponentType();
/**
* 获取注解类型
*
* @return 注解类型
*/
protected abstract Class<? extends Annotation> getAnnotationType();
}
@@ -0,0 +1,78 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.literule.config;
import org.springblade.core.literule.annotation.RuleEngineComponent;
import org.springblade.core.literule.builder.RuleBuilder;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
/**
* 流程构建器组件注册器
* 用于扫描并注册带有@EngineComponent注解的类
*
* @author BladeX
*/
public class EngineComponentRegistrar extends AbstractComponentRegistrar {
@Override
protected TypeFilter getTypeFilter() {
return new AnnotationTypeFilter(RuleEngineComponent.class);
}
@Override
protected boolean isValidComponent(Class<?> clazz) {
return RuleBuilder.class.isAssignableFrom(clazz);
}
@Override
protected String getBeanName(Class<?> clazz) {
RuleEngineComponent annotation = clazz.getAnnotation(RuleEngineComponent.class);
String beanName = annotation.id();
// 如果id为空,则使用value
if (beanName.isEmpty()) {
beanName = annotation.value();
}
// 如果bean名称仍为空,使用类名首字母小写
if (beanName.isEmpty()) {
beanName = Character.toLowerCase(clazz.getSimpleName().charAt(0)) + clazz.getSimpleName().substring(1);
}
return beanName;
}
@Override
protected String getComponentType() {
return "FlowBuilder";
}
@Override
protected Class<RuleEngineComponent> getAnnotationType() {
return RuleEngineComponent.class;
}
}
@@ -0,0 +1,79 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.literule.config;
import org.springblade.core.literule.annotation.LiteRuleComponent;
import org.springblade.core.literule.provider.Rule;
import org.springblade.core.literule.provider.SwitchRule;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
/**
* 规则组件注册器
* 用于扫描并注册带有@RuleComponent注解的类
*
* @author BladeX
*/
public class RuleComponentRegistrar extends AbstractComponentRegistrar {
@Override
protected TypeFilter getTypeFilter() {
return new AnnotationTypeFilter(LiteRuleComponent.class);
}
@Override
protected boolean isValidComponent(Class<?> clazz) {
return Rule.class.isAssignableFrom(clazz) || SwitchRule.class.isAssignableFrom(clazz);
}
@Override
protected String getBeanName(Class<?> clazz) {
LiteRuleComponent annotation = clazz.getAnnotation(LiteRuleComponent.class);
String beanName = annotation.id();
// 如果id为空,则使用value
if (beanName.isEmpty()) {
beanName = annotation.value();
}
// 如果bean名称仍为空,使用类名首字母小写
if (beanName.isEmpty()) {
beanName = Character.toLowerCase(clazz.getSimpleName().charAt(0)) + clazz.getSimpleName().substring(1);
}
return beanName;
}
@Override
protected String getComponentType() {
return "Rule";
}
@Override
protected Class<LiteRuleComponent> getAnnotationType() {
return LiteRuleComponent.class;
}
}
@@ -0,0 +1,107 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.literule.config;
import org.springblade.core.literule.engine.BladeRuleEngineExecutor;
import org.springblade.core.literule.engine.RuleEngineExecutor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.scheduling.annotation.EnableScheduling;
import java.util.concurrent.*;
/**
* 规则引擎自动配置类
* 用于自动配置规则引擎相关组件
*
* @author BladeX
*/
@Configuration
@EnableScheduling
@Import({EngineComponentRegistrar.class, RuleComponentRegistrar.class})
@EnableConfigurationProperties(RuleEngineProperties.class)
public class RuleEngineAutoConfiguration {
/**
* 注册规则引擎执行器
*
* @return 规则引擎执行器
*/
@Bean
@ConditionalOnMissingBean
public RuleEngineExecutor ruleEngineExecutor(ApplicationContext applicationContext, RuleEngineProperties properties) {
return new BladeRuleEngineExecutor(applicationContext, properties);
}
/**
* 注册规则执行线程池
*
* @param properties 规则引擎配置属性
* @return 线程池
*/
@Bean
@ConditionalOnMissingBean(name = "ruleExecutorThreadPool")
public ThreadPoolExecutor ruleExecutorThreadPool(RuleEngineProperties properties) {
ThreadFactory threadFactory = r -> {
Thread thread = new Thread(r);
thread.setName("rule-executor-" + thread.getId());
thread.setDaemon(true);
return thread;
};
// 创建队列
ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor(properties, threadFactory);
// 设置拒绝策略
String rejectedPolicy = properties.getExecution().getRejectedPolicy();
RejectedExecutionHandler handler = switch (rejectedPolicy) {
case "ABORT" -> new ThreadPoolExecutor.AbortPolicy();
case "DISCARD" -> new ThreadPoolExecutor.DiscardPolicy();
case "DISCARD_OLDEST" -> new ThreadPoolExecutor.DiscardOldestPolicy();
default -> new ThreadPoolExecutor.CallerRunsPolicy();
};
threadPoolExecutor.setRejectedExecutionHandler(handler);
return threadPoolExecutor;
}
private static ThreadPoolExecutor getThreadPoolExecutor(RuleEngineProperties properties, ThreadFactory threadFactory) {
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(properties.getExecution().getQueueCapacity());
return new ThreadPoolExecutor(
properties.getExecution().getMaxParallelThreads(), // 核心线程数
properties.getExecution().getMaxParallelThreads(), // 最大线程数
properties.getExecution().getKeepAliveSeconds(), // 空闲线程保持时间
TimeUnit.SECONDS, // 时间单位
workQueue, // 任务队列
threadFactory // 线程工厂
);
}
}
@@ -0,0 +1,119 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.literule.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.ArrayList;
import java.util.List;
/**
* 规则引擎配置属性
* 集中管理规则引擎的配置项
*
* @author BladeX
*/
@Data
@ConfigurationProperties(prefix = "literule")
public class RuleEngineProperties {
/**
* 执行配置
*/
private Execution execution = new Execution();
/**
* 缓存配置
*/
private Cache cache = new Cache();
/**
* 预加载配置
*/
private Preload preload = new Preload();
/**
* 执行配置
*/
@Data
public static class Execution {
/**
* 执行超时时间(毫秒)
*/
private long timeout = 30000;
/**
* 并行执行的最大线程数
*/
private int maxParallelThreads = 10;
/**
* 线程池队列容量
*/
private int queueCapacity = 1000;
/**
* 线程池拒绝策略
* ABORT: 抛出异常
* CALLER_RUNS: 在调用者线程中执行
* DISCARD: 丢弃任务
* DISCARD_OLDEST: 丢弃最旧的任务
*/
private String rejectedPolicy = "CALLER_RUNS";
/**
* 线程池线程保持活跃时间(秒)
*/
private int keepAliveSeconds = 60;
}
/**
* 缓存配置
*/
@Data
public static class Cache {
/**
* 是否启用缓存
*/
private boolean enabled = true;
}
/**
* 预加载配置
*/
@Data
public static class Preload {
/**
* 是否启用预加载
*/
private boolean enabled = true;
/**
* 高优先级规则ID列表,优先预加载
*/
private List<String> highPriorityRules = new ArrayList<>();
}
}
@@ -0,0 +1,139 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.literule.context;
import lombok.Getter;
import org.springblade.core.literule.builder.chain.RuleChain;
import org.springblade.core.literule.provider.RuleConfig;
import org.springblade.core.literule.provider.RuleContext;
import org.springblade.core.literule.builder.RuleBuilderExecutor;
/**
* 规则上下文持有者
* 实现AutoCloseable接口,用于安全管理ThreadLocal资源
*
* @author BladeX
*/
public class RuleContextHolder implements AutoCloseable {
/**
* 获取参数
*/
@Getter
private final Object requestData;
/**
* 获取上下文
*/
@Getter
private final RuleContext context;
/**
* 获取配置
*/
@Getter
private final RuleConfig config;
/**
* 原始参数
*/
private final Object originalRequestData;
/**
* 原始上下文
*/
private final RuleContext originalContext;
/**
* 原始配置
*/
private final RuleConfig originalConfig;
/**
* 构造函数
*
* @param requestData 传递参数
* @param context 规则上下文
* @param config 规则配置
*/
public RuleContextHolder(Object requestData, RuleContext context, RuleConfig config) {
// 保存最新值
this.requestData = requestData;
this.context = context;
this.config = config;
// 保存原始值
this.originalRequestData = RuleContextManager.getRequestData();
this.originalContext = RuleContextManager.getContext();
this.originalConfig = RuleContextManager.getConfig();
// 设置新的上下文
RuleContextManager.set(requestData, context, config);
}
/**
* 执行规则
*
* @param rule 要执行的规则
* @throws Exception 执行过程中的异常
*/
public void execute(RuleChain rule) throws Exception {
if (rule != null) {
rule.execute();
}
}
/**
* 检查上下文是否执行成功
*
* @return 是否执行成功
*/
public boolean isSuccess() {
return context != null && context.isSuccess();
}
/**
* 关闭方法,清理ThreadLocal资源
*/
@Override
public void close() {
// 清理执行数据,处理嵌套调用和并发处理的场景
if (originalRequestData != null) {
RuleContextManager.setRequestData(originalRequestData);
} else {
RuleContextManager.removeRequestData();
}
if (originalContext != null) {
RuleContextManager.setContext(originalContext);
} else {
RuleContextManager.removeContext();
}
if (originalConfig != null) {
RuleContextManager.setConfig(originalConfig);
} else {
RuleContextManager.removeConfig();
}
// 清理执行栈,避免ThreadLocal资源泄漏
RuleBuilderExecutor.clearExecutionStack();
}
}
@@ -0,0 +1,175 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.literule.context;
import org.springblade.core.literule.provider.RuleConfig;
import org.springblade.core.literule.provider.RuleContext;
/**
* 规则上下文管理器
* 用于在线程内共享规则上下文和配置
*
* @author BladeX
*/
public class RuleContextManager {
/**
* 支持子线程继承父线程的上下文
*/
private static final InheritableThreadLocal<RuleContext> RULE_CONTEXT = new InheritableThreadLocal<>();
/**
* 支持子线程继承父线程的配置
*/
private static final InheritableThreadLocal<RuleConfig> RULE_CONFIG = new InheritableThreadLocal<>() {
@Override
protected RuleConfig initialValue() {
return RuleConfig.getDefault();
}
};
/**
* 支持子线程继承父线程的额外参数
*/
private static final InheritableThreadLocal<Object> REQUEST_DATA = new InheritableThreadLocal<>();
/**
* 设置上下文和配置
*
* @param requestData 额外参数
* @param context 规则上下文
* @param config 规则配置
*/
public static void set(Object requestData, RuleContext context, RuleConfig config) {
RULE_CONTEXT.set(context);
if (requestData != null) {
REQUEST_DATA.set(requestData);
}
if (config != null) {
RULE_CONFIG.set(config);
}
}
/**
* 设置上下文
*
* @param context 规则上下文
*/
public static void setContext(RuleContext context) {
RULE_CONTEXT.set(context);
}
/**
* 设置配置
*
* @param config 规则配置
*/
public static void setConfig(RuleConfig config) {
if (config != null) {
RULE_CONFIG.set(config);
}
}
/**
* 设置额外参数
*
* @param data 额外参数
*/
public static void setRequestData(Object data) {
REQUEST_DATA.set(data);
}
/**
* 获取额外参数
*
* @param <T> 参数类型
* @return 额外参数
*/
@SuppressWarnings("unchecked")
public static <T> T getRequestData() {
return (T) REQUEST_DATA.get();
}
/**
* 获取上下文
*
* @return 规则上下文
*/
public static RuleContext getContext() {
return RULE_CONTEXT.get();
}
/**
* 获取配置
*
* @return 规则配置
*/
public static RuleConfig getConfig() {
return RULE_CONFIG.get();
}
/**
* 清理上下文
*/
public static void removeContext() {
RULE_CONTEXT.remove();
}
/**
* 清理配置
*/
public static void removeConfig() {
RULE_CONFIG.remove();
}
/**
* 清理额外参数
*/
public static void removeRequestData() {
REQUEST_DATA.remove();
}
/**
* 清理上下文和配置
*/
public static void clear() {
RULE_CONTEXT.remove();
RULE_CONFIG.remove();
REQUEST_DATA.remove();
}
/**
* 检查上下文是否已设置
*
* @return 上下文是否已设置
*/
public static boolean isSet() {
return RULE_CONTEXT.get() != null;
}
private RuleContextManager() {
// 私有构造函数
}
}
@@ -0,0 +1,196 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.literule.context;
import org.springblade.core.literule.builder.RuleBuilderExecutor;
import org.springblade.core.literule.provider.RuleConfig;
import org.springblade.core.literule.provider.RuleContext;
import java.util.concurrent.Callable;
import java.util.function.Supplier;
/**
* 上下文传播器
* 用于在异步或并行执行中传递和管理上下文
*
* @author BladeX
*/
public class RuleContextPropagator {
/**
* 规则上下文
*/
private final RuleContext context;
/**
* 规则配置
*/
private final RuleConfig config;
/**
* 额外参数
*/
private final Object requestData;
/**
* 构造函数
*
* @param context 规则上下文
* @param config 规则配置
* @param requestData 额外参数
*/
public RuleContextPropagator(RuleContext context, RuleConfig config, Object requestData) {
this.context = context;
this.config = config;
this.requestData = requestData;
}
/**
* 恢复上下文
*/
public void restore() {
RuleContextManager.setContext(context);
RuleContextManager.setConfig(config);
RuleContextManager.setRequestData(requestData);
}
/**
* 包装Runnable,确保在子线程中正确传递和清理上下文
*
* @param task 原始任务
* @param context 规则上下文
* @param config 规则配置
* @return 包装后的Runnable
*/
public static Runnable wrapRunnable(Runnable task, RuleContext context, RuleConfig config) {
return wrapRunnable(task, context, config, null);
}
/**
* 包装Runnable,确保在子线程中正确传递和清理上下文
*
* @param task 原始任务
* @param context 规则上下文
* @param config 规则配置
* @param requestData 额外参数
* @return 包装后的Runnable
*/
public static Runnable wrapRunnable(Runnable task, RuleContext context, RuleConfig config, Object requestData) {
return () -> {
// 设置上下文
RuleContextManager.setContext(context);
RuleContextManager.setConfig(config);
RuleContextManager.setRequestData(requestData);
try {
// 执行任务
task.run();
} finally {
// 清理上下文
RuleContextManager.clear();
RuleBuilderExecutor.clearExecutionStack();
}
};
}
/**
* 包装Callable,确保在子线程中正确传递和清理上下文
*
* @param task 原始任务
* @param context 规则上下文
* @param config 规则配置
* @param <V> 返回值类型
* @return 包装后的Callable
*/
public static <V> Callable<V> wrapCallable(Callable<V> task, RuleContext context, RuleConfig config) {
return wrapCallable(task, context, config, null);
}
/**
* 包装Callable,确保在子线程中正确传递和清理上下文
*
* @param task 原始任务
* @param context 规则上下文
* @param config 规则配置
* @param requestData 额外参数
* @param <V> 返回值类型
* @return 包装后的Callable
*/
public static <V> Callable<V> wrapCallable(Callable<V> task, RuleContext context, RuleConfig config, Object requestData) {
return () -> {
// 设置上下文
RuleContextManager.setContext(context);
RuleContextManager.setConfig(config);
RuleContextManager.setRequestData(requestData);
try {
// 执行任务
return task.call();
} finally {
// 清理上下文
RuleContextManager.clear();
RuleBuilderExecutor.clearExecutionStack();
}
};
}
/**
* 包装Supplier,确保在子线程中正确传递和清理上下文
*
* @param supplier 原始供应商
* @param context 规则上下文
* @param config 规则配置
* @param <V> 返回值类型
* @return 包装后的Supplier
*/
public static <V> Supplier<V> wrapSupplier(Supplier<V> supplier, RuleContext context, RuleConfig config) {
return wrapSupplier(supplier, context, config, null);
}
/**
* 包装Supplier,确保在子线程中正确传递和清理上下文
*
* @param supplier 原始供应商
* @param context 规则上下文
* @param config 规则配置
* @param requestData 额外参数
* @param <V> 返回值类型
* @return 包装后的Supplier
*/
public static <V> Supplier<V> wrapSupplier(Supplier<V> supplier, RuleContext context, RuleConfig config, Object requestData) {
return () -> {
// 设置上下文
RuleContextManager.setContext(context);
RuleContextManager.setConfig(config);
RuleContextManager.setRequestData(requestData);
try {
// 执行任务
return supplier.get();
} finally {
// 清理上下文
RuleContextManager.clear();
}
};
}
}
@@ -0,0 +1,115 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.literule.core;
import org.springblade.core.literule.annotation.LiteRuleComponent;
import org.springblade.core.literule.context.RuleContextManager;
import org.springblade.core.literule.exception.RuleException;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.literule.provider.RuleConfig;
import org.springblade.core.literule.provider.RuleContext;
/**
* 规则基类
* 提供公共能力,如上下文管理、配置获取等
*
* @author BladeX
*/
@Slf4j
public abstract class AbstractRuleComponent {
/**
* 获取规则ID
* 优先从注解获取,如果没有注解则使用类名
*
* @return 规则ID
*/
protected String getRuleId() {
LiteRuleComponent annotation = this.getClass().getAnnotation(LiteRuleComponent.class);
if (annotation != null) {
String id = annotation.id();
if (!id.isEmpty()) {
return id;
}
return annotation.value();
}
return this.getClass().getSimpleName();
}
/**
* 获取额外参数
*
* @param <T> 参数类型
* @return 额外参数
*/
protected <T> T getRequestData() {
return RuleContextManager.getRequestData();
}
/**
* 获取上下文
*
* @param clazz 上下文类型
* @param <T> 上下文类型
* @return 上下文实例
*/
protected <T extends RuleContext> T getContextBean(Class<T> clazz) {
RuleContext context = RuleContextManager.getContext();
if (context == null) {
throw new RuleException("Rule context is not set");
}
// 直接类型匹配
if (clazz.isInstance(context)) {
return clazz.cast(context);
}
// 检查类名是否相同(忽略包名)
if (clazz.getSimpleName().equals(context.getClass().getSimpleName())) {
try {
// 尝试强制转换
@SuppressWarnings("unchecked")
T result = (T) context;
return result;
} catch (ClassCastException e) {
log.warn("Failed to cast context: {} to {}", context.getClass().getName(), clazz.getName());
}
}
throw new RuleException("Context type not match: " + context.getClass().getName() + ", expected: " + clazz.getName());
}
/**
* 获取配置
*
* @return 规则配置
*/
protected RuleConfig getConfig() {
return RuleContextManager.getConfig();
}
}
@@ -0,0 +1,89 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.literule.core;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.literule.context.RuleContextManager;
import org.springblade.core.literule.provider.Rule;
import org.springblade.core.literule.provider.RuleContext;
/**
* 普通规则抽象实现
* 提供规则执行的模板方法,处理执行前后的通用逻辑
*
* @author BladeX
*/
@Slf4j
public abstract class RuleComponent extends AbstractRuleComponent implements Rule {
@Override
public final void execute() throws Exception {
String ruleId = getRuleId();
long startTime = System.currentTimeMillis();
try {
// 执行具体规则逻辑
process();
// 记录执行时间
if (getConfig().isEnableTimeMonitor()) {
long cost = System.currentTimeMillis() - startTime;
recordExecutionTime(ruleId, cost);
}
} catch (Exception e) {
// 记录错误日志
if (getConfig().isEnableLogging()) {
log.error("Rule execute error: {}", ruleId, e);
}
throw e;
}
}
/**
* 记录执行时间
*
* @param ruleId 规则ID
* @param cost 执行耗时
*/
protected void recordExecutionTime(String ruleId, long cost) {
RuleContext context = RuleContextManager.getContext();
if (context instanceof RuleContextComponent) {
((RuleContextComponent) context).recordExecutionTime(ruleId, cost);
}
if (getConfig().isPrintExecutionTime()) {
log.info("Rule [{}] execute cost: {}ms", ruleId, cost);
}
}
/**
* 执行规则,由子类实现
*
* @throws Exception 执行过程中可能抛出的异常
*/
protected abstract void process() throws Exception;
}
@@ -0,0 +1,132 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.literule.core;
import lombok.Data;
import org.springblade.core.literule.provider.RuleContext;
import java.io.Serial;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* 规则上下文基类
* 提供上下文数据存储、错误信息管理和执行时间记录等功能
*
* @author BladeX
*/
@Data
public abstract class RuleContextComponent implements RuleContext {
@Serial
private static final long serialVersionUID = 1L;
/**
* 上下文数据,使用线程安全的ConcurrentHashMap
*/
private final Map<String, Object> contextData = new ConcurrentHashMap<>();
/**
* 错误消息列表,使用线程安全的同步列表
*/
private final List<String> errorMessages = Collections.synchronizedList(new ArrayList<>());
/**
* 是否执行成功,使用AtomicBoolean保证线程安全
*/
private final AtomicBoolean success = new AtomicBoolean(true);
/**
* 规则执行时间记录,使用线程安全的ConcurrentHashMap
*/
private final Map<String, Long> executionTimes = new ConcurrentHashMap<>();
/**
* 添加错误信息
*
* @param message 错误信息
*/
public void addError(String message) {
this.errorMessages.add(message);
this.success.set(false);
}
/**
* 记录规则执行时间
*
* @param ruleId 规则ID
* @param executionTime 执行时间(毫秒)
*/
public void recordExecutionTime(String ruleId, long executionTime) {
executionTimes.put(ruleId, executionTime);
}
/**
* 获取上下文数据
*
* @param key 数据键
* @param <T> 数据类型
* @return 数据值
*/
@SuppressWarnings("unchecked")
public <T> T getData(String key) {
return (T) contextData.get(key);
}
/**
* 设置上下文数据
*
* @param key 数据键
* @param value 数据值
*/
public void setData(String key, Object value) {
contextData.put(key, value);
}
/**
* 获取执行状态
*
* @return 是否成功
*/
@Override
public boolean isSuccess() {
return success.get();
}
/**
* 设置执行状态
*
* @param success 执行状态
*/
@Override
public void setSuccess(boolean success) {
this.success.set(success);
}
}
@@ -0,0 +1,94 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.literule.core;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.literule.context.RuleContextManager;
import org.springblade.core.literule.provider.RuleContext;
import org.springblade.core.literule.provider.SwitchRule;
import java.util.List;
/**
* 分支规则抽象实现
* 提供分支规则执行的模板方法,处理执行前后的通用逻辑
*
* @author BladeX
*/
@Slf4j
public abstract class RuleSwitchComponent extends AbstractRuleComponent implements SwitchRule {
@Override
public final List<String> execute() throws Exception {
String ruleId = getRuleId();
long startTime = System.currentTimeMillis();
try {
// 执行具体分支规则逻辑
List<String> nextRules = process();
// 记录执行时间
if (getConfig().isEnableTimeMonitor()) {
long cost = System.currentTimeMillis() - startTime;
recordExecutionTime(ruleId, cost);
}
return nextRules;
} catch (Exception e) {
// 记录错误日志
if (getConfig().isEnableLogging()) {
log.error("Rule Switch execute error: {}", ruleId, e);
}
throw e;
}
}
/**
* 记录执行时间
*
* @param ruleId 规则ID
* @param cost 执行耗时
*/
protected void recordExecutionTime(String ruleId, long cost) {
RuleContext context = RuleContextManager.getContext();
if (context instanceof RuleContextComponent) {
((RuleContextComponent) context).recordExecutionTime(ruleId, cost);
}
if (getConfig().isPrintExecutionTime()) {
log.info("Rule Switch [{}] execute cost: {}ms", ruleId, cost);
}
}
/**
* 执行规则,由子类实现
*
* @return 下一个要执行的规则ID列表
* @throws Exception 执行过程中可能抛出的异常
*/
protected abstract List<String> process() throws Exception;
}
@@ -0,0 +1,230 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.literule.engine;
import jakarta.annotation.PreDestroy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.literule.builder.RuleBuilder;
import org.springblade.core.literule.builder.RuleBuilderExecutor;
import org.springblade.core.literule.builder.chain.RuleChain;
import org.springblade.core.literule.config.RuleEngineProperties;
import org.springblade.core.literule.context.RuleContextHolder;
import org.springblade.core.literule.context.RuleContextPropagator;
import org.springblade.core.literule.provider.LiteRuleResponse;
import org.springblade.core.literule.provider.RuleConfig;
import org.springblade.core.literule.provider.RuleContext;
import org.springframework.context.ApplicationContext;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
/**
* 默认规则引擎执行器实现
* 提供规则链的同步和异步执行能力
*
* @author BladeX
*/
@Slf4j
@RequiredArgsConstructor
public class BladeRuleEngineExecutor implements RuleEngineExecutor {
private final ApplicationContext applicationContext;
private final RuleEngineProperties properties;
/**
* 规则链缓存
* 使用永久缓存策略,缓存一旦创建不会过期
*/
private final Map<String, RuleChain> ruleChainCache = new ConcurrentHashMap<>();
@Override
public <T extends RuleContext> LiteRuleResponse<T> execute(String chainId, Object requestData, T context, RuleConfig config) {
long startTime = System.currentTimeMillis();
// 参数校验
if (chainId == null || chainId.isEmpty()) {
return buildResponse(context, false, "Chain ID cannot be empty", startTime);
}
// 上下文校验
if (context == null) {
return buildResponse(null, false, "Context cannot be null", startTime);
}
try {
// 从缓存获取规则链
RuleChain chain = null;
if (properties.getCache().isEnabled()) {
chain = ruleChainCache.get(chainId);
}
// 缓存未命中,构建规则链
if (chain == null) {
RuleBuilder ruleBuilder = applicationContext.getBean(chainId, RuleBuilder.class);
chain = ruleBuilder.build();
// 放入缓存
if (properties.getCache().isEnabled()) {
addToCache(chainId, chain);
}
}
// 设置上下文并执行规则链
try (RuleContextHolder contextHolder = new RuleContextHolder(requestData, context, config)) {
// 使用contextHolder执行规则链
contextHolder.execute(chain);
// 构建成功响应
return buildResponse(context, context.isSuccess(),
context.isSuccess() ? "Rule execution succeed" : "Rule execution failed", startTime);
}
} catch (Exception e) {
String message = "Rule chain execution error: " + e.getMessage();
log.error(message, e);
// 设置上下文执行失败
context.setSuccess(false);
return buildResponse(context, false, message, startTime);
} finally {
// 清理执行栈
RuleBuilderExecutor.clearExecutionStack();
}
}
@Override
public <T extends RuleContext> CompletableFuture<LiteRuleResponse<T>> executeAsync(String chainId, Object requestData, T context, RuleConfig config, Executor executor) {
// 参数校验
if (chainId == null || chainId.isEmpty()) {
return CompletableFuture.completedFuture(
buildResponse(context, false, "Chain ID cannot be empty", System.currentTimeMillis())
);
}
// 上下文校验
if (context == null) {
return CompletableFuture.completedFuture(
buildResponse(null, false, "Context cannot be null", System.currentTimeMillis())
);
}
// 使用ContextPropagator包装任务,确保上下文正确传递
Supplier<LiteRuleResponse<T>> asyncTask = RuleContextPropagator.wrapSupplier(
// 异步执行逻辑
() -> execute(chainId, requestData, context, config),
// 传递当前线程的上下文
context,
// 传递规则配置
config,
// 传递额外参数
requestData
);
// 提交异步任务
return CompletableFuture.supplyAsync(asyncTask, executor)
.orTimeout(properties.getExecution().getTimeout(), TimeUnit.MILLISECONDS)
.exceptionally(e -> {
String message = "Async rule execution error: " + e.getMessage();
log.error(message, e);
// 设置上下文执行失败
context.setSuccess(false);
long errorTime = System.currentTimeMillis();
return buildResponse(context, false, message, errorTime);
});
}
/**
* 构建规则执行响应
*
* @param context 规则上下文
* @param success 是否成功
* @param message 错误消息
* @param startTime 开始时间
* @param <T> 上下文类型
* @return 规则执行响应
*/
private <T extends RuleContext> LiteRuleResponse<T> buildResponse(T context, boolean success, String message, long startTime) {
return LiteRuleResponse.<T>builder()
.success(success)
.message(message)
.context(context)
.executionTime(System.currentTimeMillis() - startTime)
.build();
}
/**
* 清理缓存
*/
public void clearCache() {
ruleChainCache.clear();
}
/**
* 从缓存中移除指定规则链
*
* @param chainId 规则链ID
*/
public void removeFromCache(String chainId) {
ruleChainCache.remove(chainId);
}
/**
* 将规则链添加到缓存
*
* @param chainId 规则链ID
* @param rule 规则实例
*/
public void addToCache(String chainId, RuleChain rule) {
if (properties.getCache().isEnabled()) {
ruleChainCache.put(chainId, rule);
}
}
/**
* 获取缓存大小
*
* @return 缓存中的规则链数量
*/
public int getCacheSize() {
return ruleChainCache.size();
}
/**
* 应用关闭时清理资源
*/
@PreDestroy
public void destroy() {
clearCache();
}
}
@@ -0,0 +1,145 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.literule.engine;
import org.springblade.core.literule.provider.RuleConfig;
import org.springblade.core.literule.provider.LiteRuleResponse;
import org.springblade.core.literule.provider.RuleContext;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
/**
* 规则引擎执行器接口
* 提供同步和异步执行规则链的方法
*
* @author BladeX
*/
public interface RuleEngineExecutor {
/**
* 同步执行规则链
*
* @param chainId 规则链ID
* @param requestData 传递参数
* @param context 规则上下文
* @param config 规则配置
* @param <T> 上下文类型
* @return 规则执行响应
*/
<T extends RuleContext> LiteRuleResponse<T> execute(String chainId, Object requestData, T context, RuleConfig config);
/**
* 使用默认配置同步执行规则链
*
* @param chainId 规则链ID
* @param requestData 传递参数
* @param context 规则上下文
* @param <T> 上下文类型
* @return 规则执行响应
*/
default <T extends RuleContext> LiteRuleResponse<T> execute(String chainId, Object requestData, T context) {
return execute(chainId, requestData, context, RuleConfig.getDefault());
}
/**
* 同步执行规则链
*
* @param chainId 规则链ID
* @param context 规则上下文
* @param config 规则配置
* @param <T> 上下文类型
* @return 规则执行响应
*/
default <T extends RuleContext> LiteRuleResponse<T> execute(String chainId, T context, RuleConfig config) {
return execute(chainId, null, context, config);
}
/**
* 使用默认配置同步执行规则链
*
* @param chainId 规则链ID
* @param context 规则上下文
* @param <T> 上下文类型
* @return 规则执行响应
*/
default <T extends RuleContext> LiteRuleResponse<T> execute(String chainId, T context) {
return execute(chainId, null, context, RuleConfig.getDefault());
}
/**
* 异步执行规则链
*
* @param chainId 规则链ID
* @param requestData 传递参数
* @param context 规则上下文
* @param config 规则配置
* @param executor 执行器
* @param <T> 上下文类型
* @return 规则执行响应的Future
*/
<T extends RuleContext> CompletableFuture<LiteRuleResponse<T>> executeAsync(String chainId, Object requestData, T context, RuleConfig config, Executor executor);
/**
* 使用默认配置异步执行规则链
*
* @param chainId 规则链ID
* @param requestData 传递参数
* @param context 规则上下文
* @param executor 执行器
* @param <T> 上下文类型
* @return 规则执行响应的Future
*/
default <T extends RuleContext> CompletableFuture<LiteRuleResponse<T>> executeAsync(String chainId, Object requestData, T context, Executor executor) {
return executeAsync(chainId, requestData, context, RuleConfig.getDefault(), executor);
}
/**
* 异步执行规则链
*
* @param chainId 规则链ID
* @param context 规则上下文
* @param config 规则配置
* @param executor 执行器
* @param <T> 上下文类型
* @return 规则执行响应的Future
*/
default <T extends RuleContext> CompletableFuture<LiteRuleResponse<T>> executeAsync(String chainId, T context, RuleConfig config, Executor executor) {
return executeAsync(chainId, null, context, config, executor);
}
/**
* 使用默认配置异步执行规则链
*
* @param chainId 规则链ID
* @param context 规则上下文
* @param executor 执行器
* @param <T> 上下文类型
* @return 规则执行响应的Future
*/
default <T extends RuleContext> CompletableFuture<LiteRuleResponse<T>> executeAsync(String chainId, T context, Executor executor) {
return executeAsync(chainId, null, context, RuleConfig.getDefault(), executor);
}
}
@@ -0,0 +1,178 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.literule.engine;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.literule.annotation.LiteRuleComponent;
import org.springblade.core.literule.annotation.RuleEngineComponent;
import org.springblade.core.literule.builder.RuleBuilder;
import org.springblade.core.literule.builder.chain.RuleChain;
import org.springblade.core.literule.config.RuleEngineProperties;
import org.springblade.core.literule.provider.Rule;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 规则预加载器
* 在应用启动时预加载所有规则
*
* @author BladeX
*/
@Slf4j
@Order
@Component
@RequiredArgsConstructor
public class RulePreloadRunner implements ApplicationRunner {
private final RuleEngineProperties properties;
private final ApplicationContext applicationContext;
private final RuleEngineExecutor ruleEngineExecutor;
/**
* 应用启动时执行预加载
*
* @param args 应用参数
*/
@Override
public void run(ApplicationArguments args) {
if (!properties.getPreload().isEnabled()) {
log.info("Rule preload is disabled, skipping preload step");
return;
}
log.info("Starting to preload blade rules");
long startTime = System.currentTimeMillis();
Map<String, Rule> rules = preloadRules();
Map<String, RuleChain> flows = preloadFlowBuilders();
warmupCache(flows);
log.info("Rule preload completed, loaded {} rules and {} rule flows, cost {}ms",
rules.size(), flows.size(), System.currentTimeMillis() - startTime);
}
/**
* 预加载所有规则
*
* @return 规则实例映射表
*/
private Map<String, Rule> preloadRules() {
Map<String, Rule> rules = new HashMap<>();
// 先加载高优先级的规则
List<String> highPriorityRules = properties.getPreload().getHighPriorityRules();
if (!highPriorityRules.isEmpty()) {
log.info("Starting to load high-priority rules: {}", highPriorityRules);
for (String ruleId : highPriorityRules) {
try {
Rule rule = applicationContext.getBean(ruleId, Rule.class);
rules.put(ruleId, rule);
log.debug("Loaded high-priority rule: {}", ruleId);
} catch (Exception e) {
log.warn("Failed to load high-priority rule: {}, reason: {}", ruleId, e.getMessage());
}
}
}
// 加载其他所有标注了@RuleComponent的规则
Map<String, Object> ruleComponents = applicationContext.getBeansWithAnnotation(LiteRuleComponent.class);
for (Map.Entry<String, Object> entry : ruleComponents.entrySet()) {
if (entry.getValue() instanceof Rule) {
String ruleId = entry.getKey();
if (!rules.containsKey(ruleId)) {
rules.put(ruleId, (Rule) entry.getValue());
log.debug("Loaded rule: {}", ruleId);
}
}
}
return rules;
}
/**
* 预加载所有规则流构建器
*
* @return 规则流实例映射表
*/
private Map<String, RuleChain> preloadFlowBuilders() {
Map<String, RuleChain> flows = new HashMap<>();
Map<String, Object> builderBeans = applicationContext.getBeansWithAnnotation(RuleEngineComponent.class);
for (Map.Entry<String, Object> entry : builderBeans.entrySet()) {
String beanName = entry.getKey();
Object bean = entry.getValue();
try {
if (bean instanceof RuleBuilder ruleBuilder) {
// 构建规则链
RuleChain rule = ruleBuilder.build();
flows.put(beanName, rule);
}
log.debug("Loaded rule flow, builder: {}", beanName);
} catch (Exception e) {
log.warn("Failed to load rule flow, builder: {}, reason: {}", beanName, e.getMessage());
}
}
return flows;
}
/**
* 预热缓存
*
* @param preloadedFlows 预加载的规则流
*/
private void warmupCache(Map<String, RuleChain> preloadedFlows) {
if (preloadedFlows.isEmpty() || !properties.getCache().isEnabled()) {
return;
}
log.info("Starting to warm up the blade rule cache");
int count = 0;
// 将规则流添加到缓存中
for (Map.Entry<String, RuleChain> entry : preloadedFlows.entrySet()) {
try {
// 调用执行器的缓存方法
if (ruleEngineExecutor instanceof BladeRuleEngineExecutor) {
((BladeRuleEngineExecutor) ruleEngineExecutor).addToCache(entry.getKey(), entry.getValue());
count++;
}
} catch (Exception e) {
log.warn("Failed to cache rule flow: {}, reason: {}", entry.getKey(), e.getMessage());
}
}
log.info("Rule cache warmup completed, cached {} rule flows", count);
}
}
@@ -0,0 +1,59 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.literule.exception;
import java.io.Serial;
/**
* 规则引擎异常类
* 用于规则执行过程中的异常处理
*
* @author BladeX
*/
public class RuleException extends RuntimeException {
@Serial
private static final long serialVersionUID = 1L;
/**
* 构造函数
*
* @param message 错误消息
*/
public RuleException(String message) {
super(message);
}
/**
* 构造函数
*
* @param message 错误消息
* @param cause 原始异常
*/
public RuleException(String message, Throwable cause) {
super(message, cause);
}
}
@@ -0,0 +1,64 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.literule.provider;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 规则引擎响应类
* 封装规则执行的结果信息
*
* @param <T> 规则上下文类型
* @author BladeX
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class LiteRuleResponse<T extends RuleContext> {
/**
* 是否执行成功
*/
private boolean success;
/**
* 错误消息
*/
private String message;
/**
* 规则上下文
*/
private T context;
/**
* 执行耗时(毫秒)
*/
private long executionTime;
}
@@ -0,0 +1,41 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.literule.provider;
/**
* 普通规则接口
* 所有普通规则都应实现此接口
*
* @author BladeX
*/
public interface Rule {
/**
* 执行规则
*
* @throws Exception 执行过程中可能抛出的异常
*/
void execute() throws Exception;
}
@@ -0,0 +1,77 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.literule.provider;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 规则执行配置类
* 用于控制规则执行的行为,如是否启用时间监控、日志等
*
* @author BladeX
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RuleConfig {
/**
* 是否启用执行时间监控
*/
private boolean enableTimeMonitor = true;
/**
* 是否打印执行时间
*/
private boolean printExecutionTime = true;
/**
* 是否启用日志
*/
private boolean enableLogging = true;
/**
* 默认超时时间30秒
*/
private int timeout = 30000;
/**
* 获取默认配置
*
* @return 默认配置实例
*/
public static RuleConfig getDefault() {
return RuleConfig.builder()
.enableTimeMonitor(true)
.printExecutionTime(true)
.enableLogging(true)
.timeout(30000)
.build();
}
}
@@ -0,0 +1,51 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.literule.provider;
import java.io.Serializable;
/**
* 规则上下文接口
* 所有规则上下文类都应实现此接口
*
* @author BladeX
*/
public interface RuleContext extends Serializable {
/**
* 是否执行成功
*
* @return 是否成功
*/
boolean isSuccess();
/**
* 设置执行状态
*
* @param success 执行状态
*/
void setSuccess(boolean success);
}
@@ -0,0 +1,44 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.literule.provider;
import java.util.List;
/**
* 分支规则接口
* 所有分支规则都应实现此接口
*
* @author BladeX
*/
public interface SwitchRule {
/**
* 执行规则并返回下一个规则ID列表
*
* @return 下一个要执行的规则ID列表
* @throws Exception 执行过程中可能抛出的异常
*/
List<String> execute() throws Exception;
}