第8章 Spring Boot集成Groovy,Grails开发

本章介绍Spring Boot集成Groovy,Grails开发。我们将开发一个极简版的pms(项目管理系统)。

Groovy和Grails简介

Groovy简介

Groovy 是一种动态语言,它在 JVM 上运行,并且与 Java 语言无缝集成。

Groovy 可以大大减少 Java 代码的数量。在 Groovy 中,不再需要为字段编写 getter 和 setter 方法,因为 Groovy 会自动提供它们。不再需要编写 for Iterator i = list.iterator() 来循环遍历一系列的项;list.each 可以做相同的事情,而且看上去更简洁,表达更清晰。简言之,Groovy 就是 21 世纪的 Java 语言。[2]

Groovy 不会替代 Java 语言 — 它只是提供了增强。您可以很快地掌握 Groovy,因为说到底,Groovy 代码就是 Java 代码。这两种语言是如此兼容,甚至可以将一个 .java 文件重命名为一个 .groovy 文件 — 例如,将 Person.java 改为 Person.groovy — 从而得到一个有效的(可执行的)Groovy 文件(虽然这个 Groovy 文件并没有用到 Groovy 提供的任何语法)。

Grails简介

Grails是一套用于快速Web应用开发的开源框架,它基于Groovy编程语言,并构建于Spring、Hibernate等开源框架之上,是一个高生产力一站式框架。

Grails这个独特的框架被视为是提升工程师生产效率的动态工具,因为其干脆的API设计,合理的默认值以及约定架构。与java的无缝集成使得这个框架成为世界上众多框架中的首选。一系列强大的特性,如基于sping的依赖注入和各式各样的插件,可以提供创建现代基于web的app的所有需要的东西。

我们使用Grails框架。就像 Rails 与 Ruby 编程语言联系非常紧密一样,Grails 也离不开 Groovy。

DRY(Don't Repeat Yourself,不要重复自己)
约定优于配置(Convention over Configuration)

DRY和约定优先于配置的思想,是由Rails兴起并迅速被广泛接收和欣赏的Web框架新思路。Grails作为JEE世界的Rails,把这些最前沿的设计理念带入已显得陈旧的JEE社区,拥有鲜明突出的特点,以及由此带来的优秀的开发效率。

对Grails来说,Groovy是其能够实现灵活多变的快速开发,区别于其他运行于JVM之上的Web框架的核心技术。

Groovy的动态特性是其最大亮点,在这方面几乎不输于Ruby等其他热门的动态语言。[3]

Grails实现原理

  • 基于Spring MVC的控制器层

  • 构建于Gant 上的命令行脚本运行环境,内置Tomcat服务器,不用重新启动服务器就可以进行重新加载

  • 基于Spring的MessageSource核心概念,提供了对国际化(i18n)的支持

  • 基于Spring事务抽象概念,实现事务服务层[1]

Github:https://github.com/grails
官网:https://grails.org/

数据库的对象关系映射层使用GORM

我们使用 Grail 对象关系映射(Grails Object Relational Mapping,GORM)API 进行数据库层的持久化工作。

安装Grails 3 开发环境

浏览器访问 http://www.grails.org/Download,下载,解压,设置环境变量即可。具体步骤如下:

1.下载并解压 grails.zip。
2.创建一个 GRAILS_HOME 环境变量。
3.将 $GRAILS_HOME/bin 添加到 PATH中。

如果你的电脑上有SDKMAN! (The Software Development Kit Manager),可以直接命令行自动安装Grails最新稳定版本:

$ sdk install grails

安装完毕,验证一下:

$ grails -v| Grails Version: 3.2.8
| Groovy Version: 2.4.10
| JVM Version: 1.8.0_40

OK, grails开发环境搞定。我们可以看到,grails依赖的Groovy,JVM环境版本。

创建Grails项目

让我们来体验JVM上的Ruby on rails式的命令行自动工程生成的快感吧!

命令行直接运行:

$ grails create-app pms
Resolving dependencies..
| Application created at /Users/jack/book/pms

我们就生成了一个grails工程demo,目录如下:

.
├── build.gradle
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── grails-app
│   ├── assets
│   │   ├── images
│   │   │   ├── apple-touch-icon-retina.png
│   │   │   ├── apple-touch-icon.png
│   │   │   ├── favicon.ico
│   │   │   ├── grails-cupsonly-logo-white.svg
│   │   │   ├── skin
│   │   │   │   ├── database_add.png
│   │   │   │   ├── database_delete.png
│   │   │   │   ├── database_edit.png
│   │   │   │   ├── database_save.png
│   │   │   │   ├── database_table.png
│   │   │   │   ├── exclamation.png
│   │   │   │   ├── house.png
│   │   │   │   ├── information.png
│   │   │   │   ├── shadow.jpg
│   │   │   │   ├── sorted_asc.gif
│   │   │   │   └── sorted_desc.gif
│   │   │   └── spinner.gif
│   │   ├── javascripts
│   │   │   ├── application.js
│   │   │   ├── bootstrap.js
│   │   │   └── jquery-2.2.0.min.js
│   │   └── stylesheets
│   │       ├── application.css
│   │       ├── bootstrap.css
│   │       ├── errors.css
│   │       ├── grails.css
│   │       ├── main.css
│   │       └── mobile.css
│   ├── conf
│   │   ├── application.yml
│   │   ├── logback.groovy
│   │   └── spring
│   │       └── resources.groovy
│   ├── controllers
│   │   └── pms
│   │       └── UrlMappings.groovy
│   ├── domain
│   ├── i18n
│   │   ├── messages.properties
│   │   ├── messages_cs_CZ.properties
│   │   ├── messages_da.properties
│   │   ├── messages_de.properties
│   │   ├── messages_es.properties
│   │   ├── messages_fr.properties
│   │   ├── messages_it.properties
│   │   ├── messages_ja.properties
│   │   ├── messages_nb.properties
│   │   ├── messages_nl.properties
│   │   ├── messages_pl.properties
│   │   ├── messages_pt_BR.properties
│   │   ├── messages_pt_PT.properties
│   │   ├── messages_ru.properties
│   │   ├── messages_sv.properties
│   │   ├── messages_th.properties
│   │   └── messages_zh_CN.properties
│   ├── init
│   │   └── pms
│   │       ├── Application.groovy
│   │       └── BootStrap.groovy
│   ├── services
│   ├── taglib
│   ├── utils
│   └── views
│       ├── error.gsp
│       ├── index.gsp
│       ├── layouts
│       │   └── main.gsp
│       └── notFound.gsp
├── grails-wrapper.jar
├── grailsw
├── grailsw.bat
├── settings.gradle
└── src├── integration-test│   └── groovy├── main│   ├── groovy│   └── webapp└── test└── groovy29 directories, 62 files

这真的是一键生成。

我们可以直接使用下面的命令运行这个工程:

$ grails run-app

它会自动下载gradle-3.4.1-bin.zip(通常会很慢):

| Resolving Dependencies. Please wait...
Downloading https://services.gradle.org/distributions/gradle-3.4.1-bin.zip

如果我们本地有gradle环境,我们也可将此工程导入idea,配置一下本地的gradle环境。如下图所示:

首次构建,gradle需要下载工程依赖的jar包。

我们可以看到build.gradle里面已经配置好一切:

buildscript {repositories {mavenLocal()maven { url "https://repo.grails.org/grails/core" }}dependencies {classpath "org.grails:grails-gradle-plugin:$grailsVersion"classpath "com.bertramlabs.plugins:asset-pipeline-gradle:2.14.1"classpath "org.grails.plugins:hibernate5:${gormVersion-".RELEASE"}"}
}version "0.1"
group "pms"apply plugin:"eclipse"
apply plugin:"idea"
apply plugin:"war"
apply plugin:"org.grails.grails-web"
apply plugin:"org.grails.grails-gsp"
apply plugin:"asset-pipeline"repositories {mavenLocal()maven { url "https://repo.grails.org/grails/core" }
}dependencies {compile "org.springframework.boot:spring-boot-starter-logging"compile "org.springframework.boot:spring-boot-autoconfigure"compile "org.grails:grails-core"compile "org.springframework.boot:spring-boot-starter-actuator"compile "org.springframework.boot:spring-boot-starter-tomcat"compile "org.grails:grails-dependencies"compile "org.grails:grails-web-boot"compile "org.grails.plugins:cache"compile "org.grails.plugins:scaffolding"compile "org.grails.plugins:hibernate5"compile "org.hibernate:hibernate-core:5.1.3.Final"compile "org.hibernate:hibernate-ehcache:5.1.3.Final"console "org.grails:grails-console"profile "org.grails.profiles:web"runtime "com.bertramlabs.plugins:asset-pipeline-grails:2.14.1"runtime "com.h2database:h2"testCompile "org.grails:grails-plugin-testing"testCompile "org.grails.plugins:geb"testRuntime "org.seleniumhq.selenium:selenium-htmlunit-driver:2.47.1"testRuntime "net.sourceforge.htmlunit:htmlunit:2.18"
}bootRun {jvmArgs('-Dspring.output.ansi.enabled=always')addResources = true
}assets {minifyJs = trueminifyCss = true
}

我们在application.yml里面配置一下server.port (默认8080):

server:port: 8008

命令行执行(我们也可以使用grails run-app运行工程,区别是grails会下载外部gradle包,配置的gradle环境不是本地机器):

gradle bootRun

你将看到类似如下启动日志:

02:18:02: Executing external task 'bootRun'...
:compileJava NO-SOURCE
:compileGroovy UP-TO-DATE
:buildProperties UP-TO-DATE
:processResources
:classes
:findMainClass
objc[27257]: Class JavaLaunchHelper is implemented in both /Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/bin/java (0x1001e44c0) and /Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre/lib/libinstrument.dylib (0x101a4e4e0). One of the two will be used. Which one is undefined.
:bootRun
Grails application running at http://localhost:8008 in environment: development

启动完毕,访问http://localhost:8008/,你将看到如下页面:

螢幕快照 2017-04-15 02.10.49.png

为了演示上的简易性,数据库我们直接用的是H2,在application.yml配置如下:

hibernate:cache:queries: falseuse_second_level_cache: trueuse_query_cache: falseregion.factory_class: org.hibernate.cache.ehcache.SingletonEhCacheRegionFactorydataSource:pooled: truejmxExport: truedriverClassName: org.h2.Driverusername: sapassword:environments:development:dataSource:dbCreate: create-dropurl: jdbc:h2:mem:devDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSEtest:dataSource:dbCreate: updateurl: jdbc:h2:mem:testDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSEproduction:dataSource:dbCreate: noneurl: jdbc:h2:./prodDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSEproperties:jmxEnabled: trueinitialSize: 5maxActive: 50minIdle: 5maxIdle: 25maxWait: 10000maxAge: 600000timeBetweenEvictionRunsMillis: 5000minEvictableIdleTimeMillis: 60000validationQuery: SELECT 1validationQueryTimeout: 3validationInterval: 15000testOnBorrow: truetestWhileIdle: truetestOnReturn: falsejdbcInterceptors: ConnectionStatedefaultTransactionIsolation: 2 # TRANSACTION_READ_COMMITTED

建立领域模型Model层

我们继续在当前工程根目录下。使用grails的create-domain-class命令创建领域类:

$grails create-domain-class Project

执行这个命令,grails也会下gradle包。下载完,grails程序自动解压,放到约定的目录,日志如下:

| Resolving Dependencies. Please wait...
Downloading https://services.gradle.org/distributions/gradle-3.4.1-bin.zip
.
.
.Unzipping /Users/jack/.gradle/wrapper/dists/gradle-3.4.1-bin/71zneekfcxxu7l9p7nr2sc65s/gradle-3.4.1-bin.zip to /Users/jack/.gradle/wrapper/dists/gradle-3.4.1-bin/71zneekfcxxu7l9p7nr2sc65s
Set executable permissions for: /Users/jack/.gradle/wrapper/dists/gradle-3.4.1-bin/71zneekfcxxu7l9p7nr2sc65s/gradle-3.4.1/bin/gradle
Cleaned up directory '/Users/jack/book/pms/build/classes/main'
Cleaned up directory '/Users/jack/book/pms/build/resources/main'CONFIGURE SUCCESSFULTotal time: 2 mins 43.589 secs
| Created grails-app/domain/pms/Project.groovy
| Created src/test/groovy/pms/ProjectSpec.groovy

我们继续创建项目Project的里程碑Milestone领域类:

$ grails create-domain-class Milestone
| Created grails-app/domain/pms/Milestone.groovy
| Created src/test/groovy/pms/MilestoneSpec.groovy

我们可以看到这两个类的代码如下:

package pmsclass Project {static constraints = {}
}package pmsclass Milestone {static constraints = {}
}

一开始,没有什么内容。其中,static constraints变量里面主要定义对应的实体类的约束条件。

下面我们来设计领域对象的属性。

一个项目Project,我们极简化处理,取几个代表的属性,比如:名称,负责人,开始时间,结束时间,状态等。

package pmsclass Project {static constraints = {}Integer idString nameString ownerDate startDateDate endDateString status
}

通常,一个项目,会有多个里程碑,所以我们这里的里程碑表,多条记录对应一个Project。里程碑属性我们就取: 关联的项目id,名称,负责人,计划时间,实际时间,状态。

package pmsclass Milestone {static constraints = {}Integer idInteger projectIdString nameString ownerDate expectDateDate actualDateString status}

使用grails脚手架自动生成Controller层,视图View层代码

grails的脚手架控制值相当简易,简单易用。我们可以使用

grails create-controller $DomainName  : 创建DomainName对应的空Controllergrails generate-controller $DomainName :创建DomainName对应的包含CRUD的Controllergrails generate-all $DomainName: 创建DomainName对应的包含CRUD的Controller,以及对应的视图view模板代码

下面我们就使用grails generate-all来创建Project,Milestone的Controller,以及视图。

$ grails generate-all Project
| Rendered template Controller.groovy to destination grails-app/controllers/pms/ProjectController.groovy
| Rendered template Spec.groovy to destination src/test/groovy/pms/ProjectControllerSpec.groovy
| Scaffolding completed for grails-app/domain/pms/Project.groovy
| Rendered template create.gsp to destination grails-app/views/project/create.gsp
| Rendered template edit.gsp to destination grails-app/views/project/edit.gsp
| Rendered template index.gsp to destination grails-app/views/project/index.gsp
| Rendered template show.gsp to destination grails-app/views/project/show.gsp
| Views generated for grails-app/domain/pms/Project.groovy$ grails generate-all Milestone
| Rendered template Controller.groovy to destination grails-app/controllers/pms/MilestoneController.groovy
| Rendered template Spec.groovy to destination src/test/groovy/pms/MilestoneControllerSpec.groovy
| Scaffolding completed for grails-app/domain/pms/Milestone.groovy
| Rendered template create.gsp to destination grails-app/views/milestone/create.gsp
| Rendered template edit.gsp to destination grails-app/views/milestone/edit.gsp
| Rendered template index.gsp to destination grails-app/views/milestone/index.gsp
| Rendered template show.gsp to destination grails-app/views/milestone/show.gsp
| Views generated for grails-app/domain/pms/Milestone.groovy

下面是创建之后的工程目录:

我们可以看出,通过统一的约定,我们得到规整的目录结构。很好的体现了“约定优于配置(Convention over Configuration)”的方法论思想。

对控制器的理解可以归结为三个 R:return、redirect 和 render。有些动作利用隐式的 return 语句将数据返回到具有相同名称的 GSP 页面。有些动作进行重定向。

我们看一下ProjectController的index方法:

    def index(Integer max) {params.max = Math.min(max ?: 10, 100)respond Project.list(params), model:[projectCount: Project.count()]}

我们没有写list,count,hasErros等方法,GROM都帮我们打理好一切了。具体的实现源码在org.grails.datastore.gorm里面。这个处理方案跟Spring-jpa的思想基本是一样的。都是通过注解元编程,动态生成相应的方法代码。

部署测试

完成上述步骤,我们就已经有了包含CRUD基本功能的Web应用了,使用

gradle bootRun

命令运行工程,使用浏览器访问:http://localhost:8008/
你将看到如下页面:

我们可以看到,“Available Controllers”列表,这个功能模块是通过如下一段gsp代码实现的:

            <div id="controllers" role="navigation"><h2>Available Controllers:</h2><ul><g:each var="c" in="${grailsApplication.controllerClasses.sort { it.fullName } }"><li class="controller"><g:link controller="${c.logicalPropertyName}">${c.fullName}</g:link></li></g:each></ul></div>

新建一个Project,保存,如下图:

点击Project列表页:

编辑该项目:

Grails通过UrlMappings统一Url映射,简化了Controller到View的映射路径的代码。只要我们按照“约定”的目录结构组织我们的代码即可。

package pmsclass UrlMappings {static mappings = {"/$controller/$action?/$id?(.$format)?"{constraints {// apply constraints here}}"/"(view:"/index")"500"(view:'/error')"404"(view:'/notFound')}
}

Grails框架里面充满了大量“约定规则”,按照“约定规则”编程,我们看到了,代码是如此之“极简”。

我们简单看一个例子。如下图:

这里的“New Milestone”,是怎么实现的呢?我们来看一下milestone/index.gsp里面的一段代码:

<g:message code="default.list.label" args="[entityName]" />

这里的default.list.label值配置在i18n/messages.properties里面。

default.home.label=Home
default.list.label={0} List
default.add.label=Add {0}
default.new.label=New {0}
default.create.label=Create {0}
default.show.label=Show {0}
default.edit.label=Edit {0}default.button.create.label=Create
default.button.edit.label=Edit
default.button.update.label=Update
default.button.delete.label=Delete
default.button.delete.confirm.message=Are you sure?

不过,在这种.properties配置文件中,中文可读性比较差。类似这样子:

default.blank.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u4E0D\u80FD\u4E3A\u7A7A

gsp代码中,以 g: 为前缀的就是 GroovyTag。

本章pms项目工程源码:
https://github.com/EasySpringBoot/pms

小结

参考资料

1.http://baike.baidu.com/item/grails
2.https://www.ibm.com/developerworks/cn/java/j-grails01158/
3.http://www.infoq.com/cn/articles/case-study-grails-partii/

《Spring Boot极简教程》第8章 Spring Boot集成Groovy,Grails开发相关推荐

  1. 《React极简教程》第一章 Hello,World!

    react React 是一个用于构建用户界面的 JAVASCRIPT 库. React主要用于构建UI,很多人认为 React 是 MVC 中的 V(视图). React 起源于 Facebook ...

  2. 《MongoDB极简教程》第一章 NoSQL简史 amp; MongoDB安装amp;环境配置

    MongoDB 是一款开源的文档数据库,并且是业内领先的 NoSQL 数据库,用 C++ 编写而成. NoSQL (NoSQL = Not Only SQL ),意即"不仅仅是SQL&quo ...

  3. 《MongoDB极简教程》第一章 NoSQL简史 MongoDB安装环境配置

    MongoDB 是一款开源的文档数据库,并且是业内领先的 NoSQL 数据库,用 C++ 编写而成. NoSQL (NoSQL = Not Only SQL ),意即"不仅仅是SQL&quo ...

  4. LLVM极简教程: 第三章 LLVM IR代码生成

    第三章 LLVM IR代码生成 原文: Code generation to LLVM IR 本章简介 欢迎进入"用LLVM开发新语言"教程的第三章.本章将介绍如何把第二章中构造的 ...

  5. 《Spring Boot极简教程》附录3 编程简史

    附录3 编程简史 1940之前 提花织布机 1801,Joseph Marie Jacquard用打孔卡为一台织布机编写指令,在提花织布机(或称甲卡提花织布机,Jacquard loom)上,运用打孔 ...

  6. 《Spring Boot极简教程》附录4 Java编程简史

    附录4 Java编程简史 从GOTO到OOP 在20世纪60年代,软件曾出现过严重危机,由软件错误而引起的信息丢失.系统报废事件屡有发生.为此,1968年,荷兰E.W.Dijkstra提出了程序设计中 ...

  7. 【抽象代数】第一章 代数系统《抽象代数极简教程》/ By 禅与计算机程序设计艺术ChatGPT

    <抽象代数极简教程> 文章目录 <抽象代数极简教程> 第一章 代数系统 1.1 集合的基本概念 1.2 二元运算 1.3 代数系统的定义 什么是代数? 抽象代数和初等代数有什么 ...

  8. 《Kotin 极简教程》第13章 使用 Kotlin 和 Anko 的Android 开发

    第13章 使用 Kotlin 和 Anko 的Android 开发 最新上架!!!< Kotlin极简教程> 陈光剑 (机械工业出版社) 可直接打开京东,淘宝,当当===> 搜索: ...

  9. 《Kotin 极简教程》第14章 使用 Kotlin DSL

    第14章 使用 Kotlin DSL 最新上架!!!< Kotlin极简教程> 陈光剑 (机械工业出版社) 可直接打开京东,淘宝,当当===> 搜索: Kotlin 极简教程 htt ...

最新文章

  1. 一个动作让全球震惊!百度Apollo就这样改写了全球智能驾驶格局
  2. 在Linux系统下配置Java开发环境
  3. 【Android 内存优化】Android 原生 API 图片压缩原理 ( 哈夫曼编码开关 | 哈夫曼编码原理 | libjpeg-turbo 函数库 )
  4. 专访腾讯徐汉彬:日请求高达3.5亿+平台的架构设计及演变
  5. python36+centos7离线安装tensorflow与talib的方法
  6. I00037 亏数(Deficient number)
  7. mysql 8.0.x版本压缩包安装方式
  8. kettle升级jetty10实验(未完成jaas认证)
  9. 学习笔记——web安全深度剖析
  10. uwsgi怎么启动停止
  11. Photoshop中预设资源安装方式-笔刷、动作、渐变、形状、样式等
  12. 华为盒子EC6108V9A-RK3128-1+4G 免拆机 卡刷固件及教程
  13. access2016访问mysql,《c#_vs2016对Access数据库的链接操作》.pdf
  14. 算法面试题-用单向链表表示十进制整数,求两个正整数之和。1234+34=1268
  15. 如何实现链表的逆序?
  16. 第一次计算机比赛心得
  17. Python办公自动化实践1:从多个excel表中提取数据并汇总到一个工作表页中,表格,抽取,sheet
  18. 使用Python实现量化交易机器人定时启动或停止小工具
  19. 不可重复读和幻读的区别
  20. 扶桑号战列舰(单调栈)

热门文章

  1. Mac Futter 开发环境配置 从0到1 流程
  2. 2018年设计师都在用的PS切图插件--摹客
  3. 日常小结:emoji表情转换utf-8编码字符串(存入数据库)/还原表情字符(页面展示)
  4. storm示例之DRPC
  5. 深入理解Kubernetes网络策略
  6. MATLAB人工蜂群算法求解超市物流配送选址问题代码实例
  7. React-Native 中如何让TextInput获得焦点?
  8. 烽火通信入选IDC2022年数字政府百强榜
  9. 9.家庭收支记账软件
  10. 腾讯公益:腾讯官方打造的公益捐助平台