文档章节

三分钟学会 Groovy 运行 DSL文件

j
 john_guo
发布于 2016/03/30 18:01
字数 1243
阅读 116
收藏 0

首先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就可以“高度” 可配置!

© 著作权归作者所有

上一篇: Jfinal深刻理解
下一篇: Jfinal深刻理解
j
粉丝 4
博文 2
码字总数 2802
作品 0
无锡
私信 提问
《三分钟阅读》你不知道的forEach的一些小细节

三分钟阅读:给自己3分钟的时间,拾起自己遗漏的知识点,这是第一期 前言 从今天开始,我准备写个《三分钟阅读》系列,三分钟时间不长,无法系统的介绍一个知识点,但能够描述清楚一个知识点...

skinner
04/18
0
0
5分钟构建spring web mvc REST风格HelloWorld

当然写本文的目的不是为了速度,只是表明现在构建一个Spring web mvc Rest风格的HelloWorld应用会很简单。不过如果看过Spring Boot这个项目,可能只需要最多3分钟就能构建一个简单的Rest风格...

黄金小猪2号
2014/01/07
4.3K
2
SpringBoot 在IDEA中实现热部署(实用版)

好的热部署让开发调试事半功倍,这样的“神技能”怎么能错过呢, 使用过IDEA的童鞋赶紧进来撸一把吧。 学习目标 快速学会在项目中使用热部署插件运行项目,提高开发效率。 快速查阅 相关教程...

yizhiwazi
2018/06/20
0
0
三分钟GET一个新技能┃03 自律神器:只花3分钟,每天至少帮你省出三小时

文/职场逗伴匠 1. 大家听说过“下雨四大怪”么? 我听说过。 啥意思?这所谓的“下雨四大怪”分别是, ①下雨必没伞 ②买伞必雨停 ③出门伞必丢 ④带伞必晴天 有同感,扎心了?尴尬不...... ...

职场逗伴匠
2018/06/05
0
0
openhwapi/HYLI_SMART_FAN

#HYLISMARTFAN 这是三分钟教你学会智能硬件系列的第三篇。第一篇教大家DIY一个蓝牙防丢器,不过那里的硬件部分仅仅是连连线而已;第二篇教自制一个蓝牙计步手环,那里重点放到安卓炫酷客户端...

openhwapi
2016/05/11
0
0

没有更多内容

加载失败,请刷新页面

加载更多

【在 Nervos CKB 上做开发】Nervos CKB 脚本编程简介[3]:自定义代币

原文作者:Xuejie 原文链接:https://xuejie.space/2019_09_06_introduction_to_ckb_script_programming_udt/ Nervos CKB 脚本编程简介[3]:自定义代币 CKB 的 Cell 模型和 VM 支持许多新的用...

NervosCommunity
49分钟前
4
0
通过OAuth2.0 获取授权访问SF 用户数据

通过OAuth2.0 获取授权访问SF 用户数据 OAuth2.0 相关知识 深入了解 Salesforce 中的 OAuth 2.0(SF官方) OAuth 2.0 的一个简单解释(阮一峰大神) OAuth 2.0 的四种方式(阮一峰大神) GitHub OA...

在山的那边
55分钟前
7
0
编写程序:从键盘上接受一个三位数(首先要确保是三位数),计算出各位之和输出。

#include<stdio.h> int main() { int a,sum=0; printf("请输入一个三位数:\n"); scanf("%d",&a); sum=a/100+a%100/10+a%10; printf("这三个数的和:%d",sum); return 0; }......

201905021729吴建森
今天
7
0
如何离开/退出/停用Python virtualenv

我正在使用virtualenv和virtualenvwrapper。 我可以使用workon命令在virtualenv之间切换。 me@mymachine:~$ workon env1(env1)me@mymachine:~$ workon env2(env2)me@mymachine:~$ workon e......

技术盛宴
今天
7
0
成长之路 万事坚持难

任何事情开了头,想要更好的发展下去,不忘初心,就一定要坚持下去。 以前自己坚持了一些事情,比如早睡不吃东西,由于中途断了,没有及时止损,导致又接着恶习断了几天。所以 及时的反省和调...

T型人才追梦者
今天
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部