init
This commit is contained in:
@@ -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` 类,提供了完整的使用示例。
|
||||
@@ -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>
|
||||
+60
@@ -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 "";
|
||||
}
|
||||
+60
@@ -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 "";
|
||||
}
|
||||
+69
@@ -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);
|
||||
}
|
||||
}
|
||||
+43
@@ -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();
|
||||
}
|
||||
+101
@@ -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() {
|
||||
// 私有构造函数,防止实例化
|
||||
}
|
||||
}
|
||||
+209
@@ -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;
|
||||
}
|
||||
}
|
||||
+50
@@ -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;
|
||||
}
|
||||
+54
@@ -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();
|
||||
}
|
||||
}
|
||||
+137
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+120
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+246
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+180
@@ -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();
|
||||
}
|
||||
+78
@@ -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;
|
||||
}
|
||||
}
|
||||
+79
@@ -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;
|
||||
}
|
||||
}
|
||||
+107
@@ -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 // 线程工厂
|
||||
);
|
||||
}
|
||||
}
|
||||
+119
@@ -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<>();
|
||||
}
|
||||
}
|
||||
+139
@@ -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();
|
||||
}
|
||||
}
|
||||
+175
@@ -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() {
|
||||
// 私有构造函数
|
||||
}
|
||||
}
|
||||
+196
@@ -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();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+115
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
+89
@@ -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;
|
||||
}
|
||||
+132
@@ -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);
|
||||
}
|
||||
}
|
||||
+94
@@ -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;
|
||||
}
|
||||
+230
@@ -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();
|
||||
}
|
||||
}
|
||||
+145
@@ -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);
|
||||
}
|
||||
}
|
||||
+178
@@ -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);
|
||||
}
|
||||
}
|
||||
+59
@@ -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);
|
||||
}
|
||||
}
|
||||
+64
@@ -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;
|
||||
}
|
||||
+77
@@ -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();
|
||||
}
|
||||
}
|
||||
+51
@@ -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);
|
||||
}
|
||||
+44
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user