栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

json-script-rule 自定义开发及扩展接口说明

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

json-script-rule 自定义开发及扩展接口说明

说明

自定义开发是指利用插件所提供的架构接口进行功能切入,这与spring提供的扩展接口机制有些类似,只需要将类注册成bean并实现相关接口就可以了,先上一个例子,如下所示

@Component
public class TestRuleProcessor implements IJSRuleGetProcessor,IJSPayloadProcessor,IJSRuleSqlBeforeProcessor{

    @Override
    public void getProcess(Map data,String className, TestRuleModel t,JSRuleActionParam jsrap) {
        System.out.println("xalalalalaniuniuniu");
    }

    @Override
    public Short order() {
        return 2;
    }

    @Override
    public Object payloadProcess(String fieldName, Object fieldValue, JSPayload zspl) {
        return fieldValue;
    }
    
    @Override
    public String beforeSql(String sql,TestRuleModel t) {
        return sql;
    }
}

这里的TestRuleProcessor是我在主项目上随便写的一个类,类上面的@Component注解也可以换成其它的,只要是能将类在spring容器中注册成bean即可。
第2步根据自己的需要去实现对应的接口,这里列举了3个接口,先简单说明其中一个后,再对所有接口以及对应的参数进行具体说明

  • IJSRuleGetProcessor:这个是二次开发主要用到的查询扩展接口,当每次查询完数据后,都会走这个接口对查询后的数据做进一步的处理,如果你实现了该接口,那么数据将会以参数的形式传入进这个方法之中,数据参数为data。
    className:为当前数据的分组类名,也就是当你进行多表查询时,同一条数据为了区分A表和B表的字段(A和B有可能存在同名的字段),这里用className加以区分,因此这里的data(在当前版本中)并不是代表1条数据(正常的逻辑通常是1个map代表1条数据行,这里的data代表的是1行数据中的A组或者B组),而是代表1条数据中的1个class,也就是A或者B的数据。
    TestRuleModel:是自定义的model,在之前的篇章里也提到过我们有4大model类型,也就是crud分别对应的JSRuleAdd,JSRuleGet,JSRuleEdit和JSRuleDelete,这4个模型是在插件中定义的,这些模型中定义的规则如required,fillBean,uuid以及calculate计算规则等都是写在了插件中,如果你有自己想要定制的规则,可以通过继承这4个基础类型进而扩展自己的规则逻辑,此处的TestRuleModel便是继承了其中1个类型,如下

@Component
public class TestRuleModel extends JSRuleGet{
    
    @JSRuleCheck(required= true)
    public String myCustomField;
}

继承了JSRuleGet表示这是一个查询模型,
注意:JSRuleGet只能有1个子类,同理其它crud模型也是一样,如果有多个子类则会在启动时给出报错提示,此外不能重写覆盖父类已有的规则,因为父类规则处理同样也是用接口去实现的,因此如果有两个同名的字段会先走一遍父类的规则再走一遍子类的规则,不过在这之前jackson会报错......
JSRuleActionParam :这个参数是开发时最重要的参数,它表示此次请求的某个action的参数,这些参数是经过系统处理过的,此类中提供的公共方法可以直接使用
JSRuleCheck:字段上面的@JSRuleCheck注解是校验注解,可以直接使用,其中定义的几个属性如下

@Documented
@Retention(value = RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD })
public @interface JSRuleCheck {
    boolean required() default false;
    String[] type() default {};
    boolean valid() default false;
    boolean validValueOrMapKey() default true;
    boolean validMapValue() default false;
    boolean validMapValueByKeyName() default false;
    boolean checkSubClass() default false;
}

常用的有:

  • required:表示json主体中是否必须要包含此字段且有值
  • type:表示json对应属性中的值是否为当前注解中所规定的值,例如

@Data
@JsonIgnoreProperties(ignoreUnknown=true)
public class JSRuleRelations implements IJSRuleCheck{

    @JSRuleCheck(required=true)
    public String[] classNames;
    public String main;
    @JSRuleCheck(type= {JSRuleConstantEnum.rule_relation_type_inner,JSRuleConstantEnum.rule_relation_type_left})
    public String type;
    
    public JSRuleRelations() {
        this.type = JSRuleConstantEnum.rule_relation_type_inner;
    }
}

这里列举的是JSRuleRelations类,其中的type字段的值只能为注解中的type对应的字符串数组

  • valid:是校验字段的值是否合法,合法的值必须是po类所定义的字段,或是带有className前缀的字段
  • checkSubClass:是否递归校验子类带有@JSRuleCheck注解的字段,如下

@Data
@JsonIgnoreProperties(ignoreUnknown=true)
public final class JSRuleAction implements IJSRuleCheck {
    
    public String name;
    @JSRuleCheck(checkSubClass=true)
    public JSRuleRelations relations;
    
    @JsonAlias({"c"})
    @JSRuleCheck(checkSubClass=true)
    public C add;
    @JsonAlias({"r"})
    @JSRuleCheck(checkSubClass=true)
    public R get;
    @JsonAlias({"u"})
    @JSRuleCheck(checkSubClass=true)
    public U edit;
    @JsonAlias({"d"})
    @JSRuleCheck(checkSubClass=true)
    public D delete;
    public JSRuleActionParam params;
    
    public JSRuleAction() {
        this.params = new JSRuleActionParam();
    }
}

这里列举的是JSRuleAction模型,所有的crud模型都校验了其子类中的注解,子类只需要实现IJSRuleCheck接口便可以使用@JSRuleCheck注解功能了,如下

public class JSRuleGet implements IJSRuleGetModel

因为接口也是有结构的,这里的IJSRuleGetModel的上层结构包含了IJSRuleCheck,所以也能达到同样的目的

我们言归正传,回到刚刚的IJSRuleGetProcessor接口中来,我们先看看插件中是如何实现这些规则的,如下

@Component
public class JSRuleGetProcessor implements IJSRuleGetProcessor{
    
    @Override
    public void getProcess(final Map data,String className,JSRuleGet jsrg,JSRuleActionParam jsrap) {
        
        if (ZSObject.sizeIsNotEmpty(jsrg.ternary)) {
            JSRuleHelper.getTernaryResult(data,jsrg.ternary);
        }
        if (ZSObject.sizeIsNotEmpty(jsrg.calculate)) {
            JSRuleHelper.calculateResultForMap(data,jsrg.calculate);
        }
    }
    
    @Override
    public Short order() {
        return -1;
    }
}

我们看到其实逻辑非常的简单,首先就是判断规则是否为空,如果为空就代表此次action并不想触发这个规则,如果不为空,直接写一个方法去处理data就可以了,这里需要注意的是这个方法是没有返回值的,你只需要对data这个map做处理就可以了
再来就是这个经常能看到的order,它代表执行处理器的顺序,因为处理器会有多个,所以需要依次执行,order默认值为0,数字越小代表越优先执行,这里写的是-1,表示优先于未设置order的处理器执行,如果你的处理器需要在它前面执行,只需要重写这个方法,把return的值调小即可。
提示:IJSRuleGetProcessor后面的泛型写你自定义的model就可以了,参考最上面的例子

抽象类

位于abstracts包,目前只有两个抽象类,JSRuleActionPreProcessor,JSRuleExtendProcessor

  • JSRuleActionPreProcessor:在处理JSPayload之后,检查rule之前执行,部分源码如下

Object start(JSRuleAction jsra) {
        jspp.processPayload(jsra);
        preProccessRuleAction(jsra);
        checkRule(jsra,jsra.params);
        if (jsra.get!=null) {
            return this.getDatas(jsra);
        }else if (jsra.add!=null) {
            return this.addDatas(jsra);
        }else if (jsra.edit!=null) {
            return this.editDatas(jsra);
        }else if (jsra.delete!=null) {
            return this.deleteDatas(jsra);
        }else {
            throw new JSRuleException("缺少crud对象");
        }
    }
  • JSRuleExtendProcessor:该类为自定义扩展类,是在扩展接口的基础上为继承它的子类处理了一些缓存上的逻辑,因此再做自定义开发时应优先考虑此类,下面介绍接口时将会详细说明此类的作用。
接口

先来看看有哪些扩展接口,上图

接口.png


如图所示,主要的接口扩展包是interfaces包下的processor包,这里面的接口都是扩展接口,除此之外,还有crud中红框所标记的扩展接口,如果你不清楚这些接口的结构层级,可以结合着model包下的类以及interfaces包下的接口来了解,这些扩展接口通常依赖于base包下的接口。

crud扩展接口

  • IJSRuleGetProcessor:上面已经描述过了,是查询的扩展接口,每次请求引擎查询出数据后都会经过这个接口,通常用来对查询出来的数据做处理
  • IJSRuleAddProcessor:执行插入前,每一条数据都会经过这个接口
  • IJSRuleEditProcessor:组装查询条件之前,一个action只执行一次该接口里的方法
  • IJSRuleDeleteProcessor:组装查询条件之前,一个action只执行一次该接口里的方法,注意删除时必须要有查询条件(避免全部删除数据,后续版本会进行改进),否则会有报错提示

其它扩展接口

  • IJSPayloadProcessor:所有使用@JSPayload注解标记的字段会经过这个接口,例如JSRuleAdd中的datas属性

@Data
@JsonIgnoreProperties(ignoreUnknown=true)
public class JSRuleAdd implements IJSRuleAddModel{
    
    @JSPayload
    @JSRuleCheck(required=true)
    public List> datas;
    @JSRuleCheck(valid=true)
    public String[] uuid;
    @JSRuleCheck(valid=true)
    public String[] longid;
    @JSRuleCheck(valid=true)
    public Map fillBean;
    @JSRuleCheck(valid=true)
    public String[] required;
    public String[] ternary;
    public String[] calculate;
    
    public JSRuleAdd() {
    }
}

它表示有效载荷字段,这部分属性都是动态的有效参数,也就是前端传递的业务参数,比如一个简单的查询输入框,第一次用户查询张三,那么有效参数就是张三,至于是模糊查询还是精确查询,这些一般都是固定的,所以通常称这种为配置(当然了json rule的配置其实也是动态的,但相比较业务参数的完全不确定性来说它也算是静态的)

public interface IJSPayloadProcessor extends IJSRuleSort{
    public default Object payloadProcess(String fieldName,Object fieldValue,JSPayload jspl) {return fieldValue;}
}

这些参数的含义大多和它们的名字一样,拿上面的例子来说,fieldName就是datas,fieldValue就是datas的list值,jspl是字段上面的注解(目前该注解没有任何属性,后续版本可能会增加)
返回值:处理后,这个字段的新值,如果没有重写此方法,则默认返回原来的值

  • IJSRuleDmlBefore:这个接口是IJSRuleAddBeforeProcessor,IJSRuleEditBeforeProcessor,IJSRuleDeleteBeforeProcessor这三个接口的父接口,表示最后在执行前的那一刻,最终生成的结果,下一步就准备开始执行了
  • IJSRuleMappingClassInfoProcessor:这个接口是自定义缓存po类信息的接口,插件随着项目启动会加载po包下的类,将这些类所配置的包含注解的信息形成对应的类信息进行缓存。

public interface IJSRuleMappingClassInfoProcessor extends IJSRuleSort{
    
    public Map initMappingClassInfo();
}

这里的返回值是一个map,key为对应po类的类名,value中的JSRuleMappingClassInfo为装载类信息的类,如果你对整个架构并不熟悉的话,建议你通过继承JSRuleExtendProcessor这个类来实现po类信息缓存的扩展(有些类信息并非是通过配置po类实现的,它有可能是启动时从数据库加载的,也有可能是从其它文件中读取的,如xml)
总而言之,只要将JSRuleMappingClassInfo这个类构造出来即可,这个类所提供的构造函数说明了它都需要哪些参数,如下

public JSRuleMappingClassInfo(String className,String tableName,String pkFieldName,List fieldNames,List dateFields,
            Map fkRelationsMapping,Map tableFieldAliasMapping,Map aliasJavaFieldMapping)

className:这个就不用说了,表示当前po类名称
tableName:所对应的数据库表名称
pkFieldName:pk字段的字段名,就是标注pk注解的字段名
fieldNames:当前po类中所有字段的字段名
dateFields:当前po类中为日期类型的字段名(也就是类型为java.util.Date,只能是该类型来表日期或时间)
fkRelationsMapping:key为fk注解所关联的className,value为当前类fk注解所标注的字段对应的数据库表字段(@JSRuleRelationField注解中如果有fieldName属性的则取其值,如果没有则取当前字段的名称)
tableFieldAliasMapping:java字段映射别名,key是java字段名,如果有别名则用别名作为value
aliasJavaFieldMapping:与上面的tableFieldAliasMapping刚好反过来,key为别名,value为java字段名,此外值得一提的是该参数的多态子类型为CaseInsensitiveMap,对于key来说是不区分大小写的

最后通过抽象扩展类JSRuleExtendProcessor来实现自定义处理器,该类提供了2个可重写方法

  1. public List buildsMappingClassInfo();
    这个方法就是上面所讲述的,如果你需要自定义缓存po配置类,那么你就需要将配置数据从各个地方(数据库,配置文件或其它)收集过来后为每一个JSRuleMappingClassInfo对象进行构造(参数如上所述),最后将所有的对象放进一个List集合中,重写该方法后返回这个对象的集合即可
  2. public void assembleRule(JSRule jsr) {};
    这个方法里的参数原本是前端json主体传入到后端引擎后解析出来的规则对象,最后将这个对象传入引擎最终解析成sql语句的,如果你的实际业务参数并非是从前端传给插件接口的,那么重写这个方法,重新组装这个JSRule对象即可,无需返回值,总而言之,这个对象代表的是前端json主体的rule属性,每个action的rule属性,当然,你也可以通过调用JSRuleEngineService中的public JSResult start(JSBody jsb)方法来启动引擎,这里你需要拼装的是JSBody对象,JSBody包含JSRule,具体对象关系可直接参考model包

提示:这里我在插件包里写了一个例子,位置在edi.rule.extend.adapter.JSRuleDBProcessor,可参考此类
说明:默认的JSRule处理器为JSRuleJsonProcessor,是处理前端每次请求给后台的json主体参数的处理器,如果需要自定义(如这里的JSRuleDBProcessor获取参数的方式是从数据库读取出来的,加到了缓存里)则需要将此类注册成spring的bean(@Component或其它方式),或者在rule.properties中配置前面提到过的这个属性也可以达到同样的目的

js.rule.processor=edi.rule.extend.adapter.JSRuleDBProcessor

这里列举的是插件中的JSRuleDBProcessor,这个路径需要配置你自己的类路径,配置完成后就会将默认的处理器更改为当前你所配置的处理器
总结:json script rule执行需要两个东西,一个是启动时处理到缓存中的po的配置信息,另一个是前端传给后台的json主体配置信息,JSRuleExtendProcessor这个扩展抽象类提供的两个方法一个解决了缓存的po配置信息,另一个解决了自定义json主体配置信息。处理json主体配置信息的方法assembleRule之所以不是抽象的,是因为子类未必一定要去自己设计如何组装JSRule对象,如果你的项目前端能够直接提供json主体参数进行请求,那么这里就不需要重写assembleRule方法,直接使用内置的处理方式就可以了。
此外buildsMappingClassInfo同样也是非必须的,但如果你要自己去设计这个集合该从哪里获得,那么这里就需要重写父类的这个方法去返回一个集合,如果你并不想自己去设计,还是希望通过配置po类的这种默认的方式去加载缓存,那么就不需要重写这个方法,但这个时候你就要配置po的包路径了,即配置文件中加js.rule.mapping.classes.location属性,否则启动时会有相应的报错提示。总而言之,这里根据你的实际需要去重写这两个方法,当然了,如果这两个方法你都希望是默认的处理方式,那么你继承这个扩展类就毫无意义了。

  • IJSRuleProcessor:这个就是上面所说的处理json主体配置信息的接口,默认的处理类是edi.rule.processor.JSRuleJsonProcessor,上面提到的JSRuleDBProcessor是扩展出来的通过DB而非前端传进来的json主体配置信息(如果是DB,因为有效载荷是动态的,所以最终还需要将json主体信息和前端传给后台的有效载荷的业务参数进行合并才行),实现IJSRuleProcessor这个接口的处理器只能有1个,可通过注册成spring的bean或配置文件实现指定处理器,目前实现该接口的有2个类,JSRuleJsonProcessor,以及JSRuleExtendProcessor抽象扩展类,因为JSRuleDBProcessor是继承了JSRuleExtendProcessor这个类,因此它具有处理能力,总而言之要自定义json主体配置信息或po配置信息,继承JSRuleExtendProcessor类即可
  • IJSRuleSqlBeforeProcessor:接口方法如下

public interface IJSRuleSqlBeforeProcessor extends IJSRuleSort{
    public default String beforeSql(String sql,R r) {return sql;}
}

实现该接口的beforeSql方法将会在执行sql之前调用此方法,默认返回原sql

  • IJSRuleStartProcessor:实现该接口将有序实现@PostConstruct注解功能,也就是在插件调用@PostConstruct方法后依次按顺序调用其start方法

备忘:实现接口的扩展功能,首先注册成spring的bean,最后再实现相应的接口
最后:至此功能使用方面的介绍大体完结了,后续如果有补充的会慢慢更新文档,接下来的文档将更多的关注于源码的设计和实现,只关注如何使用的朋友不太建议了解的太深

 

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/992435.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号