Spring Boot + rule engine URule, too strong!

Click to follow the official account, Java dry goodsdelivered in time

Source: juejin.cn/post/

7210194936276680759

Some time ago, when I was doing project refactoring, I encountered a lot of conditions that needed to be judged in many places. Of course, a lot of if-else judgments can be used to solve it, but I didn’t know what was going on at the time, so I wanted to play something else. Ever since, I went to investigate the rule engine.

Of course, there are many mature rule engines on the market with many functions and good performance. However, I just want to play something different (don’t do this when you do technology selection, this is a negative teaching material). In the end, a rule engine of URule attracted me, mainly because it can be directly configured by using a browser, without too much installation, and the visual rules are also well done. After a series of research, it was integrated into the project later, and the results of the research were recorded by the way.

1. Introduction

A rule engine is actually a component that can be embedded into a program. The complex judgment rules of the program are separated from the business code, so that the program only needs to care about its own business, and does not need to make complex logical judgments; the simple understanding is that the rules accept a set of input data and are configured through predetermined rules , and output a set of results.

Of course, there are many mature rule engines on the market, such as: Drools, Aviator, EasyRules, etc. But URule, which can run on various types of operating systems such as Windows, Linux, and Unix, adopts a pure browser editing mode, does not need to install tools, and directly edits and tests rules on the browser.

Of course, this rule engine has the difference between the open source version and the pro version. As for what the pro version is, everyone understands it. Let’s put a table below to understand the specific differences.

64908efe68a10c205f8d46a0d8cd3631.png
f8cd7fb00814b686bb2b848ccb2d3486.png
3537026205e41efa37560266444fcb6a.png

2. Installation and use

In actual use, there are four ways to use URule Pro, namely embedded mode, local mode, distributed computing mode and independent service mode.

But we don’t consider URule Pro here. Our own open source version is a secondary development based on the integration of springboot in the open source version. After searching around, there is actually a solution. The general project modules are as follows:

d5343f8993036fc9188c05d82b519c0b.jpeg

To create an empty database by yourself, you only need to modify the configuration of the database in the edas-rule-server service, and then start the service. After the first startup is complete, a table will be created in the database.

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/urule-data?serverTimezone=Asia/Shanghai &useUnicode=true &characterEncoding=utf-8 &allowMultiQueries=true &useSSL= false
spring.datasource.username=root
spring.datasource.password=mysql

As mentioned above, it is purely using a browser to edit and configure rules. You only need to open the browser and enter the address: http://localhost:8090/urule/frame. When you see this interface, it means that the startup is successful.

aa6f32c7a4e62e1cff1bc0dae64a3666.jpeg

3. Basic concepts

3.1 Overall introduction

Let me talk about the components of URule, mainly two parts: 1. Designer part 2. Rule execution engine. The designer part is mainly composed of library files and rule files. Let’s look at the overall structure diagram

600143af8397918f1555eadb1a5dce92.jpeg

3.2 Library file

As shown in the figure above, there are 4 types of library files, including variable library, parameter library, constant library and action library. In fact, it is similar to the entity objects, enumerations, constants and methods in the system developed by Java.

As mentioned above, the rules are configured visually. In the process of configuring rules, it is necessary to introduce various defined library files, and then combine business requirements to configure business rules that meet business scenarios, so there are library files everywhere.

3.2.1 Variable library file

In business development, we will create many Getter and Setter Java classes, such as PO, VO, BO, DTO, POJO, etc. In fact, these new objects are mainly used as data carriers to transmit data.

In URule, the variable library is used to map these objects, which can then be used in rules, and finally complete the interaction between business and rules. The last picture is used to create a variable library

ad6d6b5bb510a0f5db3ec11c588a6993.jpeg

By the way, there are so many visual configurations in the above nonsense, this is the first time to show the configuration interface, ashamed.

The above picture is clear at a glance. Right click under the “Library” menu, then click Add variable library, and finally define the name of the variable library you like. Of course, the name only supports Chinese or English, and other characters are not available.

fba5636f19ee41889f186e1de3d30843.jpeg After creating the variable library, you can edit the variable library edit, yes Think it is to add attributes to POJO

FC95D8FF5BC6EE959590B5555 7FD3.JPEG I understand it without a bending. On the left side of the figure is the creation class, where name is its alias, and the configuration rule uses it to replace this class. On the right side of the picture are the attributes of the class. I have written a few casually here, and I guess I can understand it after reading it.

Finally, create the corresponding class in the business system. Note that the fully qualified name is consistent with the class path of the configuration variable library.

package com.cicada;

import com.bstek.urule.model.Label;
import lombok.Data;

/**
 * @author Taro source code
 * @version 1.0
 * @date 2023/3/3 15:38
 * @description
 */
@Data
public class Stu {

    @Label("Name")
    private String name;

    @Label("age")
    private int age;

    @Label("class")
    private String classes;
}

Finally, let’s talk about the @Label annotation. This annotation is provided by URule. It mainly describes the attributes of the field, and it should be consistent with the title column of the variable library. Listening to the official introduction, you can use this annotation to realize the mapping between POJO attributes and variable library attributes. That is, the POJO is written, and then the variable library corresponding to the rules does not need to be rewritten, and can be generated directly. Anyway, there is this function, and it is directly mentioned here.

3.2.2 Constant library file

Speaking of constant libraries, this can be considered as constants and enumerations in our Java system. For example, for gender, you should define an enumeration; for example, for a docking organization, you can also define an enumeration.

Of course, similar to the variable library, the constant library can also be mapped to the enumeration in the system. The advantage of this is that we can avoid manual input and prevent input errors. It is also relatively simple to create a constant library, just right-click under the “Library” menu and select “Add Constant Library”.

After the constant library file is created, the following page will also appear:

8df554d843fd8f18f727d4e03ac2668f.jpeg

3.2.3 Parameter library file

The parameter library is a temporary variable in the URule rule, and the type and quantity of the variable are not fixed. It can be thought of as similar to a Map. In fact, it is a Map that stores the parameter library.

In the same way, right-click directly under the “Library” menu and select “Add Parameter Library”.

e465707e3ec33fb850972f078a87ad24.jpeg As you can see, the parameter library is missing on the left Categorize this item, Add parameters directly, select the type and do it, which is relatively simple. I use English for the “Name” column, which is the key in the Map, and the “Title” column is for display when configuring rules. Chinese is more intuitive.

Of course, it is also important to note that the defined name must be unique, because the key in the Map is unique, otherwise there will be coverage.

3.2.4 Action library file

The action library can map the bean methods configured in the spring, and then these methods can be directly called in the rules. For the usual routine, right click under the “Library” menu and click “Add Action Library”.

a0a114db8a2c5a4e4cdc368845146342.jpeg Then I added a Class Action, then mark the @Component annotation on the class, and hand over the class to the spring bean container for management. Add some methods to this class, and mark the @ExposeAction annotation on the method. This annotation is defined by URule, indicating that the marked methods will be read by the action library.

package com.bstek.urule.cicada;

import com.bstek.urule.action.ActionId;
import com.bstek.urule.model.ExposeAction;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author Taro source code
 * @version 1.0
 * @date 2023/3/10 13:59
 * @description
 */
@Component("action")
public class Action {

    @ActionId("Hello")
    public String hello(){
        return "hello";
    }

    @ExposeAction(value="Method 1")
    public boolean evalTest(String username){
        if(username==null){
            return false;
        }else if(username.equals("Zhang San")){
            return true;
        }
        return false;
    }

    @ExposeAction(value="TestInt")
    public int testInt(int a,int b){
        return a + b;
    }

    @ExposeAction(value="print content")
    public void printContent(String username, Date birthday){
        SimpleDateFormat sd=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        if(birthday!=null){
            System.out.println(username + "this year already" + sd.format(birthday) + "year old!");
        }else{
            System.out.println("Hello " + username + "");
        }
    }
    
    @ExposeAction(value="Print Stu")
    public void printUser(Stu m){
        System.out.println("Hello " + m.getName() + ", is age:" + m.getAge());
    }
}

Finally, add a bean on the action library page, enter the name of the corresponding spring bean in the “Bean Id” column, and enter action here. Then click the small hand button in the operation column, and the method that has just marked the ExposeAction annotation in the Action class will pop up. Select a specified method to add in, and finally see that the parameters corresponding to the method will be automatically loaded in.

1e76f96408b39429ff29281b9334f926.jpeg
71fa13b7f5e4d1efad7d239d933e9d19.jpeg

Finally, after the library files such as variable library, parameter library, action library, and constant library are defined, they can be imported when configuring various rule files. But once these library files are used by a rule file, don’t modify the library files at will.

3.3 Rule Set

When it comes to rule sets, as the name suggests, they are configuration rules. The library files defined above need to be imported into the rule set for configuration and use. It is the most frequently used business rule implementation.

A rule set refers to a collection of rules and consists of three part rules: if, then, otherwise.

In terms of the definition of the rule set, URule is divided into two types: wizard type and script type;

  • Wizard-style rule set: It is a highly visual configuration with a click of the mouse on the page, which not only can be understood by developers, but also the highlight of this rule engine.

  • Scripted rule set: You can tell by the name, this thing needs to be scripted. Raising the configuration threshold requires someone who knows a little bit of coding to write it.

3.3.1 Wizard-style rule set

Still the same, create a new one first. This time, right-click on the “Decision Set” menu and click “Add Wizard Decision Set” to create a rule set.

71a1c71633669be4dde9c7b622f6275f.jpeg Before configuring rules, you can import the previous definitions Good library file. I import the variable library file here, click “Variable Library” on the page, and then select the specified variable library file. as the picture shows;

787c8ecdfdc954ab99e55263dc7983ae.jpeg Finally, you can configure the rules happily, the wizard style has nothing to say , are all visual interfaces, just a little bit. Below is a simple ruleset that I configured;

3e6de58250DEFA7510172F7D 4df1.jpeg You can see consisting of three parts: if, then, otherwise;

  1. If: configure the condition of the rule;

  2. Then: Configure the actions to be executed after the conditions are met. Generally, there are more assignments of configuration variables

  3. Otherwise: Configure actions that do not meet the conditions

Finally, after adding the rules, execute the rules through the code;

package com.cicada;

import cn.hutool.core.bean.BeanUtil;
import com. Result;
import com.bstek.urule.Utils;
import com.bstek.urule.runtime.KnowledgePackage;
import com.bstek.urule.runtime.KnowledgeSession;
import com.bstek.urule.runtime.KnowledgeSessionFactory;
import com.bstek.urule.runtime.service.KnowledgeService;
import com.cicada.req.StuReq;
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 java.io.IOException;

/**
 * @author Taro source code
 * @version 1.0
 * @date 2023/3/10 16:47
 * @description
 */
@RestController
@RequestMapping("/rule")
public class RuleDataController {

    @PostMapping("/stu")
    public Result rule(@RequestBody StuReq stuReq) throws IOException {
        KnowledgeService knowledgeService = (KnowledgeService) Utils.getApplicationContext().getBean(KnowledgeService.BEAN_ID);
        KnowledgePackage knowledgePackage = knowledgeService.getKnowledge("xxx/xxx");
        KnowledgeSession knowledgeSession = KnowledgeSessionFactory. newKnowledgeSession(knowledgePackage);
        Stu stu = BeanUtil. copyProperties(stuReq, Stu. class);
        knowledgeSession.insert(stu);
        knowledgeSession.fireRules();
        return Result. success(stu. getTeacher());
    }
}

f02df4941b7fc7769e8d3c0b81d96292.jpeg

The request interface, the final parameter meets the configured conditions, and the output result configured in “then” is returned.

3.3.2 Scripted rule sets

The principles of the script-style rule set are exactly the same as those of the wizard-style. It is nothing more than raising the threshold and implementing the configuration rules by writing scripts. I won’t introduce too much here.

3.4 Decision table

Let’s talk about the decision table. In fact, it is another display form of the rule set. Compared with the rule set, I prefer to use the decision table to configure the rules, because it is more intuitive and easier to understand. But the essence is no different from the ruleset.

Without going into too much detail, here I will put a configured decision table;

585a9b0d2e55764f4d689dc77ebcc50a.jpeg

3.5 Other

Of course, there are other concepts and functions, and I won’t introduce them one by one here, because the above mentioned are already the most commonly used ones, and those who want to know can learn by themselves. Other features include: cross-decision tables, scorecards, complex scorecards, decision trees, rule flow; of course, some of these are features of the Pro version.

4. Application scenarios

Recently, I am developing a large-scale version of the requirements, and there is a scenario, as follows; users who participate in the purchase order will have their own rank, which can also be called a role. Each user will have three positions: ordinary user, member, and elite member.

Then, at the beginning of each month, users will be promoted. Ordinary users will be promoted to members if they meet the requirements, and members will be promoted to elite members if they meet the requirements.

Of course, there will be different rules for ordinary users to be promoted to members, and members to be promoted to elite members;

  1. Ordinary users -> members: within 3 months, the number of registered members has reached 3; within 3 months, the order amount of myself and the people under the team has exceeded 10,000; the personal order continuation rate has exceeded 80%.

  2. Member -> Elite Member: The number of registered members reached 6 within 3 months; within 3 months, the order amount of myself and the people under the team exceeded 50,000; the personal order continuation rate exceeded 90%.

  3. Cannot be promoted across levels. Ordinary users can only reach membership at most, and can be promoted to elite members only after reaching membership.

Of course, this is just a simplified part of the requirements. I have made some changes. The real requirement scenario is not so simple.

Next, I make a rule configuration for this requirement, here I use a decision table for configuration; before configuring the rules, I add a variable library file and a constant library;

5528c37e2b4d6a32dadead810e94efb4.jpeg
b0b07f07e3937a950a67136bb1849c12.jpeg

Finally, add a decision table and configure the rules;

aa3b56bfdeda7e66514d25cf422823f2.jpeg

It can be seen that the table has five columns in total, of which the first four columns are rules, and the last column is the output information after satisfying the rules. In this way, it is very clear to look at, even if you are not a technical person, you can easily understand the rules.

5. Summary

The rule engine is available or not for our system, it can be icing on the cake and help us separate out the scenarios that require a lot of judgment in the business. However, this stripping of rules requires our developers to understand the requirements, and on the basis of understanding, we need to make abstract concepts concrete. This is also the must-pass of the entire programming.


Popular content:

  • Are you still writing join table queries by hand? MyBatis-Plus is so delicious!

  • Spring Boot + flowable quickly implement workflow

  • Use waste, use your old computer to build a server!

  • Meituan’s dynamic thread pool is really fragrant!

  • Controller layer code should be written like this, concise and elegant!


81324b6fc7f5362c519c5b18317d3d0d.jpeg


I recently interviewed BAT, and compiled an interview material "Java Interview BAT Clearance Manual", covering Java core technology, JVM, Java concurrency, SSM, microservices, databases, data structures, etc.
How to get it: Click "Watching", follow the official account and reply to 666 to get it, and more content will be provided one after another. 

See you tomorrow (ω)