8.2 Spring Boot集成Groovy、Grails开发
2017-09-13 14:29
525 查看
8.2 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 └── groovy 29 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[/code] 如果我们本地有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 = true minifyCss = 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: false use_second_level_cache: true use_query_cache: false region.factory_class: org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory dataSource: pooled: true jmxExport: true driverClassName: org.h2.Driver username: sa password: environments: development: dataSource: dbCreate: create-drop url: jdbc:h2:mem:devDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE test: dataSource: dbCreate: update url: jdbc:h2:mem:testDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE production: dataSource: dbCreate: none url: jdbc:h2:./prodDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE properties: jmxEnabled: true initialSize: 5 maxActive: 50 minIdle: 5 maxIdle: 25 maxWait: 10000 maxAge: 600000 timeBetweenEvictionRunsMillis: 5000 minEvictableIdleTimeMillis: 60000 validationQuery: SELECT 1 validationQueryTimeout: 3 validationInterval: 15000 testOnBorrow: true testWhileIdle: true testOnReturn: false jdbcInterceptors: ConnectionState defaultTransactionIsolation: 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 SUCCESSFUL
Total 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 pms class Project { static constraints = { } } package pms class Milestone { static constraints = { } }
一开始,没有什么内容。其中,static constraints变量里面主要定义对应的实体类的约束条件。
下面我们来设计领域对象的属性。
一个项目Project,我们极简化处理,取几个代表的属性,比如:名称,负责人,开始时间,结束时间,状态等。package pms class Project { static constraints = { } Integer id String name String owner Date startDate Date endDate String status }
通常,一个项目,会有多个里程碑,所以我们这里的里程碑表,多条记录对应一个Project。里程碑属性我们就取: 关联的项目id,名称,负责人,计划时间,实际时间,状态。package pms class Milestone { static constraints = { } Integer id Integer projectId String name String owner Date expectDate Date actualDate String status }使用grails脚手架自动生成Controller层,视图View层代码
grails的脚手架控制值相当简易,简单易用。我们可以使用grails create-controller $DomainName : 创建DomainName对应的空Controller grails generate-controller $DomainName :创建DomainName对应的包含CRUD的Controller grails 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 pms class 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开发
- 最好的 Grails/Groovy 的集成开发环境
- 8.1 Spring Boot集成Groovy混合Java开发
- vaadin与grails的集成应用开发
- eclipse开发Groovy代码,与java集成,maven打包编译
- 《Spring Boot极简教程》第9章 Spring Boot集成Scala混合Java开发
- 8.3 Spring Boot集成Scala混合Java开发
- grails基于groovy开发的动态框架。开发过程中常见的错。
- Grails开发随笔系列(一) 方便的BuildConfig.groovy
- 详解Spring Boot集成MyBatis的开发流程
- 《Spring Boot极简教程》第8_章: Spring Boot集成Groovy混合Java开发
- SpringSourceTool Suite 2.3.3.集成Grails开发
- 8.6 Spring Boot集成 Spring Security 《Spring Boot开发:从0到1》 工程实例: LightSword
- grails 集成ehcache terracotta-ee-3.7.2 企业版 开发配置
- MOSS 2010:Visual Studio 2010开发体验(22)——利用BCS和WCF进行应用程序集成
- Perl集成开发环境大全
- iOS开发-ios7下拉刷新,上提加载快速集成
- VS.NET 2003集成环境插件开发指南(三)----操纵VS开发环境(完结篇)
- php开发集成phpnow的安装步骤(win7下)
- 百慕大永中为何有权继续开发集成Office?