自定義開發(fā)
說明
本篇的內容是框架的核心思想,較為重要,所有內置的插件也全都是基于這種開發(fā)模式開發(fā)出來的。自定義開發(fā)主要包括自定義模型,自定義插件,自定義數(shù)據(jù)庫字段及函數(shù)。自定義模型包括自定義action,自定義result,自定義controller,自定義模型是實現(xiàn)了IJSRuleCustom接口的bean,有且只能有一個被注冊進spring容器中。自定義插件是開發(fā)者根據(jù)自身業(yè)務場景封裝的代碼片段,它應具有高內聚低耦合的特性,它是腳本代調的基本單位,因此需要一定的抽象設計能力。自定義數(shù)據(jù)庫字段及函數(shù)主要用于映射數(shù)據(jù)庫的函數(shù),如substr(),current_date,sum(),開發(fā)者可以自定義這些函數(shù)的名稱,未定義的函數(shù)將不可用。
自定義action
action對應的擴展對象為edi.rule.model.JSRuleAction,通過繼承這個類并將此類注冊成spring bean開發(fā)者可以自定義一個action。這個action好比一個倉庫,所有的插件類都會在這個倉庫類中被定義成一個實現(xiàn)了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插件時將會直接進入開發(fā)者自定義的JSRuleGetCustomModel這個類里的邏輯。除此之外開發(fā)者還可以定義其它的插件類型,如上面的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 "這里直接返回結果->";
}
}
注意:由于插件本身以及下面所有的屬性都會經(jīng)過jackson序列化,因此相關的jackson注解在這里也是可以使用的,比如@JsonIgnore,@JsonFormat等等,此外聲明的變量要么是public訪問權限,要么提供public類型的get方法,否則jackson會提示未被承認的屬性
上面的例子是一個自定義插件的例子,插件其實就是一個實現(xiàn)了IJSRuleActionModel 接口的class類,其處理邏輯便是重寫start方法,這里還有一些自定義開發(fā)時需要用到的注解,這些注解僅對插件內部屬性生效,允許遞歸
-
@JSRuleInject:該注解與spring的@Autowired注解類似,用于依賴注入的,使用該注解時通常需要搭配@JsonIgnore注解,用于忽略某些注入字段的反序列化操作,因為被該注解標識的字段只有為空時才會有效。其中names屬性表示獲取指定的bean名稱集合,types屬性表示獲取指定的bean類型集合,這兩個屬性通常用于注入Collection,Map,Array等集合類型的字段,表示獲取多個bean,當用在非集合類型的字段上面時會按照順序進行獲取,當沒有找到任何bean并且屬性required為true的時候此時會報錯,否則只要找到一個bean就會成功注入
注意:此注解注入帶有@Transactional注解的bean時會報錯,因此不能使用該注解注入JSRuleService,為了避免嵌套循環(huán)調用,邏輯上在model中也不允許注入JSRuleService,如果需要嵌套執(zhí)行action,可以參考接口IJSRuleActionModel中的startActions方法 - @JSRuleModelField:此注解用于某些字段初始化后的一些后續(xù)處理,處理類需要實現(xiàn)IJSRuleModelFieldProcessor擴展接口并注冊成spring bean。當此注解標記在一個類型為IJSRuleModel的字段時將會產(chǎn)生向下遞歸處理,此時該注解的作用將會失效。type屬性表示該字段將會被哪個類進行處理,默認不進行任何處理
-
@JSRuleCheck:該注解用于校驗字段值的合法性,如果該字段的類型是IJSRuleModel類型,那么它會向下遞歸檢查。
注意:上面3個任何一個注解標記一個IJSRuleModel類型的字段時都將產(chǎn)生向下遞歸的效果,會遞歸處理IJSRuleModel類型對象中的字段,如果沒有標記則不會遞歸處理
提示:以上3個注解的執(zhí)行順序是@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>();
}
}
構造函數(shù)中用于定義結果集對象內部屬性的默認值,重寫父類屬性意味著要對父類屬性進行更改
自定義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來實現(xiàn)一個自定義控制器,與此同時還應該關閉內置的控制器,可以通過以下方法來關閉
edi:
rule:
close:
engineController: true
自定義數(shù)據(jù)庫函數(shù)及字段
自定義函數(shù)
所有的擴展類和接口均位于edi.rule.extend包下,其中JSRuleDBFunctions為自定義函數(shù)的頂級類,它下面有對應的各種數(shù)據(jù)庫的子類,如JSRuleMysqlFunctions,JSRuleOracleFunctions等,通過繼承這些子類來實現(xiàn)一個數(shù)據(jù)庫函數(shù)的映射關系
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));
}
}
上面的例子展示了如何定義一個函數(shù)映射,其中key "abs"為json中使用的函數(shù)名,它可以是任意名稱,JSRuleFunctionInfo對象為實際的函數(shù)名和參數(shù)個數(shù)。函數(shù)映射定義完成后可以在json中直接通過R表達式來使用,例如:#$now(),這里的now便是key,未定義的函數(shù)不能在json中使用
自定義字段
數(shù)據(jù)庫中存在一些非函數(shù)的字段,例如mysql的currentDate,這些字段不同于表字段,它的結果不屬于任何一個表的結果,因此要定義這個字段必須要用另外的方式??蚣芴峁┝薐SRuleDBFields對象用于這些特殊字段的定義,下面有四個子類對應四種不同的數(shù)據(jù)庫類型,下面先以mysql為例
public class JSRuleMysqlSysFields extends JSRuleDBFields{
static {
fields.put("currentDate", "current_date");
fields.put("currentTime", "current_time");
}
}
通過上面的例子不難看出,它與自定義函數(shù)沒有什么太大的區(qū)別,因此在這里就不多贅述了
自定義斷言器
在使用shunt插件時,框架內置的JSRuleAssertUsual對象只提供了一些基本的判斷功能,對于一些復雜的判斷還無法實現(xiàn),因此這個時候就需要開發(fā)者自定義一些斷言的邏輯,如下面的例子
@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的名字
自定義開發(fā)需要了解的
獲取json參數(shù)
Spring bean中可以通過如下方式獲取
@Autowired
private JSRuleArgsVessel v
插件中可以使用如下方式獲取
@JSRuleInject
private JSRuleArgsVessel v
上下文對象
上下文對象JSRuleContext,它提供了如下參數(shù)
- properties:用于獲取spring application配置屬性
- beanFactory:spring bean工廠
- dataSource:框架最終使用的數(shù)據(jù)源,類型為javax.sql.DataSource
- dbInfo:框架使用的數(shù)據(jù)庫信息,包含數(shù)據(jù)庫名稱、"mapper"類型和function類型等
- security:框架目前所采用的安全模式信息
擴展接口
- JSRuleDefaultExtend:該擴展類實現(xiàn)了IJSRequestHandler和IJSRuleRoleHandler以及IJSRuleMappingsInfo接口,因此通過繼承該類可以對上述三個接口方法進行自定義擴展,IJSRuleMappingsInfo接口用于處理非實體類映射的場景,如映射信息來自于xml文件或數(shù)據(jù)庫而非java實體類。IJSRequestHandler用于自定義處理每次請求時json中的body對象的初始化,IJSRuleRoleHandler接口用于處理請求方的角色信息,開發(fā)者需要手動識別請求方的角色并返回一個請求方角色的set集合。該類需要通過注解@Component注解注冊成spring bean或在application中配置edit.rule.processor屬性,并輸入類的全限定名稱,如下
edi:
rule:
processor: test.myrule.JSRuleDBProcessor
- IJSRuleModel:json中所有對象屬性的頂級接口,如果你自定義的類是json中的一個屬性,那么理論上來說應該實現(xiàn)這個接口
- IJSRuleModelFieldProcessor:注解@JSRuleModelField所標識字段的處理接口,實現(xiàn)該接口的處理類可以在字段反序列化后重新賦值,該接口可以存在多個,重寫order方法將實現(xiàn)排序
- IJSRuleActionModel:所有插件的頂級接口,自定義插件必須實現(xiàn)該接口,其start方法用于對action邏輯的處理
- IJSRuleAssert:實現(xiàn)該接口的spring bean可以在shunt插件中進行調用
- IJSRulePostLaunch:實現(xiàn)該接口的類在框架加載完成后會自動被調用,允許存在多個實現(xiàn)類,重寫order方法將實現(xiàn)排序
- IJSRuleRoleAuthority:該接口用于初始化框架權限,其內部方法將返回一個"Map"集合,"key"為角色名稱,"value"為權限對象。權限對象包含crud權限以及插件權限,crud權限為表與角色之間的關聯(lián)關系。初始化的crud權限受注解@JSRuleTable中的permit以及roles屬性制約