总体:
- 最后发版为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<T, U> implements Rule<T, U> {++
action 识别代码
java
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);
如果没有设置,使用如下代码识别
java
@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类的核心代码如下:
java
/**
* 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管理
java
/**
* 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里面
java
//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进行命名
java
@Rule
public class HelloWorld {
@Given("hello")
private String hello;
@Given("world")
private String world;
然后复制
java
facts.setValue("hello", "Hello");
facts.setValue("world", "World");
Spring 使用
java
@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
java
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.)
java
/**
* 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转换而来