zoukankan      html  css  js  c++  java
  • easy-rules spring boot 一个简单的starter

    以下是一个简单的包装的一个easy-rules spring boot starter,以及使用说明

    easy-rules spring boot starter 项目

    • 说明
      就是一个简单的spring boot starter,包装了easy rules 同时基于配置文件进行rule 的加载,注意此版本使用了4.0 de snapshot
      (使用了beanresolver),当前版本只处理了基于本地文件的加载模式,以及对于spel expression 的支持(因为可以更好的集成
      spring)
    • pom.xml
     
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>com.github.rongfengliang</groupId>
        <artifactId>easy-rules-spring-boot-starter</artifactId>
        <version>1.0-SNAPSHOT</version>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starters</artifactId>
            <version>2.2.3.RELEASE</version>
        </parent>
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <encoding>UTF-8</encoding>
            <java.version>1.8</java.version>
            <maven.compiler.source>1.8</maven.compiler.source>
            <maven.compiler.target>1.8</maven.compiler.target>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
            <dependency>
                <groupId>org.jeasy</groupId>
                <artifactId>easy-rules-core</artifactId>
                <version>4.0.0-SNAPSHOT</version>
            </dependency>
            <dependency>
                <groupId>org.jeasy</groupId>
                <artifactId>easy-rules-mvel</artifactId>
                <version>4.0.0-SNAPSHOT</version>
            </dependency>
            <dependency>
                <groupId>org.jeasy</groupId>
                <artifactId>easy-rules-spel</artifactId>
                <version>4.0.0-SNAPSHOT</version>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-simple</artifactId>
                <version>1.7.25</version>
            </dependency>
        </dependencies>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-source-plugin</artifactId>
                    <configuration>
                        <attach>true</attach>
                    </configuration>
                    <executions>
                        <execution>
                            <phase>package</phase>
                            <goals>
                                <goal>jar-no-fork</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </project>
    • starter spi 配置
      src/main/resources/META-INF/spring.factories
     
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=
    com.github.rongfengliang.EasyRulesAutoConfiguration
    • starter 核心入口
      EasyRulesAutoConfiguration.java 此文件比较简单,主要是暴露一个通用的bean,以及基于配置生成spring bean
     
    package com.github.rongfengliang;
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.jeasy.rules.api.RuleListener;
    import org.jeasy.rules.api.Rules;
    import org.jeasy.rules.api.RulesEngine;
    import org.jeasy.rules.api.RulesEngineListener;
    import org.jeasy.rules.core.DefaultRulesEngine;
    import org.jeasy.rules.core.RulesEngineParameters;
    import org.jeasy.rules.spel.SpELRuleFactory;
    import org.jeasy.rules.support.JsonRuleDefinitionReader;
    import org.jeasy.rules.support.YamlRuleDefinitionReader;
    import org.springframework.beans.factory.config.ConfigurableBeanFactory;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Scope;
    import org.springframework.expression.BeanResolver;
    import java.io.FileReader;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.function.Consumer;
    @Configuration
    @EnableConfigurationProperties(EasyRulesEngineConfiguration.class)
    public class EasyRulesAutoConfiguration {
        Log log = LogFactory.getLog(EasyRulesAutoConfiguration.class);
        private final EasyRulesEngineConfiguration properties;
        EasyRulesAutoConfiguration(EasyRulesEngineConfiguration properties){
            this.properties=properties;
        }
        @Bean
        @ConditionalOnMissingBean
        public RuleListener defaultRulesListener(){
            return new DefaultRulesListener();
        }
        @Bean
        @ConditionalOnMissingBean
        public RulesEngineListener defaultRuleEngineListener(){
            return new DefaultRuleEngineListener();
        }
        @Bean
        @ConditionalOnMissingBean
        public BeanResolver defaultedResolver(SpringBeanUtil springBeanUtil){
            return new SimpleBeanResovler(SpringBeanUtil.getApplicationContext());
        }
        @Bean
        @ConditionalOnMissingBean
        public SpringBeanUtil springBeanUtil(){
            return new SpringBeanUtil();
        }
        /**
         * 获取配置额规则列表
         *
         * @param beanResolver spring beanResolver
         * @return Map<String,Rules>
         * @throws Exception
         */
        @Bean
        public Map<String,Rules> configRules(BeanResolver beanResolver) throws Exception {
            Map<String,Rules> rules = new HashMap<>();
            this.properties.getRules().forEach(new Consumer<RulesConfig>() {
                @Override
                public void accept(RulesConfig rulesConfig) {
                  switch (rulesConfig.getContentType()){
                      case JSON:
                          SpELRuleFactory jsonRuleFactory = new SpELRuleFactory(new JsonRuleDefinitionReader(),beanResolver);
                          Rules jsonRules = null;
                          try {
                              jsonRules = jsonRuleFactory.createRules(new FileReader(this.getClass().getClassLoader().getResource(rulesConfig.getRulesLocation()).getFile()));
                          } catch (Exception e) {
                              e.printStackTrace();
                          }
                          rules.put(rulesConfig.getRulesId(),jsonRules);
                          break;
                      case YAML:
                           SpELRuleFactory yamlRuleFactory = new SpELRuleFactory(new YamlRuleDefinitionReader(),beanResolver);
                           Rules yamlRules = null;
                          try {
                              yamlRules = yamlRuleFactory.createRules(new FileReader(this.getClass().getClassLoader().getResource(rulesConfig.getRulesLocation()).getFile()));
                          } catch (Exception e) {
                              e.printStackTrace();
                          }
                          rules.put(rulesConfig.getRulesId(),yamlRules);
                          break;
                      default:
                          throw new IllegalStateException("Unexpected value: " + rulesConfig.getContentType());
                  }
                }
            });
            return rules;
        }
        /**
         * 为了安全使用原型模式
         * @param defaultRulesListener
         * @param defaultRuleEngineListener
         * @return RulesEngine
         */
        @Bean
        @ConditionalOnMissingBean(RulesEngine.class)
        @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
        public RulesEngine rulesEngine(RuleListener defaultRulesListener, RulesEngineListener defaultRuleEngineListener) {
            log.info("create rule Engine");
            RulesEngineParameters parameters = new RulesEngineParameters();
            if(this.properties.getPriorityThreshold()>0){
                parameters.setPriorityThreshold(this.properties.getPriorityThreshold());
            }
            if(this.properties.isSkipOnFirstAppliedRule()){
                parameters.setSkipOnFirstAppliedRule(this.properties.isSkipOnFirstAppliedRule());
            }
            if(this.properties.isSkipOnFirstFailedRule()){
                parameters.setSkipOnFirstFailedRule(this.properties.isSkipOnFirstFailedRule());
            }
            if(this.properties.isSkipOnFirstNonTriggeredRule()){
                parameters.setSkipOnFirstNonTriggeredRule(this.properties.isSkipOnFirstNonTriggeredRule());
            }
            DefaultRulesEngine rulesEngine = new DefaultRulesEngine(parameters);
            rulesEngine.registerRuleListener(defaultRulesListener);
            rulesEngine.registerRulesEngineListener(defaultRuleEngineListener);
            return rulesEngine;
        }
    }
     
     
    • EasyRulesEngineConfiguration
      EasyRulesEngineConfiguration.java
      easy rules 配置,spring boot 在启动的时候就基于此配置生成ruleEngine 以及加载定义好的rule 文件
     
    package com.github.rongfengliang;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import java.util.List;
    @ConfigurationProperties(prefix = "easyrules")
    public class EasyRulesEngineConfiguration {
        private boolean skipOnFirstAppliedRule;
        private boolean skipOnFirstNonTriggeredRule;
        private boolean skipOnFirstFailedRule;
        private int priorityThreshold;
        private List<RulesConfig> rules;
        public boolean isSkipOnFirstAppliedRule() {
            return skipOnFirstAppliedRule;
        }
        public void setSkipOnFirstAppliedRule(boolean skipOnFirstAppliedRule) {
            this.skipOnFirstAppliedRule = skipOnFirstAppliedRule;
        }
        public boolean isSkipOnFirstNonTriggeredRule() {
            return skipOnFirstNonTriggeredRule;
        }
        public void setSkipOnFirstNonTriggeredRule(boolean skipOnFirstNonTriggeredRule) {
            this.skipOnFirstNonTriggeredRule = skipOnFirstNonTriggeredRule;
        }
        public boolean isSkipOnFirstFailedRule() {
            return skipOnFirstFailedRule;
        }
        public void setSkipOnFirstFailedRule(boolean skipOnFirstFailedRule) {
            this.skipOnFirstFailedRule = skipOnFirstFailedRule;
        }
        public int getPriorityThreshold() {
            return priorityThreshold;
        }
        public void setPriorityThreshold(int priorityThreshold) {
            this.priorityThreshold = priorityThreshold;
        }
        public List<RulesConfig> getRules() {
            return rules;
        }
        public void setRules(List<RulesConfig> rules) {
            this.rules = rules;
        }
    }
     
    • 规则配置文件格式说明
      为了方便使用基于yaml配置,同时基于规则id关联规则文件,方便对规则管理
     
    easyrules:
      skipOnFirstAppliedRule: false
      skipOnFirstNonTriggeredRule: false
      priorityThreshold: 1000000
    // 多规则加载,基于rulesId 可以区分不同的业务,基于contentType 标识文件类型
      rules:
      - rulesId: "userlogin"
        rulesLocation: "rules-json.json"
        contentType: JSON
    • 处理规则的源码
      上边已经包含了,下边说明下
      数据返回的是一个hashmap,我们可以通过get方式,获取key 对应的规则,可以灵活选择
      同时支持了json以及yaml 格式的处理(官方api)规则配置使用spel,添加了beanResolver
      可以方便的引用项目中的spring bean
     
    @Bean
        public Map<String,Rules> configRules(BeanResolver beanResolver) throws Exception {
            Map<String,Rules> rules = new HashMap<>();
            this.properties.getRules().forEach(new Consumer<RulesConfig>() {
                @Override
                public void accept(RulesConfig rulesConfig) {
                  switch (rulesConfig.getContentType()){
                      case JSON:
                          SpELRuleFactory jsonRuleFactory = new SpELRuleFactory(new JsonRuleDefinitionReader(),beanResolver);
                          Rules jsonRules = null;
                          try {
                              jsonRules = jsonRuleFactory.createRules(new FileReader(this.getClass().getClassLoader().getResource(rulesConfig.getRulesLocation()).getFile()));
                          } catch (Exception e) {
                              e.printStackTrace();
                          }
                          rules.put(rulesConfig.getRulesId(),jsonRules);
                          break;
                      case YAML:
                           SpELRuleFactory yamlRuleFactory = new SpELRuleFactory(new YamlRuleDefinitionReader(),beanResolver);
                           Rules yamlRules = null;
                          try {
                              yamlRules = yamlRuleFactory.createRules(new FileReader(this.getClass().getClassLoader().getResource(rulesConfig.getRulesLocation()).getFile()));
                          } catch (Exception e) {
                              e.printStackTrace();
                          }
                          rules.put(rulesConfig.getRulesId(),yamlRules);
                          break;
                      default:
                          throw new IllegalStateException("Unexpected value: " + rulesConfig.getContentType());
                  }
                }
            });
            return rules;
        }

    使用

    为了安全处理,ruleEngine 使用了原型模式,同时为了方便使用暴露了一个SpringBeanUtil bean

    • pom.xml
     <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
      </parent>
      <groupId>com.dalong.easy-rules-demo</groupId>
      <artifactId>demo</artifactId>
      <version>0.0.1-SNAPSHOT</version>
      <name>demo</name>
      <description>Demo project for Spring Boot</description>
      <properties>
        <java.version>1.8</java.version>
      </properties>
      <dependencies>
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
          <groupId>com.github.rongfengliang</groupId>
          <artifactId>easy-rules-spring-boot-starter</artifactId>
          <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-test</artifactId>
          <scope>test</scope>
          <exclusions>
            <exclusion>
              <groupId>org.junit.vintage</groupId>
              <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
          </exclusions>
        </dependency>
      </dependencies>
      <build>
        <plugins>
          <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
          </plugin>
        </plugins>
      </build>
    </project>
    • 配置文件
      src/main/resources/application.yml
     
    easyrules:
      skipOnFirstAppliedRule: false
      skipOnFirstNonTriggeredRule: false
      priorityThreshold: 1000000
      rules:
      - rulesId: "userlogin"
        rulesLocation: "rules-json.json"
        contentType: JSON

    rules-json.json:

    [{
      "name": "1",
      "description": "1",
      "priority": 1,
      "compositeRuleType": "UnitRuleGroup",
      "composingRules": [
        {
          "name": "2",
          "description": "2",
          "condition": "#{#biz.age >= 18}",
          "priority": 2,
          "actions": [
            "#{@myService.setInfo(#biz)}",
            "#{T(com.dalong.easyrulesdemo.demo.UserServiceImpl).doAction4(#biz)}"
          ]
        }
      ]}
    ]
    • rest api 调用
      注意为了获取最后rule 的数据,已经添加了一个FinalRule ,注意数据的传递需要包含一个biz 的key,当然可以自己定义
     
    @RestController
    public class UserApi {
        @Autowired
        Map<String,Rules> configRules;
        @RequestMapping(value = "/", method = RequestMethod.POST)
        public Object info(@RequestBody User user) throws Exception {
            Rules rules = configRules.get("userlogin");
            Facts facts = new Facts();
            // 生成一个唯一id,方便基于数据id规则流程查询
            user.setUniqueId(UUID.randomUUID().toString());
            FinalRule<User> rule = new FinalRule<User>();
            // rules.register(spELRule);
            rules.register(rule);
            facts.put("biz",user);
            // 默认模式
            // myEngine.fire(rules,facts);
            // 应该使用原型模式
            SpringBeanUtil.getBean("rulesEngine",RulesEngine.class).fire(rules,facts);
            if(rule.isExecuted()){
                User userResult= rule.getResult();
                System.out.println("result from final ruls"+userResult.toString());
                return userResult;
            }
            else {
                return null;
            }
        }
    }

    一些扩展点

    目前添加了基于listener的信息追踪的,只是简单的日志打印,实际上我们可以基于此扩展写入数据到一个时序数据库中
    方便基于生成的业务追踪id,分析rule 链的执行情况,同时基于rule 配置文件,生成一个可视化的pipline,同时基于log
    可以方便的分析数据,查看业务状态,后边会添加prometheus 以及key/value 存储配置的支持
    参考listener 的一个扩展

     
    Component
    public class MyRuleListener implements RuleListener {
        Log log = LogFactory.getLog(MyRuleListener.class);
        @Override
        public boolean beforeEvaluate(Rule rule, Facts facts) {
            return true;
        }
        @Override
        public void afterEvaluate(Rule rule, Facts facts, boolean b) {
            log.info("-----------------afterEvaluate-----------------");
            log.info("my RulesListener: "+"rule name: "+rule.getName()+"rule desc: "+rule.getDescription()+facts.toString());
        }
        @Override
        public void beforeExecute(Rule rule, Facts facts) {
            log.info("-----------------beforeExecute-----------------");
            log.info("my RulesListener: "+"rule name: "+rule.getName()+"rule desc: "+rule.getDescription()+facts.toString());
        }
        @Override
        public void onSuccess(Rule rule, Facts facts) {
            log.info("-----------------onSuccess-----------------");
            log.info("my RulesListener: "+"rule name: "+rule.getName()+"rule desc: "+rule.getDescription()+facts.toString());
        }
        @Override
        public void onFailure(Rule rule, Facts facts, Exception e) {
            log.info("-----------------onFailure-----------------");
            log.info("my RulesListener: "+"rule name: "+rule.getName()+"rule desc: "+rule.getDescription()+facts.toString());
        }
    }
     

    一些说明

    目前支持的功能还是比较少的,核心还是基于spel以及配置文件格式

    参考资料

    https://github.com/rongfengliang/easy-rules-spring-boot-starer

  • 相关阅读:
    CNN(卷积神经网络)入门
    基于linux vim环境python代码自动补全
    Linux 基本bash命令
    基于pytorch的CNN、LSTM神经网络模型调参小结
    深度学习中Batch size对训练效果的影响
    argparse.ArgumentParser()用法解析
    大数据学习之Hive数据仓库 20
    centOS中安装MySQL超级方便简单的方法
    大数据学习之zookeeper案例节点动态上下线感知19
    大数据学习之zookeeper客户端的命令行及API操作18
  • 原文地址:https://www.cnblogs.com/rongfengliang/p/12715438.html
Copyright © 2011-2022 走看看