加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

三分钟学会 Groovy 运行 DSL文件

发布时间:2020-12-14 16:44:41 所属栏目:大数据 来源:网络整理
导读:首先groovy和dsl的概念自不必多说.. Groovy: 可以简单理解成是对Java的封装,它提供了更加简洁的语法和其他动态语言的一些特性,如JS的闭包.在Groovy中也有闭包,而且个人认为这个闭包是Groovy中最灵活的地方. DSL: 可以简单理解成就是一个配置文件,或者说是

首先groovy和dsl的概念自不必多说..

Groovy: 可以简单理解成是对Java的封装,它提供了更加简洁的语法和其他动态语言的一些特性,如JS的闭包.在Groovy中也有闭包,而且个人认为这个闭包是Groovy中最灵活的地方.

DSL: 可以简单理解成就是一个配置文件,或者说是面向领域的某种配置文件,如Ant 的build.xml 某种意义上就是Ant的DSL文件.


下面简短的说一下实际运用:


1) ?需求

? 系统要和外部系统集成交互,交互的方式是当在本系统做完一笔业务后,要在特定的共享文件夹中产生一个XML文件,但是要注意这个XML文件中的属性的序列是可变的,也就是说,第一天客户要姓名排在年龄之后,第二天客户忽然脑抽要年龄排在姓名之前.要删除一个字段,要在某个字段前固定加上一个前缀...

<xml>
<name>zhangsan</name>
<age>18</age>
</xml>


2) 设计思路

这该如何是好???

本身产生XML 在java中是如此的简单,但是你丫要改变序列,这个不是很蛋疼吗....

想啊想... 想啊想...

XStream.. JAXB.. 反射自己拼XML,那可配置怎么弄呢?

太烦了...

唉?! 听说Groovy中有个XMLBuilder? 什么玩意..

先Google一下,大神深入浅出,讲的这么简单,那就直接用吧,看样子是可以做到的

http://www.ibm.com/developerworks/cn/java/j-pg05199/?


3) 具体实现

?本身XmlBuilder 就很简洁,只要

def?comment?=?"<![CDATA[<!--?address?is?new?to?this?release?-->]]>"
def?builder?=?new?groovy.xml.StreamingMarkupBuilder()
builder.encoding?=?"UTF-8"
def?person?=?{
??mkp.xmlDeclaration()
??mkp.pi("xml-stylesheet":?"type='text/xsl'?href='myfile.xslt'"?)
??mkp.declareNamespace('':'http://myDefaultNamespace')
??mkp.declareNamespace('location':'http://someOtherNamespace')
??person(id:100){
????firstname("Jane")
????lastname("Doe")
????mkp.yieldUnescaped(comment)
????location.address("123?Main")
??}
}
def?writer?=?new?FileWriter("person.xml")
writer?<<?builder.bind(person)

像这样,其中mkp是StreamingMarkupBuilder中的内置变量person 就是要产生的 xml的Root Node,下面将上面的代码Run一下产生的xml,可以看到person.xml中的内容:

<?xml?version="1.0"?encoding="UTF-8"?>
<?xml-stylesheet?type='text/xsl'?href='myfile.xslt'?>
<person?id='100'
????????xmlns='http://myDefaultNamespace'
????????xmlns:location='http://someOtherNamespace'>
??<firstname>Jane</firstname>
??<lastname>Doe</lastname>
??<![CDATA[<!--?address?is?new?to?this?release?-->]]>
??<location:address>123?Main</location:address>
</person>

相信很多稍有Coding经验的人对上面产生xml的代码一眼便明,哈哈..

知道如何产生xml后那么怎么实现可配置呢?

其实只要把上面这段代码放到一个配置文件中就可以了,groovy的语法,也就是上面这段代码本身就相当于配置文件,但是他又是可以随时运行的,也就是说就算你项目on line 了 ,客户脑抽了,要减少一个字段 或者是改变一下序列,那我们只需要download下这个所谓的dsl后稍作修改再上传就OK了.


下面贴上实际代码.

package?com.ebao.gs.sp.dsl

import?com.ebao.gs.sp.dsl.tasks.Tasks
import?com.ebao.gs.sp.model.Policy
import?com.ebao.gs.sp.pub.context.AppContext
import?com.ebao.gs.sp.pub.context.DSLEngineContext
import?com.ebao.gs.sp.pub.exception.BeforeIssuanceRuleFailureException
import?com.ebao.gs.sp.pub.exception.DSLNotFoundException
import?com.ebao.gs.sp.pub.exception.UserDefinedError
import?groovy.util.logging.Slf4j


@Slf4j
class?DSLEngine?{

????static?final?DSLEngine?instance?=?new?DSLEngine();

????static?GroovyScriptEngine?scriptEngine

????public?static?final?DSLEngine?getInstance()?{
????????return?instance;
????}

????static?boolean?isValidDSLRoot(String?dslRootFolder)?{
????????try?{
????????????if?(dslRootFolder?==?null?||?dslRootFolder.trim().equals(""))?{
????????????????throw?new?DSLNotFoundException("DSL?root?folder?must?be?provided")
????????????}
????????????def?rootFile?=?new?File(dslRootFolder)
????????????if?(!rootFile.exists()?||?!rootFile.isDirectory())?{
????????????????throw?new?DSLNotFoundException("DSL?root?folder?[${dslRootFolder}]?does?not?exists?or?not?a?folder")
????????????}
????????????def?dslNotFound?=?true
????????????rootFile.eachDir?{?File?tenantFolder?->
????????????????def?tenantCode?=?tenantFolder.name
????????????????if?(tenantCode.toUpperCase()?!=?Const.SHARED.toUpperCase())?{
????????????????????tenantFolder.eachDir?{?prdtFolder?->
????????????????????????def?prdtCode?=?prdtFolder.name
????????????????????????if?(prdtCode.toUpperCase()?!=?Const.SHARED.toUpperCase())?{
????????????????????????????prdtFolder.eachDir?{?verFolder?->
????????????????????????????????def?prdtVersion?=?verFolder.name
????????????????????????????????if?(prdtVersion.toUpperCase()?!=?Const.SHARED.toUpperCase())?{
????????????????????????????????????def?dslFiles?=?verFolder.listFiles(new?FilenameFilter()?{
????????????????????????????????????????@Override
????????????????????????????????????????boolean?accept(File?dir,?String?name)?{
????????????????????????????????????????????return?name.toLowerCase().endsWith(".dsl")
????????????????????????????????????????}
????????????????????????????????????})
????????????????????????????????????if?(dslFiles.size()?<?1)?{
????????????????????????????????????????throw?new?DSLNotFoundException("No?DSL?file?defined?for?[${tenantCode},?${prdtCode},?${prdtVersion}]")
????????????????????????????????????}
????????????????????????????????????dslNotFound?=?false
????????????????????????????????}
????????????????????????????}
????????????????????????}
????????????????????}
????????????????}
????????????}
????????????if?(dslNotFound)?{
????????????????throw?new?DSLNotFoundException("Invalid?DSL?root:?no?any?DSL?files?found?in?this?folder")
????????????}
????????????return?true
????????}?catch?(DSLNotFoundException?e)?{
????????????return?false
????????}
????}

????private?initScriptEngine()?{
????????String?root?=?//?这里是你dsl文件就是上文中所述的可配置的文件所在的根目录
????????if?(scriptEngine?==?null)?{
????????????synchronized?(this)?{
????????????????scriptEngine?=?new?GroovyScriptEngine(root)
????????????}
????????}
????}


????void?generateConfigurationXML(Policy?policy)?{
????????initScriptEngine()
????????initDSLEngineContext(policy)

????????def?binding?=?new?Binding()
????????def?tasks?=?new?Tasks(policy:policy)
????????binding.setVariable("Tasks",?tasks)

????????def?fileName?=?//?你具体Dsl在dsl根目录中的名字,通过上述步骤,然Groovy的ScriptEngine?可以找到你dsl文件即可
????????def?result?=?scriptEngine.run(fileName,?binding)
????????
????????println?result.toString()
????}
}

上面代码中的Policy对象就是产生xml的原对象。接下来可以看到

????????def?binding?=?new?Binding()
????????def?tasks?=?new?Tasks(policy:policy)
????????binding.setVariable("Tasks",?tasks)

这里面绑定了一个Tasks 变量,作用是在你的DSL中起始位置处必须要有Tasks以让Groovy可以找打这个DSL文件运行的初始调用点.


class?Tasks?{

???def?builder?=?new?StreamingMarkupBuilder()
???def?policy?

???def?call(Closure?cl)?{
??????cl.setDelegate(this);
??????cl.setResolveStrategy(Closure.DELEGATE_ONLY)
??????cl.call();
????}
????
}

接下来当运行到Tasks对象实例中的时候,改实例中的builder 和policy 起始已经被实例化了,在dsl文件中我们只需要如下:


Tasks{
????builder.bind?{
????????mkp.xmlDeclaration()
????????policy?{
????????????field1?(policy.field1)
????????????field2?(policy.field1?+?"suffix")
????????}
????}
}

便可得到产生的xml

<xml?...>
<field1>aaa</field1>
<field1>222suffix</field1>
</xml>

以后只需要改这个dsl就可以“高度” 可配置!

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读