通过GroovySDL实现GradleScript

文章目录

          • Groovy是什么
          • Groovy Dependency
          • DSL是什么
          • 初识Groovy DSL
          • Closure, Delegate, Script & Shell
          • Compilation Customizers
          • DSL Style Customizer
          • DSL风格脚本展示

Groovy是什么

Groovy是一种在JVM上运行的敏捷开发语言

Groovy 80%的语法和Java完全一致,同时吸取了其它语言的一些灵活性特征

Java编程者想要学习Groovy,只要不刻意去追求高阶用法,基本一天就能学会

Groovy Dependency
plugins {id 'groovy'
}repositories {maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' }google()mavenCentral()
}dependencies {implementation 'org.apache.groovy:groovy:4.0.17'implementation 'org.codehaus.groovy:groovy-all:3.0.21'implementation 'org.apache.ivy:ivy:2.5.2'implementation 'com.google.guava:guava:33+'
}
DSL是什么

DSL全称Domain Specific Language

是一种弱化编程特性,更专注于业务逻辑表达的一种语言

DSL的语法更加简洁,对专业编程能力要求低,尤其适合用于编写命令脚本,比如Gradle脚本就是这种风格

但DSL的简洁性背后,往往都有专业的面向对象风格代码在支撑,但是用户只和DSL这一层打交道

一些比较新的语言,都提供了这种风格的调用方式,比如Groovy DSL,Kotlin DSL

本章要讲的,就是Groovy DSL的实现方式

初识Groovy DSL
def show = { println it }
def square = { Math.pow(it, 2) as int }
def squareRoot = { Math.sqrt(it) as int }def please(action) {[the: { what ->[of: { n -> action(what(n)) }]}]
}please show the square of 100
please show the squareRoot of 100

这是一个完整的Groovy文件

Groovy既可以按脚本的方式,直接执行整个文件

也可以按面向对象的方式,通过静态main方法来启动

可以看到,上半部分是面向对象风格的代码,下半部分是DSL风格的代码

上半部分用于实现DSL,而下半部分则是DSL调用

有了上半部分的支撑,我们可以像讲白话一样,让脚本去完成指定的工作

之所以我们在使用Gradle时,看不到上半部分,是因为Groovy提供了脚本封装功能

我们可以将上半部分代码封装为一个Script,然后使用该Script来执行Gradle脚本

Closure, Delegate, Script & Shell

上面介绍的,是Groovy定义方法的用法之一,主要用来展示DSL语法风格

用过Gradle脚本的应该知道,Gradle脚本是存在多个对象,和对象层级嵌套的

如果通过以上风格来编写代码,必定会非常复杂

Groovy提供了专门的类来解析和执行脚本,主要有

  • Closure:闭包代码块,对应Gradle中{ … }的全部内容
  • Closure代码块是可以执行的,可以为其指定一个隐藏的context对象,类似于this
  • Closure代码块中的代码,实际都是通过这个context对象来执行
  • 在Groovy中我们一般把这个context对象称为Delegate,因为代码是交给它来代理执行的
  • Script:整个Gradle脚本,可以看成一个大的Closure,这个顶级Closure的context,就是Script
  • Shell:用来配置和执行脚本,它可以指定脚本位置,指定用哪个Script执行脚本,还可以向脚本中注入对象等
// GradleScript.groovypackage com.code.groovyabstract class GradleScript extends Script {abstract void script()def email(Closure closure) {def delegateContext = new EmailSpec()def delegatedClosure = closure.rehydrate(delegateContext, this, this)delegatedClosure.resolveStrategy = Closure.DELEGATE_ONLYdelegatedClosure()}@OverrideObject run() {println ''script()println ''return 100}
}class EmailSpec {void from(String from) { println "From: $from" }void to(String... to) { println "To: $to" }void subject(String subject) { println "Subject: $subject" }void body(Closure closure) {def delegateContext = new BodySpec()def delegatedClosure = closure.rehydrate(delegateContext, this, this)delegatedClosure.resolveStrategy = Closure.DELEGATE_ONLYdelegatedClosure()}
}class BodySpec {void p(String content) { System.err.println "    $content" }
}
// GradleShell.groovypackage com.code.groovyimport org.codehaus.groovy.control.CompilerConfiguration// bind property
def binding = new Binding()
binding.setVariable('message', "Gradle Script Run Finished")
binding.setProperty('date', new Date().toString())
binding.setProperty('email.body.date', new Date().toString())
// specify script class
def config = new CompilerConfiguration()
config.scriptBaseClass = GradleScript.class.name
// load and execute shell
def shell = new GroovyShell(binding, config)
def scriptFile = new File('./script.gradle')
def scriptSource = new GroovyCodeSource(scriptFile, 'utf-8')
shell.evaluate(scriptSource)
// script.gradledef binding = binding
email {from 'LiNing@groovy.com'to 'Tom@@groovy.com', 'Jimmy@groovy.com'subject 'Groovy Started'body {p "Hi, let's start learning English now !"p binding['date']p binding['email.body.date']}
}println message
// outputFrom: LiNing@groovy.com
To: [Tom@@groovy.com, Jimmy@groovy.com]
Subject: Groovy StartedHi, let's start learning English now !Fri Mar 15 10:50:44 CST 2024Fri Mar 15 10:50:44 CST 2024
Gradle Script Run Finished
Compilation Customizers

Groovy还提供了一些配置类,用于自定义编译规则

比如自动导入类,向脚本中添加默认变量,修改脚本执行逻辑等

// CustomizerShell.groovyimport groovy.transform.ConditionalInterrupt
import org.codehaus.groovy.ast.Parameter
import org.codehaus.groovy.ast.expr.ClosureExpression
import org.codehaus.groovy.ast.expr.ConstantExpression
import org.codehaus.groovy.ast.stmt.ReturnStatement
import org.codehaus.groovy.control.CompilerConfiguration
import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer
import groovy.util.logging.Log
import org.codehaus.groovy.control.customizers.ImportCustomizer
import org.codehaus.groovy.control.customizers.SecureASTCustomizer// auto import class with alias
def importCustomizer = new ImportCustomizer()
importCustomizer.addImport('Map', 'java.util.concurrent.ConcurrentHashMap')
// inject annotation into script by ast transformation
def annotationCustomizer = new ASTTransformationCustomizer(Log, value: 'LOGGER')
// auto interrupt on matched statement
def statement = new ReturnStatement(ConstantExpression.FALSE)
def expression = new ClosureExpression(Parameter.EMPTY_ARRAY, statement)
def interruptCustomizer = new ASTTransformationCustomizer(ConditionalInterrupt,value: expression,thrown: SecurityException
)
// restrict accessible types by ast transformation
def secureCustomizer = new SecureASTCustomizer()
secureCustomizer.with {allowedReceiversClasses = [Object, String, Integer].asImmutable()
}
// add customizers to shell config
def config = new CompilerConfiguration()
config.addCompilationCustomizers(importCustomizer)
config.addCompilationCustomizers(annotationCustomizer)
config.addCompilationCustomizers(interruptCustomizer)
config.addCompilationCustomizers(secureCustomizer)
// execute with config and script
def shell = new GroovyShell(config)
def scriptFile = new File('./script.gradle')
def scriptSource = new GroovyCodeSource(scriptFile, 'utf-8')
shell.evaluate(scriptSource)
// script.gradle// Logger is injected
println LOGGER.class.name
LOGGER.info 'Hello'// use ConcurrentHashMap with alias
def map = new Map()
map['name'] = 'ConcurrentHashMap'
println map['name']// injected variables are regard as Object type
// so map is allowed as method caller
map.put('name', 'ConcurrentHashMap')// throw exception on return-false statement
Boolean find() {return false
}
println find()// Date is not a allowed type as method caller
new Date().getTime()
DSL Style Customizer

到此为止,我们已经大致了解一个Script是如何被加载和执行的

不知道大家有没有发现,我们像是在写一个编译器,只是这个编译器框架是Groovy封装好的

核心的内容上面已经讲解完了,这一节我们简单演示一下,Groovy内置的一些DSL风格的编译组件

// CompilerCustomizationBuilder.class// register internal customizer factorys
public CompilerCustomizationBuilder() {this.registerFactory("customizers", new CustomizersFactory());this.registerFactory("ast", new ASTTransformationCustomizerFactory());this.registerFactory("imports", new ImportCustomizerFactory());this.registerFactory("inline", new InlinedASTCustomizerFactory());this.registerFactory("secureAst", new SecureASTCustomizerFactory());this.registerFactory("source", new SourceAwareCustomizerFactory());
}// inject customizers to compile config
// closure specify customizers that created by factorys
public static CompilerConfiguration withConfig(CompilerConfiguration config, Closure closure)
// GradleShell.groovyimport groovy.transform.ToString
import groovy.util.logging.Log
import org.codehaus.groovy.control.CompilePhase
import org.codehaus.groovy.control.CompilerConfiguration
import org.codehaus.groovy.control.customizers.builder.CompilerCustomizationBuilderdef config = new CompilerConfiguration()
CompilerCustomizationBuilder.withConfig(config) {inline(phase: CompilePhase.SEMANTIC_ANALYSIS) { source, context, classNode ->println 'visiting $classNode'}imports{normal 'java.util.Date'normal 'groovy.transform.ToString'}ast(Log, value: 'LOGGER')ast(ToString)
}
def script = new File('./script.gradle')
def shell = new GroovyShell(config)
shell.evaluate(script)
// script.gradledef date = new Date()class QQQ {def name = "LiNing"def age = 28
}LOGGER.info date.toString()
LOGGER.info new QQQ().toString()
DSL风格脚本展示
import groovy.json.JsonBuilder
import groovy.json.JsonOutputdef builder = new JsonBuilder()
builder.records {car {name 'HSV Maloo'make 'Holden'year 2006country 'Australia'record {type 'speed'description 'production pickup truck with speed of 271kph'}}
}
String json = JsonOutput.prettyPrint(builder.toString())
println json
def builder = new NodeBuilder()
def userlist = builder.userlist {user(id: '1', firstname: 'John', lastname: 'Smith') {address(type: 'home',street: '1 Main St.',city: 'Springfield',state: 'MA',zip: '12345')address(type: 'work',street: '2 South St.',city: 'Boston',state: 'MA',zip: '98765')}user(id: '2', firstname: 'Alice', lastname: 'Doe')
}
def users = userlist.user.@firstname.join(', ')
println users

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/754009.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

9成省份“鸿蒙化”,它真起来了?

自去年9月华为宣布鸿蒙原生应用全面启动以来,鸿蒙正以不可阻挡之势,快速在全国千行百业的移动应用领域推进。不仅有支付宝、快手、淘宝、京东等超200家头部互联网企业加入鸿蒙生态;2024年以来,上海、浙江、广西等多省市政务民生、…

C++_day6

思维导图: 2试编程 封装一个动物的基类,类中有私有成员: 姓名,颜色,指针成员年纪 再封装一个狗这样类,共有继承于动物类,自己拓展的私有成员有:指针成员:腿的个数(整型 int count),共有成员函数…

2024全新红娘交友系统定制版源码 | 相亲交友小程序源码 全开源可二开

内容目录 一、详细介绍二、效果展示1.部分代码2.效果图展示 三、学习资料下载 一、详细介绍 全新红娘交友系统定制版源码 | 相亲交友小程序源码 全开源可二开 定制版红娘交友平台小程序源码,很牛逼的东西,虽然是小程序,但是有700多M大&…

modbus客户端

通信方式支持 串口 / udp / tcp通信; 设备协议支持RTU / ASCII / TCP; 读取类型支持bool / short / int / float / double / long / batchbool / batchword

JAVA八股day1

遇到的问题 相比于包装类型(对象类型), 基本数据类型占用的空间往往非常小为什么说是几乎所有对象实例都存在于堆中呢?静态变量和成员变量、成员变量和局部变量的区别为什么浮点数运算的时候会有精度丢失的风险?如何解…

MySQL Enterprise Backup (MEB) for MySQL 8.0 深入解析及企业级备份恢复演练

一、备份恢复核心概念 MEB简介 MySQL Enterprise Backup 是 Oracle 提供的专业物理备份工具,专为MySQL 8.0及以后版本设计,提供高效且低干扰的在线备份解决方案。它能够实现对InnoDB存储引擎数据的快速备份,并兼容MySQL的诸多高级特性。 主…

【Chapter1】操作系统概述,计算机操作系统教程,第四版,左万利,王英

文章目录 一、操作系统的基本概念1.1操作系统的层次结构1.2操作系统的运行视图1.3操作系统的概念(定义)1.4操作系统的功能和目标1.4.1操作系统的功能和目标——作为系统资源的管理者1.4.2操作系统的功能和目标——向上层提供方便易用的服务1.4.2.1GUI:图形化用户接口…

python 基础知识点(蓝桥杯python科目个人复习计划65)

今日复习内容:做题 例题1:遥远的雪国列车 问题描述: 小蓝和小红今天在房间里一起看完了“雪国列车”这部电影,看完之后他们感触颇深,同时他们想到了这样一道题目: 现在有一个数轴,长度为N&a…

PyTorch学习笔记之激活函数篇(二)

文章目录 2、Tanh函数2.1 公式2.2 对应的图像2.3 对应生成图像代码2.4 优点与不足2.5 torch.tanh()函数 2、Tanh函数 2.1 公式 Tanh函数的公式: f ( x ) e x − e − x e x e − x f(x)\frac{e^x-e^{-x}}{e^xe^{-x}} f(x)exe−xex−e−x​ Tanh函数的导函数&am…

在Latex中优雅的插入svg图片(Ubuntu22.04)

文章目录 一、前言二、准备工作三、脚本编程四、结论 一、前言 在 LaTeX \LaTeX LATE​X 中,插入图片常用的为 figure 环境加 \includegraphics 命令: \begin{figure}[!htbp]\centering\includegraphics[width\textwidth]{图片名.jpg/jpeg/png/pdf}\c…

Java SE入门及基础(40)

目录 自定义异常 1. 为什么要使用自定义异常 自定义运行时异常语法 自定义检查异常语法 示例 2. 异常使用注意事项 Java SE文章参考:Java SE入门及基础知识合集-CSDN博客 自定义异常 1. 为什么要使用自定义异常 在Java 中,异常的类型非常的多,…

CSS 零基础入门教程

目录 1. div 和 span2. 什么是CSS?3. CSS 引入方式3.1 内部样式表3.2 外部样式表3.3 行内样式 4. 选择器4.1 标签选择器4.2 类选择器4.3 id 选择器4.4 通配符选择器 5. CSS 基础属性6. 谷歌浏览器调试工具 正文开始。 1. div 和 span 在学习 CSS 之前,…

什么是网站?为什么要搭建网站?

网站:简单来说,网站就是通过互联网来展示信息的页面集合。它可以在电脑或者手机上打开,提供各种功能,比如查看新闻、购买商品、搜索信息等。 一、建网站的目的:展示个人或企业的存在 网站建设的首要目的之一是展示个人…

【C++】三大特性之多态

1 定义及实现 1.1 概念 多态是C三大特性之一。通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。 多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。比如学…

简化您的网络连接 —— socks5代理,怎么轻松设置

您需要 SOCKS5 代理吗?在本文中,我们将向您展示逐步的 SOCKS5 代理设置。了解如何获取 SOCKS5 和其他代理协议 —— HTTP 和 HTTPS 代理。 正在寻找 SOCKS5 代理?幸运的是,Smartdaili 现在支持 SOCKS5 协议以及住宅、移动和专用数…

Java后端面试:框架篇高频面试(Spring、SpringMVC、SpringBoot、MyBatis)

👨‍🎓作者简介:一位大四、研0学生,正在努力准备大四暑假的实习 🌌上期文章:Java后端面试:MySQL面试篇(底层事务、SQL调优) 📚订阅专栏:Java后端面…

十四、ReadWriteLock

ReadWriteLock 读写锁 又叫排他锁 如果使用互斥锁,一个线程在读,其他线程也不能读也不能写 换成读写锁的时候,读线程是读锁,写线程是写锁,写锁是排他的 在多线程大大提高效率,当一个线程在读的时候&…

高可用篇_A Docker容器化技术_IV Dockerfile的基本概念和应用

原创作者:田超凡(程序员田宝宝) 版权所有,引用请注明原作者,严禁复制转载 DockerFile 解析 一个镜像文件到底是如何创建? dockerfile 描述出镜像文件需要的一些依赖配置和环境变量 执行命令将我们docker…

glib交叉编译

Glib交叉编译 逸一时,误一世。 —— 田所浩二「夏夜银梦」 交叉编译 GLib 涉及到在一个平台上生成能够在另一个平台上运行的目标文件。在这种情况下,我们将会在一台主机(通常是开发机器)上使用交叉编译工具链来构建 GLib 库&#…

从历年315曝光案例,看APP隐私合规安全

更多网络安全干货内容:点此获取 ——————— 随着移动互联网新兴技术的发展与普及,移动APP的应用渗透到人们的衣食住行方方面面,衍生出各类消费场景的同时,也带来了无数的个人隐私数据泄露、网络诈骗事件。 历年来&#xff…