rulebook 简单记录

总体:

  • 最后发版为2018年
  • 相比EASY RULE,不支持jexl等脚本语言
  • 支持Spring
  • 内部执行都是使用jdk proxy模式,无论是annotation还是builder模式
  • 可以使用CoRRuleBook 实现chain模式,在chain内按order进行执行
  • 一个rule里面可以有多个action(@Then),多个fact(@Given),多个condition(@When),但注意When只有一个会执行
  • 支持将一个java package的所有rule类组成一个Chain-CoRRuleBook
  • 支持audit

核心概念

Much like the Given-When-Then language for defining tests that was popularized by BDD, RuleBook uses a Given-When-Then language for defining rules. The RuleBook Given-When-Then methods have the following meanings.

  • Given some Fact(s)
  • When a condition evaluates to true
  • Then an action is triggered

核心类及示意代码

rule执行类:GoldenRule

/**
A standard implementation of {@link Rule}.
@param the fact type
@param the Result type
*/
public class GoldenRule implements Rule {

action 识别代码

public List<Object> getActions() {
  if (_rule.getActions().size() < 1) {
    List<Object> actionList = new ArrayList<>();
    for (Method actionMethod : getAnnotatedMethods(Then.class, _pojoRule.getClass())) {
      actionMethod.setAccessible(true);
      Object then = getThenMethodAsBiConsumer(actionMethod).map(Object.class::cast)
          .orElse(getThenMethodAsConsumer(actionMethod).orElse(factMap -> { }));
      actionList.add(then);
    }
    _rule.getActions().addAll(actionList);
  }
  return _rule.getActions();
}

condition 设置和获取,可以手工指定condition 如:auditableRule.setCondition(condition);
如果没有设置,使用如下代码识别

@Override
@SuppressWarnings("unchecked")
public Predicate<NameValueReferableMap> getCondition() {
  //Use what was set by then() first, if it's there
  if (_rule.getCondition() != null) {
    return _rule.getCondition();
  }

  //If nothing was explicitly set, then convert the method in the class
  _rule.setCondition(Arrays.stream(_pojoRule.getClass().getMethods())
          .filter(method -> method.getReturnType() == boolean.class || method.getReturnType() == Boolean.class)
          .filter(method -> Arrays.stream(method.getDeclaredAnnotations()).anyMatch(When.class::isInstance))
          .findFirst()

注意findFirst,代表只有一个生效

Chain 管理类 CoRRuleBook 使用Reflections 识别package下所有rule类的核心代码如下:

/**
   * Gets the POJO Rules to be used by the RuleBook via reflection of the specified package.
   * @return  a List of POJO Rules
   */
  protected List<Class<?>> getPojoRules() {
    Reflections reflections = new Reflections(_package);

    List<Class<?>> rules = reflections
        .getTypesAnnotatedWith(com.deliveredtechnologies.rulebook.annotation.Rule.class).stream()
        .filter(rule -> _package.equals(rule.getPackage().getName())) // Search only within package, not subpackages
        .filter(rule -> rule.getAnnotatedSuperclass() != null) // Include classes only, exclude interfaces, etc.
        .collect(Collectors.toList());

    rules.sort(comparingInt(aClass ->
        getAnnotation(com.deliveredtechnologies.rulebook.annotation.Rule.class, aClass).order()));

    return rules;
  }

Fact管理

/**
 * A Fact is a single piece of data that can be supplied to a {@link Rule}.
 * Facts are not immutable; they may be changed by rules and used to derive a result state.
 */
public class Fact<T> implements NameValueReferable<T> {
  private String _name;
  private T _value;


/**
 * A FactMap decorates {@link Map}; it stores facts by their name and provides convenience methods for
 * accessing {@link Fact} objects.
 */
public class FactMap<T> implements NameValueReferableMap<T> {

  private Map<String, NameValueReferable<T>> _facts;

  public FactMap(Map<String, NameValueReferable<T>> facts) {
    _facts = facts;
  }

实际上有点key重复的感觉,每个fact 本身还有name和value

Then/action 的执行核心代码 在goldenRule里面

//invoke the action
  Stream.of(action.getClass().getMethods())
          .filter(method -> method.getName().equals("accept"))
          .findFirst()
            .ifPresent(method -> {
              try {
                method.setAccessible(true);
                method.invoke(action,
                    ArrayUtils.combine(
                            new Object[]{new TypeConvertibleFactMap<>(usingFacts)},
                            new Object[]{getResult().orElseGet(() -> result)},
                            method.getParameterCount()));
                if (result.getValue() != null) {
                  _result = result;
                }
              } catch (IllegalAccessException | InvocationTargetException err) {
                LOGGER.error("Error invoking action on " + action.getClass(), err);
                if (_actionType.equals(ERROR_ON_FAILURE)) {
                  throw err.getCause() == null ? new RuleException(err) :
                      err.getCause() instanceof RuleException ? (RuleException)err.getCause() :
                          new RuleException(err.getCause());
                }
              }
            });
  facts.putAll(usingFacts);
}

多fact示意

fact 通过@Given进行命名

@Rule
public class HelloWorld {

  @Given("hello")
  private String hello;

  @Given("world")
  private String world;

然后复制

facts.setValue("hello", "Hello");
facts.setValue("world", "World");

Spring 使用

@Configuration
@ComponentScan("com.example.rulebook.helloworld")
public class SpringConfig {
@Bean
public RuleBook ruleBook() {
RuleBook ruleBook = new SpringAwareRuleBookRunner("com.example.rulebook.helloworld");
return ruleBook;
}
}

扩展

可以扩展rule类,参考RuleAdapter

public RuleAdapter(Object pojoRule, Rule rule) throws InvalidClassException {
  com.deliveredtechnologies.rulebook.annotation.Rule ruleAnnotation =
      getAnnotation(com.deliveredtechnologies.rulebook.annotation.Rule.class, pojoRule.getClass());

  if (ruleAnnotation == null) {
    throw new InvalidClassException(pojoRule.getClass() + " is not a Rule; missing @Rule annotation");
  }

  _actionType = ruleAnnotation.ruleChainAction();
  _rule = rule == null ? new GoldenRule(Object.class, _actionType) : rule;
  _pojoRule = pojoRule;
}

扩展RuleBook(The RuleBook interface for defining objects that handle the behavior of rules chained together.)

/**
   * Creates a new RuleBookRunner using the specified package and the supplied RuleBook.
   * @param ruleBookClass the RuleBook type to use as a delegate for the RuleBookRunner.
   * @param rulePackage   the package to scan for POJO rules.
   */
  public RuleBookRunner(Class<? extends RuleBook> ruleBookClass, String rulePackage) {
    super(ruleBookClass);
    _prototypeClass = ruleBookClass;
    _package = rulePackage;
  }

当然chain类也可以自己写。

其它

使用predicate的test 来判断condition 来
boolean test(T t)
Evaluates this predicate on the given argument.
Parameters:
t - the input argument
Returns:
true if the input argument matches the predicate, otherwise false

使用isAssignableFrom 来判断输入的fact 是否满足rule类里面的定义
isAssignableFrom是用来判断子类和父类的关系的,或者接口的实现类和接口的关系的,默认所有的类的终极父类都是
Object。如果
A.isAssignableFrom(B)结果是true,证明
B可以转换成为
A,也就是
A可以由
B转换而来

你可能感兴趣的:(java,架构,rulebook,java,rule,engine)