easy-drools

不知不覺間,開源項目已經做了快兩年了。涉及的東西也越來越多。我始終堅持着一個念頭,持續學習。此生最慶幸的一件事,便是我所爲之工作的,正好是我喜歡的。
說下背景吧,公司剛好在用規則引擎。然後就想着,做一個東西出來,簡化開發。還是挺有意思的吧。

MAVEN引入

老規矩,還是先通過maven把easy-drools引入到項目中

	    <dependency>
			<groupId>io.github.xiaoyudeguang</groupId>
			<artifactId>easy-drools</artifactId>
			<version>4.0.30</version>
		</dependency>
		<!--日誌框架-->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-simple</artifactId>
		</dependency>
		<!--規則引擎依賴包-->
		<dependency>
			<groupId>org.drools</groupId>
			<artifactId>drools-core</artifactId>
			<version>7.35.0.Final</version>
		</dependency>
		<dependency>
			<groupId>org.kie</groupId>
			<artifactId>kie-spring</artifactId>
			<version>7.35.0.Final</version>
		</dependency>

1.JAVA轉規則

1.1 一段標準的Java代碼:

package com.zlyx.easy.start;

import java.util.Arrays;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.zlyx.easy.drools.builder.RuleBuilder;
import com.zlyx.easy.drools.domain.RuleContextDomain;
import com.zlyx.easy.drools.executor.RuleExecutor;
import com.zlyx.easy.drools.validate.RuleValidator;

/**
 * @prop message string 結束標記
 */
public class Rule  {

	/**
	 * rule
	 *
	 * @param context context
	 *
	 * @throws Exception
	 */
	public void rule(RuleContextDomain context) {
		Logger log = LoggerFactory.getLogger("規則引擎測試");
		try {
			List<String> list = Arrays.asList("a", "b");
			for (int i = 0; i < 6; i++) {
				System.out.println("第" + (i + 1) + "次輸出 ->" + list.get(i % 2));
			}
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		}
		log.info("{}", "規則引擎測試運行結束");
	}
}

沒什麼難度,相信大家一定都能看懂的。現在,我們的目標是把它轉化成一段drools規則引擎可以直接運行的腳本。

1.2 新建規則執行器(需要繼承RuleExecuto父類)

package com.zlyx.easy.start;

import java.util.Arrays;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.zlyx.easy.drools.domain.RuleContextDomain;

/**
 * @prop message string 結束標記
 */
public class Rule extends RuleExecutor<RuleContextDomain> {

	/**
	 * rule
	 *
	 * @param context context
	 *
	 * @throws Exception
	 */
	public void rule(RuleContextDomain context) {
		Logger log = LoggerFactory.getLogger("規則引擎測試");
		try {
			List<String> list = Arrays.asList("a", "b");
			for (int i = 0; i < 6; i++) {
				System.out.println("第" + (i + 1) + "次輸出 ->" + list.get(i % 2));
			}
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		}
		log.info("{}", "規則引擎測試運行結束");
	}

	@Override
	public RuleContextDomain initContext() throws Exception {
		// TODO Auto-generated method stub
		return new RuleContextDomain();
	}
}

可以看到,我們讓Rule類繼承了RuleExecutor父類,並聲明瞭泛型規則上下文類RuleContextDomain(可以替換爲業務中的實際上下文類)。同時,實現了一個initContext()方法,很明顯是用來準備上下文條件的。
好了,前期條件我們已經準備好了。現在,我們需要想辦法讓規則執行器跑起來。

1.3 啓動代碼

package com.zlyx.easy.start;

import java.util.Arrays;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.zlyx.easy.drools.domain.RuleContextDomain;
import com.zlyx.easy.drools.validate.RuleValidator;

/**
 * @prop message string 結束標記
 */
public class Rule extends RuleExecutor<RuleContextDomain> {

	/**
	 * rule
	 *
	 * @param context context
	 *
	 * @throws Exception
	 */
	public void rule(RuleContextDomain context) {
		Logger log = LoggerFactory.getLogger("規則引擎測試");
		try {
			List<String> list = Arrays.asList("a", "b");
			for (int i = 0; i < 6; i++) {
				System.out.println("第" + (i + 1) + "次輸出 ->" + list.get(i % 2));
			}
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		}
		log.info("{}", "規則引擎測試運行結束");
	}

	@Override
	public RuleContextDomain initContext() throws Exception {
		// TODO Auto-generated method stub
		return new RuleContextDomain();
	}

	public static void main(String[] args) throws Exception {
		RuleBuilder.RuleBody ruleBody = RuleBuilder.newBuilder("測試測試");
		ruleBody.when("$ctx: RuleContextDomain();"); // 規則條件
		ruleBody.then(Rule.class, "rule"); // 規則內容
		RuleValidator.validate(ruleBody, new Rule());
	}
}

到這裏,將一個普通的Java類轉換爲規則的準備工作就完成了。接下來,只需要執行代碼就可以了。

1.4 執行日誌


==============================================================================
輸出原始規則文本內容:

package com.zlyx.easy.start;

import com.zlyx.easy.drools.domain.RuleContextDomain;
import com.zlyx.easy.drools.builder.RuleBuilder;
import com.zlyx.easy.drools.domain.RuleContextDomain;
import com.zlyx.easy.drools.validate.RuleValidator;
import java.util.Arrays;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * @prop message string 結束標記
 */

rule "$rule_name"
when
    $ctx: RuleContextDomain();
then

    Logger log = LoggerFactory.getLogger("規則引擎測試");
    try {
        List<String> list = Arrays.asList("a", "b");
        for (int i = 0; i < 6; i++) {
            System.out.println("第" + (i + 1) + "次輸出 ->" + list.get(i % 2));
        }
    } catch (Exception e) {
        log.error(e.getMessage(), e);
    }
    log.info("{}", "規則引擎運行結束");
end
==============================================================================


輸出渲染後規則文本內容:

package com.zlyx.easy.start;

import com.zlyx.easy.drools.domain.RuleContextDomain;
import com.zlyx.easy.drools.builder.RuleBuilder;
import com.zlyx.easy.drools.domain.RuleContextDomain;
import com.zlyx.easy.drools.validate.RuleValidator;
import java.util.Arrays;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * @prop message string 結束標記
 */

rule "測試測試"
when
    $ctx: RuleContextDomain();
then

    Logger log = LoggerFactory.getLogger("規則引擎測試");
    try {
        List<String> list = Arrays.asList("a", "b");
        for (int i = 0; i < 6; i++) {
            System.out.println("第" + (i + 1) + "次輸出 ->" + list.get(i % 2));
        }
    } catch (Exception e) {
        log.error(e.getMessage(), e);
    }
    log.info("{}", "規則引擎運行結束");
end
==============================================================================


執行開始規則:

第1次輸出 ->a
第2次輸出 ->b
第3次輸出 ->a
第4次輸出 ->b
第5次輸出 ->a
第6次輸出 ->b
[main] INFO 規則引擎測試 - 規則引擎運行結束


規則執行完畢


==============================================================================

其中, "輸出原始規則文本內容"部分的代碼就是轉換並驗證腳本無錯誤後的Drools規則腳本。複製出來就可以直接交給Drools規則引擎執行了。

1.5 添加自定義屬性動態修改程序

接下里我們試試高級點的功能,對Rule類做一些修改:

package com.zlyx.easy.start;

import java.util.Arrays;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.zlyx.easy.drools.builder.RuleBuilder;
import com.zlyx.easy.drools.domain.RuleContextDomain;
import com.zlyx.easy.drools.validate.RuleValidator;

/**
 * @prop message string 結束標記
 */
public class Rule extends RuleExecutor<RuleContextDomain> {

	/**
	 * rule
	 *
	 * @param context context
	 *
	 * @throws Exception
	 */
	public void rule(RuleContextDomain context) {
		Logger log = LoggerFactory.getLogger("規則引擎測試");
		try {
			List<String> list = Arrays.asList("a", "b");
			for (int i = 0; i < 6; i++) {
				System.out.println("第" + (i + 1) + "次輸出 ->" + list.get(i % 2));
			}
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		}
		log.info("{}", "規則引擎測試運行結束");
	}

	@Override
	public RuleContextDomain initContext() throws Exception {
		// TODO Auto-generated method stub
		return new RuleContextDomain();
	}

	/**
	 *
	 * 自定義設置
	 *
	 * @param classBody
	 * @return
	 */
	public RuleBuilder.RuleBody doFilter(RuleBuilder.RuleBody ruleBody) {
		ruleBody.importClass(getTClass());
		ruleBody.placeholders("規則引擎測試運行結束", "$message");
		ruleBody.placeholders("6", "$count"); // 將常量替換爲$符號引用的變量(規則導出爲java的時候會用到)

		ruleBody.props("count", "10"); // 將$符號引用的替換爲變量常量(驗證規則的時候會用到)
		ruleBody.props("message", "程序正常結束"); // 將$符號引用的替換爲變量常量(驗證規則的時候會用到)
		return ruleBody;
	}

	public static void main(String[] args) throws Exception {
		RuleBuilder.RuleBody ruleBody = RuleBuilder.newBuilder("測試測試");
		ruleBody.when("$ctx: RuleContextDomain();"); // 規則條件
		ruleBody.then(Rule.class, "rule"); // 規則內容
		RuleValidator.validate(ruleBody, new Rule());
	}
}

1.6 自定義屬性動態修改程序執行日誌

改造完畢,再次執行:


==============================================================================
輸出原始規則文本內容:

package com.zlyx.easy.start;

import com.zlyx.easy.drools.domain.RuleContextDomain;
import com.zlyx.easy.drools.builder.RuleBuilder;
import com.zlyx.easy.drools.domain.RuleContextDomain;
import com.zlyx.easy.drools.validate.RuleValidator;
import java.util.Arrays;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * @prop message string 結束標記
 */

rule "$rule_name"
when
    $ctx: RuleContextDomain();
then

    Logger log = LoggerFactory.getLogger("規則引擎測試");
    try {
        List<String> list = Arrays.asList("a", "b");
        for (int i = 0; i < $count; i++) {
            System.out.println("第" + (i + 1) + "次輸出 ->" + list.get(i % 2));
        }
    } catch (Exception e) {
        log.error(e.getMessage(), e);
    }
    log.info("{}", "$message");
end
==============================================================================


輸出渲染後規則文本內容:

package com.zlyx.easy.start;

import com.zlyx.easy.drools.domain.RuleContextDomain;
import com.zlyx.easy.drools.builder.RuleBuilder;
import com.zlyx.easy.drools.domain.RuleContextDomain;
import com.zlyx.easy.drools.validate.RuleValidator;
import java.util.Arrays;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * @prop message string 結束標記
 */

rule "測試測試"
when
    $ctx: RuleContextDomain();
then

    Logger log = LoggerFactory.getLogger("規則引擎測試");
    try {
        List<String> list = Arrays.asList("a", "b");
        for (int i = 0; i < 10; i++) {
            System.out.println("第" + (i + 1) + "次輸出 ->" + list.get(i % 2));
        }
    } catch (Exception e) {
        log.error(e.getMessage(), e);
    }
    log.info("{}", "程序正常結束");
end
==============================================================================


執行開始規則:

第1次輸出 ->a
第2次輸出 ->b
第3次輸出 ->a
第4次輸出 ->b
第5次輸出 ->a
第6次輸出 ->b
第7次輸出 ->a
第8次輸出 ->b
第9次輸出 ->a
第10次輸出 ->b
[main] INFO 規則引擎測試 - 程序正常結束


規則執行完畢


==============================================================================

2.驗證規則腳本

2.1 項目下新增規則腳本文件

在src/main/resources下創建rule文件夾,在rule文件夾下創建rule.drl文件,將規則腳本複製進去。比如上面轉換出來的規則文件:

package com.zlyx.easy.start;

import com.zlyx.easy.drools.domain.RuleContextDomain;
import com.zlyx.easy.drools.builder.RuleBuilder;
import com.zlyx.easy.drools.domain.RuleContextDomain;
import com.zlyx.easy.drools.validate.RuleValidator;
import java.util.Arrays;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * @prop message string 結束標記
 */

rule "$rule_name"
when
    $ctx: RuleContextDomain();
then
    Logger log = LoggerFactory.getLogger("規則引擎測試");
    try {
        List<String> list = Arrays.asList("a", "b");
        for (int i = 0; i < $count; i++) {
            System.out.println("第" + (i + 1) + "次輸出 ->" + list.get(i % 2));
        }
    } catch (Exception e) {
        log.error(e.getMessage(), e);
    }
    log.info("{}", "$message");
end

2.2 新建規則執行器(需要繼承RuleExecutor父類)

package com.zlyx.easy.start;

import java.util.HashMap;

import com.zlyx.easy.drools.builder.RuleBuilder;
import com.zlyx.easy.drools.domain.RuleContextDomain;
import com.zlyx.easy.drools.validate.RuleValidator;

/**
 * 導入規則並驗證
 *
 * @Author 趙光
 * @Desc DroolsTest
 * @Date 2020年5月11日
 */
public class DroolsExecutor extends RuleExecutor<RuleContextDomain> {

	@Override
	public RuleContextDomain initContext() throws Exception {
		return new RuleContextDomain();
	}
	public static void main(String[] args) throws Exception {
		RuleValidator.validate("測試測試", new DroolsExecutor());
	}
}

2.3 執行DroolsExecutor類main方法,執行日誌:


==============================================================================
輸出原始規則文本內容:



import com.zlyx.easy.drools.domain.RuleContextDomain;
import java.util.Arrays;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *  @prop message string 結束標記
 */
rule "$rule_name"
when
    $ctx: RuleContextDomain();
then

        Logger log = LoggerFactory.getLogger("$rule_name");
	try {
		List<String> list = Arrays.asList("a", "b");
		for (int i = 0; i < 6; i++) {
			System.out.println("第" + (i + 1) + "次輸出 ->" + list.get(i % 2));
		}
	} catch (Exception e) {
		log.error(e.getMessage(), e);
	}
	log.info("{}", "$message");
end
==============================================================================


輸出渲染後規則文本內容:



import com.zlyx.easy.drools.domain.RuleContextDomain;
import java.util.Arrays;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *  @prop message string 結束標記
 */
rule "測試測試"
when
    $ctx: RuleContextDomain();
then

        Logger log = LoggerFactory.getLogger("測試測試");
	try {
		List<String> list = Arrays.asList("a", "b");
		for (int i = 0; i < 6; i++) {
			System.out.println("第" + (i + 1) + "次輸出 ->" + list.get(i % 2));
		}
	} catch (Exception e) {
		log.error(e.getMessage(), e);
	}
	log.info("{}", "$message");
end
==============================================================================


執行開始規則:

第1次輸出 ->a
第2次輸出 ->b
第3次輸出 ->a
第4次輸出 ->b
第5次輸出 ->a
第6次輸出 ->b
[main] INFO 測試測試 - $message


規則執行完畢


==============================================================================

同樣的,可以通過重寫RuleExecutor的doFilter()方法動態修改執行邏輯。

3. 規則文件轉JAVA類

3.1 項目下新增模板文件

在src/main/resources下創建rule文件夾,在rule文件夾下創建rule.vml文件,將下面的模板代碼複製進去:

package $package_name;

import com.zlyx.easy.drools.builder.RuleBuilder;
import com.zlyx.easy.drools.executor.RuleExecutor;
import com.zlyx.easy.drools.validate.RuleValidator;
$import_package 
public class $class_name extends RuleExecutor<$rule_context_domain> {

	/**
	 * $method_name
	 *
	 * @param context context
	 *
	 * @throws Exception
	 */
	public void $method_name($rule_context_domain context) {
		$method_body
	}
	
	@Override
	public $rule_context_domain initContext() throws Exception {
		return new $rule_context_domain();
	}

	public static void main(String[] args) throws Exception {
		RuleBuilder.RuleBody ruleBody = RuleBuilder.newBuilder("$rule_name");
		ruleBody.when("$ctx: $rule_context_domain();"); // 規則條件
		ruleBody.then($class_name .class, "$method_name"); // 規則內容
		//導入包
		ruleBody.importClass($rule_context_domain .class);
		ruleBody.importClass(Logger.class);
		ruleBody.importClass(LoggerFactory.class);
		//設置屬性
		ruleBody.props("message", "執行完成");
		RuleValidator.validate(ruleBody, new $class_name());
	}

}

3.2 新建規則執行器(需要繼承RuleExecutor父類)

package com.zlyx.easy.start;

import com.zlyx.easy.drools.domain.RuleContextDomain;
import com.zlyx.easy.drools.validate.RuleValidator;

/**
 * 導入規則並驗證
 *
 * @Author 趙光
 * @Desc DroolsTest
 * @Date 2020年5月11日
 */
public class DroolsExecutor extends RuleExecutor<RuleContextDomain> {

	@Override
	public RuleContextDomain initContext() throws Exception {
		return new RuleContextDomain();
	}

	public static void main(String[] args) throws Exception {
		RuleValidator.toJava("測試測試");
	}
}

執行代碼後就會在DroolsExecutor同級目錄下生成Rule.class文件。

3.3 生成Rule.class文件

package com.zlyx.easy.start;

import com.zlyx.easy.drools.builder.RuleBuilder;
import com.zlyx.easy.drools.executor.RuleExecutor;
import com.zlyx.easy.drools.validate.RuleValidator;
import com.zlyx.easy.drools.domain.RuleContextDomain;
import java.util.Arrays;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *  @prop message string 結束標記
 */ 
public class Rule extends RuleExecutor<RuleContextDomain> {

	/**
	 * rule
	 *
	 * @param context context
	 *
	 * @throws Exception
	 */
	public void rule(RuleContextDomain context) {
		Logger log = LoggerFactory.getLogger("$rule_name");
	try {
		List<String> list = Arrays.asList("a", "b");
		for (int i = 0; i < 6; i++) {
			System.out.println("第" + (i + 1) + "次輸出 ->" + list.get(i % 2));
		}
	} catch (Exception e) {
		log.error(e.getMessage(), e);
	}
	log.info("{}", "$message");
	}
	
	@Override
	public RuleContextDomain initContext() throws Exception {
		return new RuleContextDomain();
	}

	public static void main(String[] args) throws Exception {
		RuleBuilder.RuleBody ruleBody = RuleBuilder.newBuilder("規則引擎測試");
		ruleBody.when("$ctx: RuleContextDomain();"); // 規則條件
		ruleBody.then(Rule .class, "rule"); // 規則內容
		//導入包
		ruleBody.importClass(RuleContextDomain .class);
		ruleBody.importClass(Logger.class);
		ruleBody.importClass(LoggerFactory.class);
		//設置屬性
		ruleBody.props("message", "執行完成");
		RuleValidator.validate(ruleBody, new Rule());
	}

}

同樣的,可以通過重寫RuleExecutor的doFilter()方法動態修改執行邏輯。

然後呢,編輯ava代碼,java代碼生成規則並驗證,複製規則出來放到drools殷勤去執行,一條完整的生產線就出來啦。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章