Java RuleBook引擎实现报错求助:InvocationTargetException异常
问题场景
尝试使用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

