自定义简单流程引擎

需求:我们在开发过程中会遇到下面这类需求,类似一张流程图,根据条件不断进行判断,命中,再进行下一轮判断。

 实现:在项目开发过程中 ,完全可以根据每个节点进行if else 判断进行流程跳转,但是这样比较繁琐,且不符合设计原则

根据上面的需求知晓以下几点

流程跳转的三要素:条件、动作、下一步

整个判断的流程都是一次性,因此直接是内存判断,无需借助数据库等记录中间状态。

利用Spel表达式 递归进行条件判断:

结构图:

 每个@Rule方法,代表了一个规则包含 条件、动作、下一步。满足condition条件,反射执行方法体,然后遍历next的后续节点,判断是否满足,不断递归执行执行最后一个节点。最后一个节点即叶子节点,next为空。

/**
 * @author lixiaojian
 * @version 5.9.
 * @Description
 * @date 2021/07/19
 */
@Target({ ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Rule {

    String condition();
    String rulename();
    String[] next() default {};
}

/**
 * @author lixiaojian
 * @version 5.9.
 * @Description
 * @date 2021/07/19
 */
@Data
public class RuleV {
    /**
     * 条件
     */
    private String condition;

    /**
     * 规则名称
     */
    private String rulename;

    /**
     * 满足条件需要执行的方法
     */
    private Method ruleMethod;

    /**
     * 后续的规则名称
     */
    private List<String> next;

}

/**
 * @author lixiaojian
 * @version 5.9.
 * @Description
 * @date 2021/07/20
 */
public class RuleEngine {
    public static final String CURRENT_RULE = "currentRule";
    final Map<String, RuleV> ruleVMap = new HashMap<>();
    public Object target;
    public  void init(Object target){
        this.target = target;
        ReflectionUtils.doWithMethods(target.getClass() , new ReflectionUtils.MethodCallback() {
            @Override
            public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {

                Rule annotation = method.getAnnotation(Rule.class);

                if (annotation != null) {
                    RuleV ruleV = new RuleV();
                    ruleV.setRulename(annotation.rulename());
                    ruleV.setCondition(annotation.condition());
                    String[] next = annotation.next();
                    ruleV.setNext(Arrays.asList(next));
                    ruleV.setRuleMethod(method);
                    ruleVMap.put(annotation.rulename(), ruleV);
                }
            }
        });
    }

    public void start(String startRuleName,HashMap variable) {
        RuleV ruleV = ruleVMap.get(startRuleName);
        variable.put(CURRENT_RULE,startRuleName);
        try {
            ruleV.getRuleMethod().invoke(target,variable);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException(e);
        }
        stepInto(variable);
    }

    /**
     * 递归进入下一个阶段的判断
     * @param variables
     */
    private void stepInto(Map<String, Object> variables) {
        RuleV ruleV = ruleVMap.get(variables.get(CURRENT_RULE));
        if (ruleV == null) {
            System.err.println("success.....stepInto...................");
        }
        for (String s : ruleV.getNext()) {
            try {
                RuleV nextRule = ruleVMap.get(s);
                String condition = nextRule.getCondition();
                Method ruleMethod = nextRule.getRuleMethod();
                boolean check = check(variables, condition);
                if (check) {
                    variables.put(CURRENT_RULE, ruleVMap.get(s).getRulename());
                    ruleMethod.invoke(target, variables);
                    stepInto(variables);
                    break;
                }
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * spel表达式判断是否满足条件
     * @param variables
     * @param condition
     * @return
     */
    public static boolean check(Map<String, Object> variables,String condition){
        ExpressionParser parser = new SpelExpressionParser();
        StandardEvaluationContext context = new StandardEvaluationContext();

        context.setVariables(variables);

        Expression exp = parser.parseExpression(condition);
        return exp.getValue(context, Boolean.class);
    }
}

 demo:

@Slf4j
@Service
public class SimpleRuleDemoService {

    @Rule(condition = "true == true",rulename = "start",next = {"phonePrefix188","phonePrefix200"})
    public void start(Map<String, Object> variables){
        log.info("begin ****************************************** start {}",variables);
        log.info("我成功进入到了 start");
        variables.put("phone", "1881410");
        log.info("end ******************** into ********************** start {}",variables);
    }

    /**
     * 满足手机号是188开头,下一阶段的规则是 finAms,findOther
     * @param variables
     */
    @Rule(condition = "#phone.startsWith('188')",rulename = "phonePrefix188",next = {"finAms","findOther"})
    public void phonePrefix188(Map<String, Object> variables){
        log.info("begin ****************************************** phonePrefix188 {}",variables);
        log.info("我成功进入到了 phonePrefix188");
        variables.put("finAms", true);
        log.info("end ******************** into ********************** phonePrefix188 {}",variables);
    }

    /**
     * 满足手机号是188开头,下一阶段的规则是 空,表示最后命中结束
     * @param variables
     */
    @Rule(condition = "#phone.startsWith('200')",rulename = "phonePrefix200")
    public void phonePrefix200(Map<String, Object> variables){
        log.info("begin ****************************************** phonePrefix200 {}",variables);
        log.info("我成功进入到了 phonePrefix200");
        variables.put("isInAms", true);
        log.info("end ******************** into ********************** phonePrefix200 {}",variables);
    }


    @Rule(condition = "#finAms == true",rulename = "finAms",next = {"jueseB","ok"})
    public void finAms(Map<String, Object> variables){
        log.info("begin ****************************************** finAms {}",variables);
        log.info("我成功进入到了 finAms");
        variables.put("jueseB", false);
        log.info("end ******************** into ********************** finAms {}",variables);
    }
    @Rule(condition = "#findOther",rulename = "findOther")
    public void findOther(Map<String, Object> variables){
        log.info("begin ****************************************** findOther {}",variables);
        log.info("我成功进入到了 findOther");
        log.info("end ******************** into ********************** findOther {}",variables);
    }

    @Rule(condition = "#jueseB == false",rulename = "jueseB")
    public void jueseB(Map<String, Object> variables){
        log.info("begin ****************************************** jueseB {}",variables);
        log.info("我成功进入到了 jueseB");
        log.info("end ******************** into ********************** jueseB {}",variables);
    }

    @Rule(condition = "!#jueseB",rulename = "ok")
    public void ok(Map<String, Object> variables){
        log.info("begin ****************************************** ok {}",variables);
        log.info("我成功进入到了 ok");
        log.info("end ******************** into ********************** ok {}",variables);
    }





    public static void main(String[] args) {
        RuleEngine ruleEngine = new RuleEngine();
        ruleEngine.init(new SimpleRuleDemoService());
        ruleEngine.start("start",new HashMap());
    }

    public void checkparse(){
        ExpressionParser parser = new SpelExpressionParser();
        Expression parseExpression = parser.parseExpression("name");
        parseExpression.getValue();
    }

}

demo的流程执行节点结果:

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值