Drools集成SpringBoot

1.说明

为了更好的在项目中使用Drools,
需要把Drools集成到Spring Boot,
下面介绍集成的方法,
并且开发简单的Demo和测试用例。

2.创建Maven工程

pom.xml工程信息:


    4.0.0
    com.ai.prd
    drools-spring-boot-demo
    0.0.1-SNAPSHOT
    Drools integrats into the Spring Boot

3.引入Spring Boot相关依赖

引入spring-boot-starter-web作为Web工程,对外提供Rest服务,
引入spring-boot-starter-log4j2日志框架,打印测试匹配结果,
引入spring-boot-starter-test测试框架,开发Junt5测试用例:


    2.3.1.RELEASE



    
        
            org.springframework.boot
            spring-boot-dependencies
            ${spring-boot.version}
            pom
            import
        
    



    
        org.springframework.boot
        spring-boot-starter-web
        
            
                org.springframework.boot
                spring-boot-starter-logging
            
        
    
    
    
        org.springframework.boot
        spring-boot-starter-log4j2
    

    
        org.springframework.boot
        spring-boot-starter-test
        
            
            
                junit
                junit
            
            
                org.junit.vintage
                junit-vintage-engine
            
        
    

4.引入Drools相关依赖

通过kie-spring引入Drools相关的jar包,
其依赖的spring版本都排除掉,
以上一步的spring boot依赖为准。


    7.47.0.Final


    
        org.kie
        kie-spring
        ${drools.version}
        
            
            
                org.springframework
                spring-tx
            
            
                org.springframework
                spring-beans
            
            
                org.springframework
                spring-core
            
            
                org.springframework
                spring-context
            
        
    

5.开发启动类

启动类DroolsApplication.java:

package com.ai.prd.drools;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DroolsApplication {
    public static void main(String[] args) {
        SpringApplication.run(DroolsApplication.class, args);
    }
}

6.开发Rest服务

操作的对象Person,
Person.java:

package com.ai.prd.drools.entity;

public class Person {
    private String name;

    private int age;
}

对外提供的Rest服务,
可以对Person对象进行规则匹配,
提供了两个接口,
单个和批量的操作接口:
PersonRuleController.java:

package com.ai.prd.drools.controller;

import java.util.List;

import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.ai.prd.drools.entity.Person;

@RestController
@RequestMapping("rule/person")
public class PersonRuleController {

    @Autowired
    private KieContainer kieContainer;

    @PostMapping("one")
    public void fireAllRules4One(@RequestBody Person person) {
        KieSession kSession = kieContainer.newKieSession();
        try {
            kSession.insert(person);
            kSession.fireAllRules();
        } finally {
            kSession.dispose();
        }
    }

    @PostMapping("list")
    public void fireAllRules4List(@RequestBody List persons) {
        KieSession kSession = kieContainer.newKieSession();
        try {
            for (Person person : persons) {
                kSession.insert(person);
            }
            kSession.fireAllRules();
        } finally {
            kSession.dispose();
        }
    }
}

7.初始化Drools

在上面PersonRuleController中需要用到KieContainer,
这个必须在Spring中先初始化才能使用,
相关功能由DroolsAutoConfiguration.java提供:

package com.ai.prd.drools.config;

import java.io.IOException;

import org.kie.api.KieBase;
import org.kie.api.KieServices;
import org.kie.api.builder.KieBuilder;
import org.kie.api.builder.KieFileSystem;
import org.kie.api.builder.KieModule;
import org.kie.api.builder.KieRepository;
import org.kie.api.builder.ReleaseId;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.kie.internal.io.ResourceFactory;
import org.kie.spring.KModuleBeanFactoryPostProcessor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;

/**
 * 配置Drools的服务类,方便在Rest接口中调用。 该类负责加载具体的drl规则文件, 不再需要kmodule.xml配置文件了。
 */
@Configuration
public class DroolsAutoConfiguration {

    private static final String RULES_PATH = "rules/com/ai/prd/";

    @Bean
    @ConditionalOnMissingBean(KieFileSystem.class)
    public KieFileSystem kieFileSystem() throws IOException {
        KieFileSystem kieFileSystem = getKieServices().newKieFileSystem();
        for (Resource file : getRuleFiles()) {
            kieFileSystem.write(ResourceFactory.newClassPathResource(RULES_PATH + file.getFilename(), "UTF-8"));
        }
        return kieFileSystem;
    }

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

    @Bean
    @ConditionalOnMissingBean(KieContainer.class)
    public KieContainer kieContainer() throws IOException {
        final KieRepository kieRepository = getKieServices().getRepository();

        kieRepository.addKieModule(new KieModule() {
            @Override
            public ReleaseId getReleaseId() {
                return kieRepository.getDefaultReleaseId();
            }
        });

        KieBuilder kieBuilder = getKieServices().newKieBuilder(kieFileSystem());
        kieBuilder.buildAll();

        return getKieServices().newKieContainer(kieRepository.getDefaultReleaseId());
    }

    private KieServices getKieServices() {
        return KieServices.Factory.get();
    }

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

    // 不能反复被使用,释放资源后需要重新获取。
    // @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    @Bean
    @ConditionalOnMissingBean(KieSession.class)
    public KieSession kieSession() throws IOException {
        return kieContainer().newKieSession();
    }

    @Bean
    @ConditionalOnMissingBean(KModuleBeanFactoryPostProcessor.class)
    public KModuleBeanFactoryPostProcessor kiePostProcessor() {
        return new KModuleBeanFactoryPostProcessor();
    }
}

8.开发规则文件

在DroolsAutoConfiguration中指定了drl规则文件
所在目录rules/com/ai/prd/,
在src/main/resources/rules/com/ai/prd/目录下新建文件
ai-rules.drl:

package com.ai.prd
 
import com.ai.prd.drools.entity.Person;
import com.ai.prd.drools.action.PersonRuleAction;

// 根据名字匹配指定的人
rule "1.find target person"
    when
        $p : Person( name == "bob" )
    then
        PersonRuleAction.doParse($p, drools.getRule());
        System.out.println("Rule name is [" + drools.getRule().getName() + "]"); 
        System.out.println("Rule package is [" + drools.getRule().getPackageName() + "]");
end

// 根据年龄匹配找到打工人
rule "2.find the work person"
    when
        $p : Person( age >= 25 && age < 65 )      
    then
        System.out.println( $p + " is a work person!" );
end

规则1匹配名字为bob的人,并且调用工具类PersonRuleAction打印相关日志,
同时打印规则的名称和包路径到控制台。
规则2匹配年龄在25到65之间的打工人,
然后把匹配到的人直接打印到控制台。

9.规则处理类

PersonRuleAction.java在匹配到相应规则时被调用,
此处仅实现日志打印的功能:

package com.ai.prd.drools.action;

import org.drools.core.definitions.rule.impl.RuleImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.ai.prd.drools.entity.Person;

/**
 * 触发Person相关的规则后的处理类
 * 
 * @author yuwen
 *
 */
public class PersonRuleAction {
    private static Logger LOG = LoggerFactory.getLogger(PersonRuleAction.class);

    // 目前只实现记录日志功能
    public static void doParse(Person person, RuleImpl rule) {
        LOG.debug("{} is matched Rule[{}]!", person, rule.getName());
    }
}

10.新建日志配置文件

在src/main/resources目录下,
新建日志配置文件Log4j2.xml:



    
        
            
        
        
            
            
                
            
            
        
    
    
        
            
        
        
            
        
    

日志文件配置后,
PersonRuleAction类打印的日志
不仅会输出到log/rule_result.log,
也会输出到控制台。

11.开发Junit5测试用例

针对上面PersonRuleController提供的Rest接口,
开发两个Junit5的测试用例,
在src/test/java/目录下
创建PersonRuleControllerTest.java:

package com.ai.prd.drools.controller;

import java.util.Arrays;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import com.ai.prd.drools.entity.Person;

@SpringBootTest
public class PersonRuleControllerTest {

    @Autowired
    PersonRuleController controller;

    @Test
    public void testOnePerson() {
        Person bob = new Person();
        bob.setName("bob");

        controller.fireAllRules4One(bob);
    }

    @Test
    public void testTwoPerson() {
        Person bob = new Person();
        bob.setAge(33);

        Person other = new Person();
        other.setAge(88);

        controller.fireAllRules4List(Arrays.asList(bob, other));
    }
}

12.运行测试用例

PersonRuleControllerTest执行后,
控制台输出:

14:22:04.851 [main] WARN  org.drools.compiler.kie.builder.impl.KieBuilderImpl - File 'rules/com/ai/prd/ai-rules.drl' is in folder 'rules/com/ai/prd' but declares package 'com.ai.prd'. It is advised to have a correspondance between package and folder names.
14:22:06.753 [main] INFO  org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor - Initializing ExecutorService 'applicationTaskExecutor'
14:22:07.063 [main] INFO  com.ai.prd.drools.controller.PersonRuleControllerTest - Started PersonRuleControllerTest in 3.279 seconds (JVM running for 4.452)
14:22:07.279 [main] DEBUG com.ai.prd.drools.action.PersonRuleAction - Person [name=bob, birthDay=null, age=0, address=null] is matched Rule[1.find target person]!
Rule name is [1.find target person]
Rule package is [com.ai.prd]
Person [name=null, birthDay=null, age=33, address=null] is a work person!
14:22:07.649 [SpringContextShutdownHook] INFO  org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor - Shutting down ExecutorService 'applicationTaskExecutor'

13.错误解决

cannot be cast to org.drools.compiler.kie.builder.impl.InternalKieModule
大概率是rule规则文件有问题,
格式,中英文字符,语法等问题,
请确保规则文件正确。
可以安装相应插件打开规则文件,
请参考:
Drools的Eclipse_IDEA插件安装

14.参考文章

Drools规则引擎 系列教程(一)SpringBoot整合 & 快速集成上手
《Drools7.0.0.Final规则引擎教程》之Springboot集成
Drools创建Maven工程

你可能感兴趣的:(Drools集成SpringBoot)