知識庫 / Spring RSS 訂閱

在Java中實現一個簡單的規則引擎

Spring
HongKong
2
10:41 AM · Dec 06 ,2025

1. 概述

在許多應用程序中,業務決策依賴於一組規則,這些規則對數據進行評估以產生結果或得出結論。 規則引擎允許動態定義和執行業務規則,同時將其與應用程序代碼解耦,從而使其更易於維護、擴展和管理應用程序中的複雜決策邏輯。

規則引擎提供分層設計、靈活性和可重用性,同時執行以結構化格式定義的業務規則。 儘管存在成熟的庫,例如 Drools 或 Easy Rules,以及其他可以處理複雜規則管理的庫,但在某些情況下,更簡單的方案也足夠。 這使我們能夠最大限度地減少依賴關係,同時滿足特定的業務需求。

在本教程中,我們將使用兩種方法構建一個簡單的規則引擎。 首先,使用 Spring Expression Language (SpEL) 進行動態規則,然後使用 POJO(Plain Old Java Object)方法,該方法提供更強的類型安全。

2. 安裝配置

要使用 Spring Expression Language,我們需要添加 spring-expression 依賴:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-expression</artifactId>
    <version>7.0.0-M7</version>
</dependency>

現在我們將定義用於與規則引擎實現的模型類。

首先,讓我們定義 客户 類:

public class Customer {
    private String name;
    private int loyaltyPoints;
    private boolean firstOrder;

    // standard getters and setters
}

接下來,我們將定義 Order 類:

public class Order {
    private double amount;
    private Customer customer;

    // standard getters and setters

}

3. 使用 SpEL (Spring 表達式語言)

Spring 表達式語言 (SpEL) 是一種支持在運行時查詢和更新對象圖的表達式語言。

它支持多種運算符,易於使用,並且與 Spring 集成良好。

3.1. 定義規則

我們將使用 SpEL 定義規則,該規則可以在配置文件中創建,也可以直接在代碼中創建,它接受一個表達式及其描述:

public class SpelRule {

    private final String expression;
    private final String description;

    public SpelRule(String expression, String description) {
        this.expression = expression;
        this.description = description;
    }

    public boolean evaluate(Order order) {
        ExpressionParser parser = new SpelExpressionParser();
        StandardEvaluationContext context = new StandardEvaluationContext(order);
        context.setVariable("order", order);
        return parser.parseExpression(expression)
          .getValue(context, Boolean.class);
    }

    // standard getters and setters
}

evaluate 方法接受一個類型為 Order 的參數,並將其置於上下文中。然後,系統將上下文中與定義的表達式進行比較,以確定輸入值是否滿足規則。

3.2. 測試

我們將使用以下兩個簡單規則來測試 SpEL 引擎:

  • 忠誠度折扣:如果忠誠度積分大於 500,則客户可享受折扣。
  • 首次訂單高價值折扣:如果客户是首次下單且訂單金額大於 500,則提供特別折扣。

以下定義並驗證這些規則:

@Test
void whenLoyalCustomer_thenEligibleForDiscount() {
    Customer customer = new Customer("Bob", 730, false);
    Order order = new Order(200.0, customer);

    SpelRule rule = new SpelRule(
        "#order.customer.loyaltyPoints > 500",
        "Loyalty discount rule"
    );
    assertTrue(rule.evaluate(order));
}

@Test
void whenFirstOrderHighAmount_thenEligibleForSpecialDiscount() {
    Customer customer = new Customer("Bob", 0, true);
    Order order = new Order(800.0, customer);

    SpelRule approvalRule = new SpelRule(
        "#order.customer.firstOrder and #order.amount > 500",
        "First-time customer with high order gets special discount"
    );
    assertTrue(approvalRule.evaluate(order));
}

我們能看到,兩個新的業務規則已添加到提供的輸入數據中並已驗證。

4. 基於 POJO 的規則引擎

現在,讓我們探索一個基於 Java 的規則引擎,它相比 SpEL 提供了更好的類型安全性。

4.1. 定義規則

我們將首先定義一個 <em >Rule</em > 接口,該接口將為所有業務規則提供合同。合同與我們為 SpEL 引擎提供的合同類似;引擎將評估表達式與傳遞給規則的 <em >Order</em >> 進行比較。evaluate> 方法提供了更大的類型安全,因為它使用輸入對象屬性來定義和執行規則:`

public interface IRule {
    boolean evaluate(Order order);
    String description();
}

接下來,我們將根據業務需求定義規則。

首先,我們從“會員折扣”規則開始:

public class LoyaltyDiscountRule implements IRule{

    @Override
    public boolean evaluate(Order order) {
        return order.getCustomer().getLoyaltyPoints() > 500;
    }

    @Override
    public String description() {
        return "Loyalty Discount Rule: Customer has more than 500 points";
    }
}

接下來,我們將定義“首級高價值折扣”規則:

public class FirstOrderHighValueSpecialDiscountRule implements IRule {

    @Override
    public boolean evaluate(Order order) {
        return order.getCustomer()
          .isFirstOrder() && order.getAmount() > 500;
    }

    @Override
    public String description() {
        return "First Order Special Discount Rule: First Time customer with high value order";
    }
}

現在我們已經制定了一些規則,接下來我們將定義規則引擎,該引擎將處理這些規則,以供輸入數據使用:

public class RuleEngine {
    private final List<IRule> rules;

    public RuleEngine(List<IRule> rules) {
        this.rules = rules;
    }

    public List<String> evaluate(Order order) {
        return rules.stream()
          .filter(rule -> rule.evaluate(order))
          .map(IRule::description)
          .collect(Collectors.toList());
    }
}

規則引擎擁有自己的 evaluate 方法,它接受輸入參數。 引擎首先評估所有規則,最後返回滿足這些規則的規則。

4.2. 測試

我們根據業務需求定義了規則,並部署了規則引擎來處理這些規則。下面我們運行一些測試以驗證它們:

@Test
void whenTwoRulesTriggered_thenBothDescriptionsReturned() {
    Customer customer = new Customer("Max", 550, true);
    Order order = new Order(600.0, customer);

    RuleEngine engine = new RuleEngine(List.of(new LoyaltyDiscountRule(), new FirstOrderHighValueSpecialDiscountRule()));

    List<String> results = engine.evaluate(order);

    assertEquals(2, results.size());
    assertTrue(results.contains("Loyalty Discount Rule: Customer has more than 500 points"));
    assertTrue(results.contains("First Order Special Discount Rule: First Time customer with high value order"));
}

@Test
void whenNoRulesTriggered_thenEmptyListReturned() {
    Customer customer = new Customer("Max", 50, false);
    Order order = new Order(200.0, customer);

    RuleEngine engine = new RuleEngine(List.of(new LoyaltyDiscountRule(), new FirstOrderHighValueSpecialDiscountRule()));

    List<String> results = engine.evaluate(order);

    assertTrue(results.isEmpty());
}

如我們所見,在第一個測試用例中,規則是基於 客户訂單 輸入而滿足的。另一方面,由於輸入參數不符合業務要求,因此在第二個測試用例中,沒有規則得到滿足。

5. 規則引擎方法比較

我們研究了兩種實現中規則的評估方式,並總結了每種方法的關鍵特性和權衡:

方面 SpEL 方法 POJO 方法
編譯時安全性 無編譯時安全性 – 錯誤僅在運行時捕獲 具有編譯時安全性
重構 底層屬性發生變化時,重構成本高 易於重構的代碼
調試 維護和調試複雜規則具有挑戰性 易於調試
靈活性 適用於頻繁規則更改 規則頻繁更改時,靈活性較低
規則更新 無需代碼更改即可更新規則 添加或更新規則需要代碼更改和重新部署

6. 結論

在本文中,我們探討了如何從零開始構建一個簡單的基於 Java 的規則引擎。

我們首先從基於 SpEL 的引擎開始,該引擎在運行時評估動態規則,但維護和調試起來具有挑戰性,並且不提供編譯時檢查。

接下來,我們探索了基於 POJO 的引擎,該引擎提供更高的類型安全性和清晰度;然而,它引入了更嚴格的規則系統。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.