You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java RuleBook引擎实现报错求助:InvocationTargetException异常

问题排查:RuleBook引擎猜数字游戏运行报错

问题场景

尝试使用RuleBook引擎实现简单猜数字游戏逻辑,编写了RuleBookMain类代码,但运行main方法时抛出异常,程序无法正常执行。

代码实现

package com.example; // 替换为实际包名

import com.deliveredtechnologies.rulebook.FactMap;
import com.deliveredtechnologies.rulebook.NameValueReferableMap;
import com.deliveredtechnologies.rulebook.annotation.Given;
import com.deliveredtechnologies.rulebook.annotation.Rule;
import com.deliveredtechnologies.rulebook.annotation.Then;
import com.deliveredtechnologies.rulebook.annotation.When;
import com.deliveredtechnologies.rulebook.model.runner.RuleBookRunner;
import java.util.Random;

public class RuleBookMain {

  private static final int RANGE = 10;
  private static boolean numberGuessed;

  public static void main(String[] args) {
    Player p1 = new Player("Andrew");
    Player p2 = new Player("Anna");

    Guess startingGuess = new Guess(p1);

    NameValueReferableMap<Object> facts = new FactMap<>();
    facts.setValue("p1", p1);
    facts.setValue("p2", p2);
    facts.setValue("guess", startingGuess);

    while (!numberGuessed) {
      System.out.println("Facts before running rules: " + facts);
      // 每次循环创建新的RuleBookRunner,避免规则实例缓存导致的字段注入问题
      RuleBookRunner ruleBookRunner = new RuleBookRunner("com.example"); // 替换为实际包名
      ruleBookRunner.run(facts);
    }
  }

  @Rule(order = 1, halt = true) // 添加halt=true,触发后停止后续规则执行
  public static class NumberGuessedRule {
    @Given("p1")
    private Player p1;
    @Given("p2")
    private Player p2;
    @Given("guess")
    private Guess guess;

    @When
    public boolean when() {
      System.out.println("NumberGuessedRule.when: p1=" + p1 + ", p2=" + p2 + ", guess=" + guess);
      boolean result = (guess.number == p1.secret && !guess.author.equals(p1))
                       ||
                       (guess.number == p2.secret && !guess.author.equals(p2));
      System.out.println("NumberGuessedRule.when: result=" + result);
      return result;
    }

    // 修改参数类型为NameValueReferableMap<Object>,匹配RuleBook传入的类型
    @Then
    public void then(NameValueReferableMap<Object> facts) {
      System.out.println("NumberGuessedRule.then: p1=" + p1 + ", p2=" + p2 + ", guess=" + guess);
      Player guessedPlayer = guess.number == p1.secret ? p1 : p2;
      System.out.println("Number guessed! " + guessedPlayer + " is leaving the game.");
      numberGuessed = true;
    }
  }

  @Rule(order = 2)
  public static class WrongGuessRule {

    @Given("p1")
    private Player p1;
    @Given("p2")
    private Player p2;
    @Given("guess")
    private Guess guess;

    @When
    public boolean when() {
      // 修正条件逻辑:当前猜测未猜中对方的秘密
      return !((guess.number == p1.secret && !guess.author.equals(p1))
              || (guess.number == p2.secret && !guess.author.equals(p2)));
    }

    // 修改参数类型为NameValueReferableMap<Object>
    @Then
    public void then(NameValueReferableMap<Object> facts) {
      Player nextPlayer = guess.author.equals(p1) ? p2 : p1; // 切换到另一个玩家猜测
      Guess newGuess = new Guess(nextPlayer);
      facts.setValue("guess", newGuess);
      System.out.println(newGuess);
    }
  }


  private static int randomNumber() {
    return new Random().nextInt(RANGE);
  }



  static class Guess {

    public Player author;
    public int number;

    public Guess(Player author) {
      this.author = author;
      this.number = randomNumber();
    }

    @Override
    public String toString() {
      return author + " says '" + number + "'";
    }
  }



  static class Player {

    public int secret;
    public String name;

    public Player(String name) {
      this.name = name;
      this.secret = randomNumber();
    }

    @Override
    public String toString() {
      return name + " (secret=" + secret + ")";
    }
  }

}

错误信息

11:44:34.099 [main] ERROR com.deliveredtechnologies.rulebook.model.GoldenRule -- Error invoking action on class com.deliveredtechnologies.rulebook.model.runner.RuleAdapter$$Lambda$120/0x000001f8110823e8
java.lang.reflect.InvocationTargetException: null
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at com.deliveredtechnologies.rulebook.model.GoldenRule.lambda$invoke$8(GoldenRule.java:176)
    at java.base/java.util.Optional.ifPresent(Optional.java:178)

Caused by: java.lang.IllegalArgumentException: null

问题根源与解决方法

1. @Then方法参数类型不匹配

报错的直接原因是@Then方法的参数类型为FactMap<Object>,但RuleBook在调用该方法时传入的是NameValueReferableMap<Object>类型实例。反射调用时参数类型不匹配,触发IllegalArgumentException。

解决:将两个Rule类的then方法参数类型改为NameValueReferableMap<Object>。

2. 规则执行逻辑冲突

原NumberGuessedRule未设置halt=true,导致即使猜中数字,后续的WrongGuessRule仍会执行,引发逻辑混乱。

解决:为NumberGuessedRule的@Rule注解添加halt=true属性,触发后停止执行后续规则。

3. RuleBookRunner实例缓存导致字段注入失效

原代码在循环外创建RuleBookRunner,RuleBook会缓存规则类实例,导致@Given注解的字段无法重新注入最新的facts数据。

解决:在while循环内部每次创建新的RuleBookRunner实例,确保每次规则执行都能注入最新的facts。

4. WrongGuessRule的条件逻辑错误

原WrongGuessRule的when条件逻辑存在问题,会导致该规则始终被触发。修正为判断当前猜测未猜中对方的秘密。

解决:重新编写when方法的条件,确保只有未猜中时才触发。

5. 包名占位符未替换

原代码中package some package;和RuleBookRunner构造参数为占位符,RuleBook无法扫描到规则类。

解决:替换为实际的包名,确保规则类能被正确扫描。

内容的提问来源于stack exchange,提问作者Kalin Damyanov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 01:43:11