每次業務規則變動都需要修改代碼、重新打包、上線部署?客户臨時想要調整折扣規則、風控策略或者計費邏輯,你就得加班加點改代碼?今天就來聊聊如何基於SpringBoot + QLExpress打造一個強大的動態規則引擎,讓業務規則變得靈活可控,再也不用擔心頻繁變更了! 原文鏈接
一、為什麼需要動態規則引擎?
在開始技術實現之前,我們先來理解為什麼動態規則引擎如此重要。
1.1 傳統業務規則的痛點
// 傳統業務規則的痛點示例
public class TraditionalBusinessRules {
public void痛點() {
System.out.println("=== 傳統業務規則的痛點 ===");
System.out.println("1. 代碼硬編碼:規則寫死在代碼裏");
System.out.println("2. 變更困難:每次修改都需要重新部署");
System.out.println("3. 發佈風險:頻繁上線增加系統風險");
System.out.println("4. 響應緩慢:無法快速響應業務需求");
System.out.println("5. 維護成本高:多個版本難以維護");
}
}
1.2 動態規則引擎的價值
// 動態規則引擎的價值
public class DynamicRuleEngineBenefits {
public void benefits() {
System.out.println("=== 動態規則引擎的價值 ===");
System.out.println("1. 業務靈活:規則可動態調整");
System.out.println("2. 快速響應:無需重新部署即可生效");
System.out.println("3. 降低風險:減少代碼變更和上線頻率");
System.out.println("4. 提升效率:業務人員可自助配置");
System.out.println("5. 統一管理:集中管理所有業務規則");
}
}
二、QLExpress簡介與優勢
QLExpress是阿里巴巴開源的一款輕量級表達式語言,非常適合用來實現動態規則引擎。
2.1 QLExpress特性
// QLExpress特性
public class QLExpressFeatures {
public void features() {
System.out.println("=== QLExpress特性 ===");
System.out.println("1. 語法簡潔:類似Java語法,易於學習");
System.out.println("2. 性能優秀:編譯後執行,速度快");
System.out.println("3. 安全可控:沙箱機制,防止惡意代碼");
System.out.println("4. 擴展性強:支持自定義函數和宏");
System.out.println("5. 易於集成:與SpringBoot無縫集成");
}
}
2.2 QLExpress與其他規則引擎對比
// QLExpress與其他規則引擎對比
public class RuleEngineComparison {
public void comparison() {
System.out.println("=== 規則引擎對比 ===");
System.out.println("Drools:功能強大但複雜,學習成本高");
System.out.println("QLExpress:輕量級,語法簡單,性能好");
System.out.println("Aviator:表達式引擎,功能相對簡單");
System.out.println("Groovy:功能強大但性能一般,安全性較差");
System.out.println("推薦:中小型項目選QLExpress,大型複雜項目選Drools");
}
}
三、SpringBoot集成QLExpress
3.1 依賴配置
<!-- pom.xml 添加依賴 -->
<dependencies>
<!-- SpringBoot Web Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- QLExpress -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>QLExpress</artifactId>
<version>3.2.4</version>
</dependency>
<!-- Redis(用於規則緩存) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 數據庫(用於規則存儲) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- 監控 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
3.2 QLExpress配置類
@Configuration
public class QLExpressConfig {
@Bean
public ExpressRunner expressRunner() {
ExpressRunner runner = new ExpressRunner();
// 註冊自定義函數
try {
runner.addFunction("contains", new ContainsFunction());
runner.addFunction("startsWith", new StartsWithFunction());
runner.addFunction("endsWith", new EndsWithFunction());
runner.addFunction("formatDate", new FormatDateFunction());
} catch (Exception e) {
throw new RuntimeException("註冊QLExpress函數失敗", e);
}
return runner;
}
@Bean
public DefaultContext<String, Object> expressContext() {
return new DefaultContext<>();
}
}
3.3 自定義函數實現
// 自定義函數:字符串包含判斷
public class ContainsFunction implements InstructionSetFunction {
@Override
public OperateData callSelf(ExpressionExecutor[] list,
ExpressPackage expressPackage,
RunnerContext context,
ErrorInfo errorInfo) throws Exception {
String source = (String) list[0].getObject(context);
String target = (String) list[1].getObject(context);
boolean result = source != null && source.contains(target);
return new OperateData(result, Boolean.class);
}
}
// 自定義函數:日期格式化
public class FormatDateFunction implements InstructionSetFunction {
@Override
public OperateData callSelf(ExpressionExecutor[] list,
ExpressPackage expressPackage,
RunnerContext context,
ErrorInfo errorInfo) throws Exception {
Date date = (Date) list[0].getObject(context);
String pattern = (String) list[1].getObject(context);
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
String result = sdf.format(date);
return new OperateData(result, String.class);
}
}
四、規則引擎核心實現
4.1 規則實體設計
@Entity
@Table(name = "rule_definitions")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RuleDefinition {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "rule_code", nullable = false, unique = true)
private String ruleCode; // 規則編碼
@Column(name = "rule_name", nullable = false)
private String ruleName; // 規則名稱
@Column(name = "rule_expression", nullable = false, columnDefinition = "text")
private String ruleExpression; // 規則表達式
@Column(name = "rule_description")
private String ruleDescription; // 規則描述
@Column(name = "rule_type", nullable = false)
private String ruleType; // 規則類型(DECISION-決策規則,VALIDATION-校驗規則等)
@Column(name = "enabled", nullable = false)
private Boolean enabled = true; // 是否啓用
@Column(name = "priority")
private Integer priority = 0; // 優先級
@Column(name = "created_at")
private LocalDateTime createdAt;
@Column(name = "updated_at")
private LocalDateTime updatedAt;
@PrePersist
protected void onCreate() {
createdAt = LocalDateTime.now();
updatedAt = LocalDateTime.now();
}
@PreUpdate
protected void onUpdate() {
updatedAt = LocalDateTime.now();
}
}
4.2 規則存儲Repository
@Repository
public interface RuleDefinitionRepository extends JpaRepository<RuleDefinition, Long> {
Optional<RuleDefinition> findByRuleCode(String ruleCode);
List<RuleDefinition> findByEnabledTrueOrderByPriorityDesc();
List<RuleDefinition> findByRuleTypeAndEnabledTrue(String ruleType);
}
4.3 規則引擎服務實現
@Service
@Slf4j
public class RuleEngineService {
@Autowired
private ExpressRunner expressRunner;
@Autowired
private RuleDefinitionRepository ruleRepository;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String RULE_CACHE_PREFIX = "rule_engine:rule:";
private static final long RULE_CACHE_TTL = 300; // 5分鐘緩存
/**
* 執行規則
*/
public RuleExecutionResult executeRule(String ruleCode, Map<String, Object> context) {
try {
// 1. 獲取規則定義
RuleDefinition rule = getRuleDefinition(ruleCode);
if (rule == null) {
return RuleExecutionResult.builder()
.success(false)
.errorCode("RULE_NOT_FOUND")
.errorMessage("規則不存在: " + ruleCode)
.build();
}
if (!rule.getEnabled()) {
return RuleExecutionResult.builder()
.success(false)
.errorCode("RULE_DISABLED")
.errorMessage("規則已禁用: " + ruleCode)
.build();
}
// 2. 執行規則表達式
Object result = executeExpression(rule.getRuleExpression(), context);
// 3. 返回執行結果
return RuleExecutionResult.builder()
.success(true)
.result(result)
.ruleCode(ruleCode)
.executedAt(LocalDateTime.now())
.build();
} catch (Exception e) {
log.error("執行規則失敗: ruleCode={}", ruleCode, e);
return RuleExecutionResult.builder()
.success(false)
.errorCode("EXECUTION_ERROR")
.errorMessage("規則執行異常: " + e.getMessage())
.build();
}
}
/**
* 批量執行規則
*/
public List<RuleExecutionResult> executeRules(List<String> ruleCodes, Map<String, Object> context) {
return ruleCodes.parallelStream()
.map(ruleCode -> executeRule(ruleCode, context))
.collect(Collectors.toList());
}
/**
* 獲取規則定義(帶緩存)
*/
private RuleDefinition getRuleDefinition(String ruleCode) {
String cacheKey = RULE_CACHE_PREFIX + ruleCode;
// 先從緩存獲取
RuleDefinition cachedRule = (RuleDefinition) redisTemplate.opsForValue().get(cacheKey);
if (cachedRule != null) {
return cachedRule;
}
// 緩存未命中,從數據庫獲取
Optional<RuleDefinition> ruleOpt = ruleRepository.findByRuleCode(ruleCode);
if (ruleOpt.isPresent()) {
RuleDefinition rule = ruleOpt.get();
// 存入緩存
redisTemplate.opsForValue().set(cacheKey, rule, RULE_CACHE_TTL, TimeUnit.SECONDS);
return rule;
}
return null;
}
/**
* 執行表達式
*/
private Object executeExpression(String expression, Map<String, Object> context) throws Exception {
// 創建執行上下文
DefaultContext<String, Object> expressContext = new DefaultContext<>();
expressContext.putAll(context);
// 執行表達式
Object result = expressRunner.execute(expression, expressContext, null, true, false);
return result;
}
/**
* 驗證規則語法
*/
public RuleValidationResult validateRule(String expression) {
try {
expressRunner.checkSyntax(expression);
return RuleValidationResult.builder()
.valid(true)
.message("規則語法正確")
.build();
} catch (Exception e) {
return RuleValidationResult.builder()
.valid(false)
.message("規則語法錯誤: " + e.getMessage())
.build();
}
}
/**
* 清除規則緩存
*/
public void clearRuleCache(String ruleCode) {
String cacheKey = RULE_CACHE_PREFIX + ruleCode;
redisTemplate.delete(cacheKey);
}
/**
* 清除所有規則緩存
*/
public void clearAllRuleCache() {
Set<String> keys = redisTemplate.keys(RULE_CACHE_PREFIX + "*");
if (keys != null && !keys.isEmpty()) {
redisTemplate.delete(keys);
}
}
}
4.4 規則執行結果封裝
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RuleExecutionResult {
private boolean success;
private String errorCode;
private String errorMessage;
private Object result;
private String ruleCode;
private LocalDateTime executedAt;
private long executionTime; // 執行耗時(毫秒)
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RuleValidationResult {
private boolean valid;
private String message;
private List<String> errors;
}
五、業務場景實戰
5.1 場景一:訂單折扣規則
// 訂單折扣規則示例
@RestController
@RequestMapping("/api/discount")
@Slf4j
public class DiscountRuleController {
@Autowired
private RuleEngineService ruleEngineService;
/**
* 計算訂單折扣
*/
@PostMapping("/calculate")
public ResponseEntity<DiscountResult> calculateDiscount(@RequestBody OrderRequest request) {
try {
// 構造規則執行上下文
Map<String, Object> context = new HashMap<>();
context.put("orderAmount", request.getOrderAmount());
context.put("customerLevel", request.getCustomerLevel());
context.put("productCategory", request.getProductCategory());
context.put("orderTime", new Date());
// 執行折扣規則
RuleExecutionResult result = ruleEngineService.executeRule("DISCOUNT_RULE_001", context);
if (result.isSuccess()) {
Double discountRate = (Double) result.getResult();
BigDecimal discountAmount = request.getOrderAmount().multiply(BigDecimal.valueOf(discountRate));
BigDecimal finalAmount = request.getOrderAmount().subtract(discountAmount);
DiscountResult discountResult = DiscountResult.builder()
.originalAmount(request.getOrderAmount())
.discountRate(discountRate)
.discountAmount(discountAmount)
.finalAmount(finalAmount)
.build();
return ResponseEntity.ok(discountResult);
} else {
return ResponseEntity.badRequest()
.body(DiscountResult.builder()
.errorMessage(result.getErrorMessage())
.build());
}
} catch (Exception e) {
log.error("計算折扣失敗", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(DiscountResult.builder()
.errorMessage("系統異常")
.build());
}
}
}
// 示例規則表達式(存儲在數據庫中)
/*
rule_code: DISCOUNT_RULE_001
rule_name: 訂單折扣計算規則
rule_expression:
if (customerLevel == "VIP") {
if (orderAmount > 1000) {
return 0.2; // VIP客户訂單金額大於1000元,享受2折
} else {
return 0.1; // VIP客户其他情況,享受1折
}
} else if (customerLevel == "GOLD") {
if (orderAmount > 500) {
return 0.15; // 金牌客户訂單金額大於500元,享受1.5折
} else {
return 0.05; // 金牌客户其他情況,享受0.5折
}
} else {
if (orderAmount > 2000) {
return 0.1; // 普通客户訂單金額大於2000元,享受1折
} else {
return 0.0; // 普通客户其他情況,無折扣
}
}
*/
5.2 場景二:風控校驗規則
// 風控校驗規則示例
@Service
@Slf4j
public class RiskControlService {
@Autowired
private RuleEngineService ruleEngineService;
/**
* 風控校驗
*/
public RiskCheckResult checkRisk(TransactionRequest request) {
try {
// 構造風控上下文
Map<String, Object> context = new HashMap<>();
context.put("userId", request.getUserId());
context.put("amount", request.getAmount());
context.put("ipAddress", request.getIpAddress());
context.put("deviceType", request.getDeviceType());
context.put("transactionTime", new Date());
context.put("userRiskScore", getUserRiskScore(request.getUserId()));
// 執行風控規則
RuleExecutionResult result = ruleEngineService.executeRule("RISK_CONTROL_RULE_001", context);
if (result.isSuccess()) {
Boolean allowTransaction = (Boolean) result.getResult();
return RiskCheckResult.builder()
.allowed(allowTransaction)
.riskLevel(allowTransaction ? "LOW" : "HIGH")
.checkTime(LocalDateTime.now())
.build();
} else {
// 規則執行失敗,默認允許交易但記錄日誌
log.warn("風控規則執行失敗: {}", result.getErrorMessage());
return RiskCheckResult.builder()
.allowed(true)
.riskLevel("UNKNOWN")
.warningMessage("風控檢查異常")
.checkTime(LocalDateTime.now())
.build();
}
} catch (Exception e) {
log.error("風控校驗異常", e);
// 異常情況下默認允許交易
return RiskCheckResult.builder()
.allowed(true)
.riskLevel("SYSTEM_ERROR")
.errorMessage("系統異常")
.checkTime(LocalDateTime.now())
.build();
}
}
private double getUserRiskScore(Long userId) {
// 獲取用户風險評分的邏輯
return 0.5;
}
}
// 示例風控規則表達式
/*
rule_code: RISK_CONTROL_RULE_001
rule_name: 交易風控校驗規則
rule_expression:
// 高風險IP地址檢查
def highRiskIps = ["192.168.1.100", "10.0.0.1"];
if (highRiskIps.contains(ipAddress)) {
return false; // 高風險IP,拒絕交易
}
// 大額交易檢查
if (amount > 10000 && userRiskScore < 0.7) {
return false; // 高額交易且用户風險評分較低,拒絕交易
}
// 異常設備檢查
if (deviceType == "emulator" || deviceType == "unknown") {
return false; // 模擬器或未知設備,拒絕交易
}
// 時間段檢查
def hour = formatDate(transactionTime, "HH");
if (hour >= "02" && hour <= "05") {
// 凌晨2點到5點,增加額外檢查
if (amount > 5000) {
return false; // 凌晨大額交易,拒絕
}
}
return true; // 通過風控檢查
*/
六、規則管理後台
6.1 規則管理Controller
@RestController
@RequestMapping("/api/rules")
@Slf4j
public class RuleManagementController {
@Autowired
private RuleEngineService ruleEngineService;
@Autowired
private RuleDefinitionRepository ruleRepository;
/**
* 查詢規則列表
*/
@GetMapping
public ResponseEntity<List<RuleDefinition>> listRules(
@RequestParam(required = false) String ruleType,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size) {
Pageable pageable = PageRequest.of(page, size, Sort.by("updatedAt").descending());
Page<RuleDefinition> rulePage;
if (StringUtils.hasText(ruleType)) {
rulePage = ruleRepository.findByRuleType(ruleType, pageable);
} else {
rulePage = ruleRepository.findAll(pageable);
}
return ResponseEntity.ok(rulePage.getContent());
}
/**
* 獲取規則詳情
*/
@GetMapping("/{id}")
public ResponseEntity<RuleDefinition> getRule(@PathVariable Long id) {
Optional<RuleDefinition> rule = ruleRepository.findById(id);
return rule.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
/**
* 創建規則
*/
@PostMapping
public ResponseEntity<RuleDefinition> createRule(@RequestBody RuleDefinition rule) {
// 驗證規則語法
RuleValidationResult validationResult = ruleEngineService.validateRule(rule.getRuleExpression());
if (!validationResult.isValid()) {
throw new IllegalArgumentException("規則語法錯誤: " + validationResult.getMessage());
}
rule.setCreatedAt(LocalDateTime.now());
rule.setUpdatedAt(LocalDateTime.now());
RuleDefinition savedRule = ruleRepository.save(rule);
// 清除相關緩存
ruleEngineService.clearRuleCache(rule.getRuleCode());
return ResponseEntity.ok(savedRule);
}
/**
* 更新規則
*/
@PutMapping("/{id}")
public ResponseEntity<RuleDefinition> updateRule(@PathVariable Long id, @RequestBody RuleDefinition rule) {
Optional<RuleDefinition> existingRule = ruleRepository.findById(id);
if (!existingRule.isPresent()) {
return ResponseEntity.notFound().build();
}
// 驗證規則語法
RuleValidationResult validationResult = ruleEngineService.validateRule(rule.getRuleExpression());
if (!validationResult.isValid()) {
throw new IllegalArgumentException("規則語法錯誤: " + validationResult.getMessage());
}
RuleDefinition updatedRule = existingRule.get();
updatedRule.setRuleName(rule.getRuleName());
updatedRule.setRuleExpression(rule.getRuleExpression());
updatedRule.setRuleDescription(rule.getRuleDescription());
updatedRule.setRuleType(rule.getRuleType());
updatedRule.setEnabled(rule.getEnabled());
updatedRule.setPriority(rule.getPriority());
updatedRule.setUpdatedAt(LocalDateTime.now());
RuleDefinition savedRule = ruleRepository.save(updatedRule);
// 清除相關緩存
ruleEngineService.clearRuleCache(updatedRule.getRuleCode());
return ResponseEntity.ok(savedRule);
}
/**
* 刪除規則
*/
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteRule(@PathVariable Long id) {
Optional<RuleDefinition> rule = ruleRepository.findById(id);
if (rule.isPresent()) {
ruleRepository.deleteById(id);
// 清除相關緩存
ruleEngineService.clearRuleCache(rule.get().getRuleCode());
}
return ResponseEntity.ok().build();
}
/**
* 測試規則
*/
@PostMapping("/test")
public ResponseEntity<RuleExecutionResult> testRule(@RequestBody TestRuleRequest request) {
RuleExecutionResult result = ruleEngineService.executeRule(request.getRuleCode(), request.getContext());
return ResponseEntity.ok(result);
}
/**
* 驗證規則語法
*/
@PostMapping("/validate")
public ResponseEntity<RuleValidationResult> validateRule(@RequestBody ValidateRuleRequest request) {
RuleValidationResult result = ruleEngineService.validateRule(request.getExpression());
return ResponseEntity.ok(result);
}
/**
* 清除所有規則緩存
*/
@PostMapping("/cache/clear")
public ResponseEntity<String> clearAllCache() {
ruleEngineService.clearAllRuleCache();
return ResponseEntity.ok("緩存清除成功");
}
}
6.2 規則測試請求對象
@Data
@NoArgsConstructor
@AllArgsConstructor
public class TestRuleRequest {
private String ruleCode;
private Map<String, Object> context;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ValidateRuleRequest {
private String expression;
}
七、性能優化與監控
7.1 規則執行監控
@Component
@Slf4j
public class RuleExecutionMetrics {
@Autowired
private MeterRegistry meterRegistry;
private final Counter ruleExecutions;
private final Counter ruleFailures;
private final Timer ruleExecutionTimer;
public RuleExecutionMetrics(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
this.ruleExecutions = Counter.builder("rule.engine.executions")
.description("規則執行次數")
.register(meterRegistry);
this.ruleFailures = Counter.builder("rule.engine.failures")
.description("規則執行失敗次數")
.register(meterRegistry);
this.ruleExecutionTimer = Timer.builder("rule.engine.execution.time")
.description("規則執行耗時")
.register(meterRegistry);
}
/**
* 記錄規則執行
*/
public void recordRuleExecution(String ruleCode, boolean success, long executionTime) {
ruleExecutions.increment(Tag.of("rule_code", ruleCode), Tag.of("success", String.valueOf(success)));
if (!success) {
ruleFailures.increment(Tag.of("rule_code", ruleCode));
}
ruleExecutionTimer.record(executionTime, TimeUnit.MILLISECONDS);
}
/**
* 獲取規則執行統計
*/
public RuleExecutionStats getExecutionStats(String ruleCode) {
// 通過Micrometer獲取統計數據
return new RuleExecutionStats();
}
}
7.2 規則緩存優化
@Service
public class OptimizedRuleCacheService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// 使用本地緩存 + Redis緩存的二級緩存策略
private final Cache<String, RuleDefinition> localCache =
Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(60, TimeUnit.SECONDS)
.build();
/**
* 獲取規則定義(二級緩存)
*/
public RuleDefinition getRuleDefinition(String ruleCode) {
// 1. 先查本地緩存
RuleDefinition rule = localCache.getIfPresent(ruleCode);
if (rule != null) {
return rule;
}
// 2. 查Redis緩存
String cacheKey = "rule_engine:rule:" + ruleCode;
rule = (RuleDefinition) redisTemplate.opsForValue().get(cacheKey);
if (rule != null) {
localCache.put(ruleCode, rule);
return rule;
}
// 3. 查數據庫並更新緩存
// 實現邏輯...
return null;
}
}
八、安全與權限控制
8.1 規則編輯權限控制
@RestController
@RequestMapping("/api/rules")
@PreAuthorize("hasRole('ADMIN') or hasRole('RULE_MANAGER')")
public class SecureRuleManagementController {
// 只有管理員或規則管理員才能訪問規則管理接口
@PostMapping
@PreAuthorize("hasAuthority('RULE_CREATE')")
public ResponseEntity<RuleDefinition> createRule(@RequestBody RuleDefinition rule) {
// 創建規則需要RULE_CREATE權限
return ResponseEntity.ok(new RuleDefinition());
}
@PutMapping("/{id}")
@PreAuthorize("hasAuthority('RULE_UPDATE')")
public ResponseEntity<RuleDefinition> updateRule(@PathVariable Long id, @RequestBody RuleDefinition rule) {
// 更新規則需要RULE_UPDATE權限
return ResponseEntity.ok(new RuleDefinition());
}
@DeleteMapping("/{id}")
@PreAuthorize("hasAuthority('RULE_DELETE')")
public ResponseEntity<Void> deleteRule(@PathVariable Long id) {
// 刪除規則需要RULE_DELETE權限
return ResponseEntity.ok().build();
}
}
8.2 規則表達式安全檢查
@Service
public class RuleSecurityValidator {
private static final Set<String> FORBIDDEN_KEYWORDS = Set.of(
"System", "Runtime", "Process", "File", "Thread",
"ClassLoader", "SecurityManager", "exit", "halt"
);
/**
* 檢查規則表達式安全性
*/
public boolean isExpressionSafe(String expression) {
// 檢查是否包含禁用關鍵字
for (String keyword : FORBIDDEN_KEYWORDS) {
if (expression.contains(keyword)) {
return false;
}
}
// 檢查是否包含危險操作
if (expression.contains("import ") ||
expression.contains("new ") ||
expression.contains("eval(")) {
return false;
}
return true;
}
}
九、最佳實踐總結
9.1 規則設計原則
public class RuleDesignPrinciples {
public void principles() {
System.out.println("=== 規則設計原則 ===");
System.out.println("1. 簡潔性:規則表達式應儘量簡單明瞭");
System.out.println("2. 可讀性:使用有意義的變量名和註釋");
System.out.println("3. 可測試性:規則應易於測試和驗證");
System.out.println("4. 可維護性:避免過於複雜的嵌套邏輯");
System.out.println("5. 性能考慮:避免在規則中執行耗時操作");
}
}
9.2 運維操作手冊
public class OperationsManual {
public void manual() {
System.out.println("=== 規則引擎運維手冊 ===");
System.out.println("日常維護:");
System.out.println("- 監控規則執行成功率和響應時間");
System.out.println("- 定期清理過期的規則緩存");
System.out.println("- 備份重要的規則定義");
System.out.println("- 監控Redis緩存使用情況");
System.out.println("\n應急處理:");
System.out.println("- 規則執行異常:檢查規則語法和上下文");
System.out.println("- 性能下降:分析慢規則並優化");
System.out.println("- 緩存失效:手動清除緩存並重啓服務");
System.out.println("- 數據庫連接異常:檢查數據庫連接池配置");
System.out.println("\n版本升級:");
System.out.println("- 規則語法變更時需要適配");
System.out.println("- 數據庫表結構變更時需要遷移");
System.out.println("- 緩存策略變更時需要清理舊緩存");
}
}
結語
通過本文的介紹,相信你已經掌握瞭如何基於SpringBoot + QLExpress打造一個功能強大的動態規則引擎。這套方案不僅能夠滿足大多數業務場景的需求,還具有良好的擴展性和維護性。
關鍵要點總結:
- 選擇合適的規則引擎:QLExpress輕量級且易用
- 合理的架構設計:緩存+數據庫的存儲方案
- 完善的監控體系:性能指標和執行日誌
- 安全保障機制:權限控制和表達式安全檢查
- 運維友好性:管理後台和操作手冊
記住,規則引擎的價值在於讓業務變得更加靈活,但也要注意避免過度設計。在實際應用中,要根據業務複雜度和變更頻率來決定是否需要引入規則引擎。
如果你覺得這篇文章對你有幫助,歡迎分享給更多的朋友。在規則引擎的探索路上,我們一起成長!
關注「服務端技術精選」,獲取更多幹貨技術文章!