drools规则

最近设计一个项目,需要:从数据库取一堆数据,作为输入,然后经过N个公式得出N个结论。

最开始考虑用代码设计一组规则,用面向对象的思维,需要抽象出一个接口,然后根据不同的规则公式,实现接口。当公式面临频繁更改时,这种方式很bug,需要频繁派生不同实现类。

Drools则可以解决该问题。

  • drools注入到Springboot,分为读取本地drl文件和从数据库获取drools2种方式。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
@Configuration
public class RuleEngineConfig {
    @Autowired
    private DatabaseService databaseService;
    @Autowired
    private RulesConfigration rulesConfigration;

    private static final String rulesPath = "src/main/resources/rules/rule.drl";
    private static final KieServices kieServices = KieServices.Factory.get();

    private Resource[] getRuleFiles() throws IOException {
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        return resolver.getResources("classpath*:" + rulesPath + "**/*.*");
    }

    @Bean
    @ConditionalOnMissingBean(KieFileSystem.class)
    public KieFileSystem kieFileSystem() throws IOException{
        KieFileSystem kieFileSystem = kieServices.newKieFileSystem();
//        Resource[] resources = this.getRuleFiles();
//        for(Resource resource:resources){
//            String path = rulesPath + resource.getFilename();
//            kieFileSystem.write(ResourceFactory.newClassPathResource(path,"UTF-8"));
//        }
        String rules =  databaseService.getCriteria(rulesConfigration.version,rulesConfigration.company);
;       kieFileSystem.write(rulesPath,rules);
        return kieFileSystem;
    }

    @Bean
    @ConditionalOnMissingBean(KieContainer.class)
    public KieContainer kieContainer() throws IOException{
        KieRepository kieRepository = kieServices.getRepository();
        kieRepository.addKieModule(() -> kieRepository.getDefaultReleaseId());
        KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem());
        kieBuilder.buildAll();
        return kieServices.newKieContainer(kieRepository.getDefaultReleaseId());
    }

    @Bean
    @ConditionalOnMissingBean(KieSession.class)
    public KieSession kieSession() throws IOException{
        return kieContainer().newKieSession();
    }

    @Bean
    @ConditionalOnMissingBean(KieBase.class)
    public KieBase kieBase() throws IOException{
        return kieContainer().getKieBase();
    }

    @Bean
    @ConditionalOnMissingBean(KModuleAnnotationPostProcessor.class)
    public static KModuleAnnotationPostProcessor kiePostProcesser(){
        return new KModuleAnnotationPostProcessor();
    }

}
  • 设计过滤器,对规则进行过滤,可以根据该改过滤器过滤规则,也可以手动agenda分组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class RulesFilter implements AgendaFilter {
private String filterName;

public RulesFilter(String filterName){
this.filterName = filterName;
}

@Override
public boolean accept(Match match) {
Map metadataMap = match.getRule().getMetaData();
String version = metadataMap.get("version").toString();
return version.equals(filterName);
}
}
  • 设计fact

fact即drools的输入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Data
@JsonDeserialize (using = MyJsonDeseriablizer.class)
public class Company implements Serializable {
@JsonProperty("ID")
private int id;
@JsonProperty("NAME")
private String name;

@JsonIgnore
private int age;
@JsonProperty("SCORE")
private BigDecimal score;
@JsonProperty("MALE")
private boolean isMale;
@JsonProperty("RATING")
private Rating rating;
}
  • 执行规则
1
2
kieSession.insert(company);
int count = kieSession.fireAllRules(new RulesFilter(rulesVersion));
  • 规则文件drl

规则文件分为3部分:

  1. 属性

drools 有自己的属性,如sailence,值越大,执行优先级越高;lock-on-active –true 表示该条规则是否有效;no-loop—在更新fact时候,不为 true的规则会重新执行,但是LHS中必须是fact的成员变量,不能使用成员方法,否则不会触发RHS执行。

另外,可以添加 metadata,使用方法 — @version (Haier-2019-V1)

  1. LHS

LHS 即条件部分,使用 when 语句

多个条件在LHS时,隔行即可,无需使用or等字段。

global 和 Java中相同,可以作为drl的全局参数使用。

  1. RHS

RHS 即结果部分,使用 then语句

除此以外,全局变量在规则文件中的作用和java代码中的作用差不多,我们可以让它承担规则文件代码和java代码之间沟通的桥梁

global funstion metadata packages

1
2


点击并拖拽以移动

1
2


点击并拖拽以移动