自定义开发
说明
本篇的内容是框架的核心思想,较为重要,所有内置的插件也全都是基于这种开发模式开发出来的。自定义开发主要包括自定义模型,自定义插件,自定义数据库字段及函数。自定义模型包括自定义action,自定义result,自定义controller,自定义模型是实现了IJSRuleCustom接口的bean,有且只能有一个被注册进spring容器中。自定义插件是开发者根据自身业务场景封装的代码片段,它应具有高内聚低耦合的特性,它是脚本代调的基本单位,因此需要一定的抽象设计能力。自定义数据库字段及函数主要用于映射数据库的函数,如substr(),current_date,sum(),开发者可以自定义这些函数的名称,未定义的函数将不可用。
自定义action
action对应的扩展对象为edi.rule.model.JSRuleAction,通过继承这个类并将此类注册成spring bean开发者可以自定义一个action。这个action好比一个仓库,所有的插件类都会在这个仓库类中被定义成一个实现了edi.rule.extend.interfaces.IJSRuleActionModel接口类型的字段,字段的名字即是插件使用时的属性,如果要重写应用框架内置的插件,只需要重写它内部对应的插件类型的字段就可以了。
@Data
@EqualsAndHashCode(callSuper=false)
@Component
public class MyActions extends JSRuleAction<MyActions>{
@JSRuleModelPermit(join={"role1"})
public JSRuleGetCustomModel get;
public JSRuleAddCustomModel add;
@JsonAlias({"del"})
public JSRuleDeleteCustomModel delete;
public JSRuleEditCustomModel edit;
private MyTestCustom mtc;
}
上面的例子重写了父类的get,add,delete,edit插件,因此需要提供get和set方法,当使用json中的get插件时将会直接进入开发者自定义的JSRuleGetCustomModel这个类里的逻辑。除此之外开发者还可以定义其它的插件类型,如上面的MyTestCustom,还可以增加一些可用的注解,例如@JSRuleModelPermit(join={"role1"}),此外由于框架是jackson进行序列化的,这里还可以使用jackson注解,例如@JsonAlias({"del"}),它将插件名称加了一个del的别名,这不会影响重写的机制,例如@JsonIgnore注解,忽略某个字段的反序列化。
自定义插件
public class MyTestCustom implements IJSRuleActionModel<MyActions>{
@JSRuleInject
@JsonIgnore
private JSRuleGlobalVariable variable;
@JSRuleModelField
public List<String> list;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
public Date date;
@Override
public Object start(MyActions action) {
return "这里直接返回结果->";
}
}
注意:由于插件本身以及下面所有的属性都会经过jackson序列化,因此相关的jackson注解在这里也是可以使用的,比如@JsonIgnore,@JsonFormat等等,此外声明的变量要么是public访问权限,要么提供public类型的get方法,否则jackson会提示未被承认的属性
上面的例子是一个自定义插件的例子,插件其实就是一个实现了IJSRuleActionModel 接口的class类,其处理逻辑便是重写start方法,这里还有一些自定义开发时需要用到的注解,这些注解仅对插件内部属性生效,允许递归
-
@JSRuleInject:该注解与spring的@Autowired注解类似,用于依赖注入的,使用该注解时通常需要搭配@JsonIgnore注解,用于忽略某些注入字段的反序列化操作,因为被该注解标识的字段只有为空时才会有效。其中names属性表示获取指定的bean名称集合,types属性表示获取指定的bean类型集合,这两个属性通常用于注入Collection,Map,Array等集合类型的字段,表示获取多个bean,当用在非集合类型的字段上面时会按照顺序进行获取,当没有找到任何bean并且属性required为true的时候此时会报错,否则只要找到一个bean就会成功注入
注意:此注解注入带有@Transactional注解的bean时会报错,因此不能使用该注解注入JSRuleService,为了避免嵌套循环调用,逻辑上在model中也不允许注入JSRuleService,如果需要嵌套执行action,可以参考接口IJSRuleActionModel中的startActions方法 - @JSRuleModelField:此注解用于某些字段初始化后的一些后续处理,处理类需要实现IJSRuleModelFieldProcessor扩展接口并注册成spring bean。当此注解标记在一个类型为IJSRuleModel的字段时将会产生向下递归处理,此时该注解的作用将会失效。type属性表示该字段将会被哪个类进行处理,默认不进行任何处理
-
@JSRuleCheck:该注解用于校验字段值的合法性,如果该字段的类型是IJSRuleModel类型,那么它会向下递归检查。
注意:上面3个任何一个注解标记一个IJSRuleModel类型的字段时都将产生向下递归的效果,会递归处理IJSRuleModel类型对象中的字段,如果没有标记则不会递归处理
提示:以上3个注解的执行顺序是@JSRuleInject,@JSRuleModelField,@JSRuleCheck
异常以及国际化处理
异常的处理只需要throw new JSRuleException(msg)即可,如果需要国际化消息,则可以通过JSRuleMessage对象的read方法
例如:throw new JSRuleException(JSRuleMessage.read(key))
如果想要自己捕获该JSRuleException类异常也可以自定义一个@ControllerAdvice注解的类用于捕获JSRuleException,可以搭配@Order注解一起使用,或搜索JSRuleExceptionProcess类查看源码是如何处理的
国际化文件可在application文件中配置属性edi.rule.config.message,以resources为根目录,以/开头输入国际化文件的位置
自定义result
自定义result模型需要继承JSResult类,如果需要更改原有的字段名称,可通过重写父类的属性并加上@JsonProperty注解来进行更改,如果需要新增一个属性,可以直接在子类中新增一个字段,如果需要删除原有的字段,可通过重写父类的属性并加上@JsonIgnore注解来进行删除,用例如下
@Data
@EqualsAndHashCode(callSuper=false)
@Component
public class TestJSResult extends JSResult{
@JsonProperty("codeAlias")
public int code;
public String msgCustom;
public LinkedHashMap<String,Object> result;
@JsonIgnore
public String log;
@JsonIgnore
public Map<String,String> actionSql = new HashMap<String,String>();
public TestJSResult() {
code = 200;
msg = "any message";
result = new LinkedHashMap<String,Object>();
}
}
构造函数中用于定义结果集对象内部属性的默认值,重写父类属性意味着要对父类属性进行更改
自定义controller
json-script-rule内置的控制器为JSRuleController,它的固定访问路径为/json/script,当你需要更改这个路径的时候可以自定义一个控制器,如下
@Data
@RestController
@RequestMapping("/xxx/xxx")
public class MyTestController {
@Autowired
protected JSRuleService service;
@Autowired
protected HttpServletRequest request;
@Autowired
protected HttpServletResponse response;
@PostMapping(value = JSRuleJsonConfig.DEFAULT_REQUEST_START)
public String start() {
return service.start(ZSHttp.getJsonStrFromReq(request),ZSHttp.initGlobalArgs(request,response));
}
}
上面的例子还可以通过继承JSRuleController来实现一个自定义控制器,与此同时还应该关闭内置的控制器,可以通过以下方法来关闭
edi:
rule:
close:
engineController: true
自定义数据库函数及字段
自定义函数
所有的扩展类和接口均位于edi.rule.extend包下,其中JSRuleDBFunctions为自定义函数的顶级类,它下面有对应的各种数据库的子类,如JSRuleMysqlFunctions,JSRuleOracleFunctions等,通过继承这些子类来实现一个数据库函数的映射关系
public class JSRuleMysqlFunctions extends JSRuleDBFunctions{
static {
functions.put("abs", new JSRuleFunctionInfo("abs",1));
functions.put("concat", new JSRuleFunctionInfo("concat"));
functions.put("now", new JSRuleFunctionInfo("now",0));
}
}
上面的例子展示了如何定义一个函数映射,其中key "abs"为json中使用的函数名,它可以是任意名称,JSRuleFunctionInfo对象为实际的函数名和参数个数。函数映射定义完成后可以在json中直接通过R表达式来使用,例如:#$now(),这里的now便是key,未定义的函数不能在json中使用
自定义字段
数据库中存在一些非函数的字段,例如mysql的currentDate,这些字段不同于表字段,它的结果不属于任何一个表的结果,因此要定义这个字段必须要用另外的方式。框架提供了JSRuleDBFields对象用于这些特殊字段的定义,下面有四个子类对应四种不同的数据库类型,下面先以mysql为例
public class JSRuleMysqlSysFields extends JSRuleDBFields{
static {
fields.put("currentDate", "current_date");
fields.put("currentTime", "current_time");
}
}
通过上面的例子不难看出,它与自定义函数没有什么太大的区别,因此在这里就不多赘述了
自定义断言器
在使用shunt插件时,框架内置的JSRuleAssertUsual对象只提供了一些基本的判断功能,对于一些复杂的判断还无法实现,因此这个时候就需要开发者自定义一些断言的逻辑,如下面的例子
@Component
public class TestAsserts implements IJSRuleAssert{
@Override
public boolean ifTrue(String name, String path, Object value, boolean ifAnd, JSRuleGlobalVariable variable) {
if (value instanceof Number) {
return true;
}
return false;
}
}
断言器与自定义模型一样需要注册成spring bean,断言器在json调用时可直接写bean的名字,不是java class的名字
自定义开发需要了解的
获取json参数
Spring bean中可以通过如下方式获取
@Autowired
private JSRuleArgsVessel v
插件中可以使用如下方式获取
@JSRuleInject
private JSRuleArgsVessel v
上下文对象
上下文对象JSRuleContext,它提供了如下参数
- properties:用于获取spring application配置属性
- beanFactory:spring bean工厂
- dataSource:框架最终使用的数据源,类型为javax.sql.DataSource
- dbInfo:框架使用的数据库信息,包含数据库名称、"mapper"类型和function类型等
- security:框架目前所采用的安全模式信息
扩展接口
- JSRuleDefaultExtend:该扩展类实现了IJSRequestHandler和IJSRuleRoleHandler以及IJSRuleMappingsInfo接口,因此通过继承该类可以对上述三个接口方法进行自定义扩展,IJSRuleMappingsInfo接口用于处理非实体类映射的场景,如映射信息来自于xml文件或数据库而非java实体类。IJSRequestHandler用于自定义处理每次请求时json中的body对象的初始化,IJSRuleRoleHandler接口用于处理请求方的角色信息,开发者需要手动识别请求方的角色并返回一个请求方角色的set集合。该类需要通过注解@Component注解注册成spring bean或在application中配置edit.rule.processor属性,并输入类的全限定名称,如下
edi:
rule:
processor: test.myrule.JSRuleDBProcessor
- IJSRuleModel:json中所有对象属性的顶级接口,如果你自定义的类是json中的一个属性,那么理论上来说应该实现这个接口
- IJSRuleModelFieldProcessor:注解@JSRuleModelField所标识字段的处理接口,实现该接口的处理类可以在字段反序列化后重新赋值,该接口可以存在多个,重写order方法将实现排序
- IJSRuleActionModel:所有插件的顶级接口,自定义插件必须实现该接口,其start方法用于对action逻辑的处理
- IJSRuleAssert:实现该接口的spring bean可以在shunt插件中进行调用
- IJSRulePostLaunch:实现该接口的类在框架加载完成后会自动被调用,允许存在多个实现类,重写order方法将实现排序
- IJSRuleRoleAuthority:该接口用于初始化框架权限,其内部方法将返回一个"Map"集合,"key"为角色名称,"value"为权限对象。权限对象包含crud权限以及插件权限,crud权限为表与角色之间的关联关系。初始化的crud权限受注解@JSRuleTable中的permit以及roles属性制约