Grails4.0.11入门

article/2025/9/13 19:42:21

文章目录

  • Grails4.0.11
    • 介绍
    • 相关依赖
    • 入门
      • Java环境搭建
      • Grails环境搭建
      • 如何创建grails应用
      • 如何选择开发工具
        • IDEA如何导入Grails项目
      • 目录结构,约定优于配置
      • 如何部署Grails项目
        • 1. 使用默认内嵌的Tomcat8部署
        • 2. 使用外部Tomtcat8部署
        • 3. 使用外部Tomcat7部署
      • Grails4支持的JavaEE容器有哪些
      • 如何快速创建控制器和领域类
        • 1. 创建领域类
        • 2. 使用脚手架创建控制器和视图
        • 3. 使用脚手架生成的代码为我们实现了哪些功能
          • 3.1 - 书籍列表页面
          • 3.2 - 添加书籍页面
          • 3.3 - 书籍详情页面
          • 3.4 - 修改书籍页面
          • 3.5 - 删除书籍页面
          • 3.6 - 小结
      • 完成入门

Grails4.0.11

介绍

Grails 是一个全栈框架,它建立在已存在的 Java 技术(如 Spring 和 Hibernate)之上。
Grails核心技术及其相关插件解决了很多 Web 开发难题,降低了在 Java 平台上构建 Web 应用程序的复杂性。
Grails提供了开箱即用的功能:

  • GORM - 一个易于使用的对象映射库,支持 SQL、MongoDB、Neo4j 等。
  • 查看用于呈现 HTML 和 JSON 的技术
  • 基于 Spring Boot 构建的控制器层
  • 一个包含数百个插件的插件系统
  • 使用 AngularJS、React 等创建应用程序的灵活配置文件
  • 基于 Gradle 的交互式命令行环境和构建系统
  • 一个嵌入式 Tomcat 容器,配置为即时重新加载

相关依赖

GroovyGORMHibernateSpringFrameworkSpringBootGradleSpock
2.5.1475.45.1.202.1.185.6.41.3

入门

Java环境搭建

在安装 Grails 4.0.11 之前,至少需要安装JDK1.8版本才行,并且设置JAVA_HOME 的环境变量。

JAVA_HOME="/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home"
export PATH=".:$PATH:$JAVA_HOME/bin"
export JAVA_HOME

Grails环境搭建

  1. 下载 Grails4.0.11 的二进制发行版并将生成的 zip 文件解压缩到/opt目录。
  2. 设置GRAILS_HOME环境变量,编辑~/.bash_profile
    export GRAILS_HOME=/opt/grails-4.0.11
    
  3. 然后将 bin 目录添加到您的 PATH 变量中:
    export PATH="$PATH:$GRAILS_HOME/bin"
    
  4. 打开新的命令行窗口,输入命令 grails -version查看输出内容
    ➜ /Users/xiaosan > grails -version
    | Grails Version: 4.0.11
    | JVM Version: 1.8.0_171
    

如何创建grails应用

进入要创建项目的目录
➜ /Users/xiaosan >cd Documents/work
使用create-app命令创建应用

➜ /Users/xiaosan/Documents/work >grails create-app helloworldgrails4
| Application created at /Users/xiaosan/Documents/work/helloworldgrails4

该命令会创建一个helloworldgrails4的项目目录,进入项目目录
➜ /Users/xiaosan/Documents/work >cd helloworldgrails4
输入grails命令,启动 Grails 交互式控制台
➜ /Users/xiaosan/Documents/work >grails
等待几秒会看到如下提示:

| Resolving Dependencies. Please wait...
| Starting interactive mode...
| Enter a command name to run. Use TAB for completion:
grails>

我们想要的是一个简单的页面,它只打印消息“Hello World!”到浏览器。在 Grails 中,无论何时您想要一个新页面,您只需为它创建一个新的控制器操作。由于我们还没有控制器,让我们现在使用 create-controller 命令创建一个:

grails> create-controller hello
| Created grails-app/controllers/helloworldgrails4/HelloController.groovy
| Created src/test/groovy/helloworldgrails4/HelloControllerSpec.groovy

创建hello时没有指定包名,默认以项目名称helloworldgrails4作为包名。
在输入命令时可以使用tab键,会自动补全命令或者提示命令。
查看HelloController.groovy文件:

package helloworldgrails4class HelloController {def index() { }
}

修改这个控制器,添加一个动作来生成“Hello World”页。

package helloworldgrails4class HelloController {def index() {render "Hello World!"}
}

动作只是一种方法。在这种特殊情况下,它调用 Grails 提供的特殊方法来呈现页面。
在grails交互控制台中启动项目:

grails> run-app
| Running application...
The Class-Path manifest attribute in /Users/xiaosan/.gradle/caches/modules-2/files-2.1/org.glassfish.jaxb/jaxb-runtime/2.3.1/dd6dda9da676a54c5b36ca2806ff95ee017d8738/jaxb-runtime-2.3.1.jar referenced one or more files that do not exist: file:/Users/xiaosan/.gradle/caches/modules-2/files-2.1/org.glassfish.jaxb/jaxb-runtime/2.3.1/dd6dda9da676a54c5b36ca2806ff95ee017d8738/jaxb-api-2.3.1.jar,file:/Users/xiaosan/.gradle/caches/modules-2/files-2.1/org.glassfish.jaxb/jaxb-runtime/2.3.1/dd6dda9da676a54c5b36ca2806ff95ee017d8738/txw2-2.3.1.jar,file:/Users/xiaosan/.gradle/caches/modules-2/files-2.1/org.glassfish.jaxb/jaxb-runtime/2.3.1/dd6dda9da676a54c5b36ca2806ff95ee017d8738/istack-commons-runtime-3.0.7.jar,file:/Users/xiaosan/.gradle/caches/modules-2/files-2.1/org.glassfish.jaxb/jaxb-runtime/2.3.1/dd6dda9da676a54c5b36ca2806ff95ee017d8738/stax-ex-1.8.jar,file:/Users/xiaosan/.gradle/caches/modules-2/files-2.1/org.glassfish.jaxb/jaxb-runtime/2.3.1/dd6dda9da676a54c5b36ca2806ff95ee017d8738/FastInfoset-1.2.15.jar,file:/Users/xiaosan/.gradle/caches/modules-2/files-2.1/org.glassfish.jaxb/jaxb-runtime/2.3.1/dd6dda9da676a54c5b36ca2806ff95ee017d8738/javax.activation-api-1.2.0.jar
The Class-Path manifest attribute in /Users/xiaosan/.gradle/caches/modules-2/files-2.1/com.sun.xml.bind/jaxb-impl/2.3.1/a1a12b85ba1435b4189e065f7dafcc3fb9410d38/jaxb-impl-2.3.1.jar referenced one or more files that do not exist: file:/Users/xiaosan/.gradle/caches/modules-2/files-2.1/com.sun.xml.bind/jaxb-impl/2.3.1/a1a12b85ba1435b4189e065f7dafcc3fb9410d38/jaxb-runtime-2.3.1.jar,file:/Users/xiaosan/.gradle/caches/modules-2/files-2.1/com.sun.xml.bind/jaxb-impl/2.3.1/a1a12b85ba1435b4189e065f7dafcc3fb9410d38/txw2-2.3.1.jar,file:/Users/xiaosan/.gradle/caches/modules-2/files-2.1/com.sun.xml.bind/jaxb-impl/2.3.1/a1a12b85ba1435b4189e065f7dafcc3fb9410d38/istack-commons-runtime-3.0.7.jar,file:/Users/xiaosan/.gradle/caches/modules-2/files-2.1/com.sun.xml.bind/jaxb-impl/2.3.1/a1a12b85ba1435b4189e065f7dafcc3fb9410d38/stax-ex-1.8.jar,file:/Users/xiaosan/.gradle/caches/modules-2/files-2.1/com.sun.xml.bind/jaxb-impl/2.3.1/a1a12b85ba1435b4189e065f7dafcc3fb9410d38/FastInfoset-1.2.15.jar,file:/Users/xiaosan/.gradle/caches/modules-2/files-2.1/com.sun.xml.bind/jaxb-impl/2.3.1/a1a12b85ba1435b4189e065f7dafcc3fb9410d38/javax.activation-api-1.2.0.jar
Grails application running at http://localhost:8080 in environment: development
<=======<==========---> 83% EXECUTING [2m 11s]
> :bootR> :bootRun
grails>

默认使用内嵌Tomcat8服务器,默认端口8080,在浏览器中输入http://localhost:8080/ 访问grails启动界面
这是Grails提供的介绍页面,页面位置在 grails-app/view/index.gsp。显示有多少个控制器,并提供指向它们的链接。
单击“HelloController”链接就可以看到包含文本“Hello World!”的自定义页面。
在这里插入图片描述

现在第一个 Grails 应用程序已经成功运行起来了。

为应用程序设置上下文路径,在 grails-app/conf/application.yml 中添加一个配置属性:

server:servlet:context-path: /helloworld

添加后的代码:

---
server:servlet:context-path: /helloworld
grails:profile: webcodegen:后面代码省略···
···    

保存之后,系统会自动将项目运行在helloworld上,不需要重启

Grails application running at http://localhost:8080/helloworld in environment: development
<==========---> 83% EXECUTING [28m 43s]
> :bootRun

这个时候就不能使用http://localhost:8080访问项目了
要使用http://localhost:8080/helloworld访问项目
在这里插入图片描述
访问"HelloWorld"页面
在这里插入图片描述
注意:

  • 控制器(Controller)可以包含许多动作(Action),每个动作对应一个不同的页面(此时忽略 AJAX)。
  • 每个页面都可以通过一个唯一的 URL 访问。
  • 该 URL 由控制器名称和操作名称组成:
    /上下文路径/控制器/动作
    /appname/controller/action
    这意味着您可以通过 /helloworld/hello/index 访问 Hello World 页面,其中
    hello 是控制器名称(从类名中删除 ‘Controller’ 后缀和小写首字母),
    index 是控制器动作名称。
    因为“index”是默认操作,可以省略。例如
    http://localhost:8080/helloworld/hello/index
    http://localhost:8080/helloworld/hello
    都可以访问到HelloWorld页面
    在这里插入图片描述

可以在启动项目时,通过命令参数指定上下文路径,这样就不用在applicatioin.yml中配置了

grails> run-app -Dgrails.server.servlet.context-path=/helloworld
| Running application...
...
...
Grails application running at http://localhost:8080/helloworld in environment: development
<=======<==========---> 83% EXECUTING [24s]
> :bootR> :bootRun
grails>

也可以在命令行中指定端口号,端口号范围在1024 ~ 49151 。

grails> run-app -Dgrails.server.servlet.context-path=/helloworld -port=9999
| Running application...
...
...
Grails application running at http://localhost:9999/helloworld in environment: development
<=======<==========---> 83% EXECUTING [43s]
> :bootR> :bootRun
grails>

在这里插入图片描述
可以在grails交互模式中,输入quit命令来退出交互模式并停止项目

也可以不在grails交互模式中启动项目,直接在外面使用命令启动:
➜ /Users/xiaosan/Documents/work/helloworldgrails4 >grails run-app -Dgrails.server.servlet.context-path=/helloworld -port=9999

官方说最好以交互模式启动应用程序,因为容器重启要快得多,但实际使用时没感觉快多少。

如何选择开发工具

推荐使用IDEA
其他可选开发工具:

  • 可以通过 Sublime 文本编辑器的 Sublime Package Control 安装插件sublimetext-grails
  • 将 VIM 设置为 Grails 编辑器,参考using-vim-as-your-grails-ide-part-1-navigating-your-project
  • Atom 编辑器安装插件atom-grails
  • Visual Studio Code安装扩展code-groovy。

IDEA如何导入Grails项目

选择 File / Open,
IDEA选择文件
选择grails项目的 build.gradle文件。
选择build.gradle文件述
以项目形式打开。
以项目形式打开
信任项目
选择信任项目
等待导入配置
等待导入配置
导入完成
导入完成

目录结构,约定优于配置

Grails 使用“约定优于配置”来配置自身。意味着Grails的文件名称和位置不需要显示的去配置,按照Grails的默认约定就可以了,因此需要熟悉 Grails 提供的目录结构。

  • grails-app:Groovy 源码的顶级目录
    • conf:存放配置文件的目录,可以配置日志,数据源,Spring bean配置等。
    • controllers:Web控制器,MVC中的C。负责处理请求并创建或准备响应,控制器可以直接生成响应或委托给视图。
    • doamin:领域类,MVC中的M。定义业务流程的状态和行为,配置属性约束,配置一对一、一对多或多对多关系。
    • i18n:支持开箱即用的国际化(i18n)。Grails底层利用SpringMVC的国际化支持。
    • services:业务层。用于存放应用程序逻辑,让控制器负责处理带有重定向等的请求。
    • taglib:标签库。
    • utils:Grails 特定的实用程序。
    • views:Groovy 服务器页面或 JSON 视图 - MVC 中的 V。
      - src/main/scripts:代码生成脚本,用于创建脚本,类似月create-app、run-app脚本。 grails4.0.11中没有该目录
  • src/main/groovy:存放groovy代码
  • src/test/groovy:存放单元测试

如何部署Grails项目

1. 使用默认内嵌的Tomcat8部署

  • 先打包成war文件
    ➜ /Users/xiaosan/Documents/work/helloworldgrails4 >grails war
    Processing File 2 of 43 - bootstrap.bundle.js
    ...
    Processing File 43 of 43 - bootstrap.min.css.map
    > Task :assetCompile
    Finished Precompiling AssetsBUILD SUCCESSFUL in 15s
    7 actionable tasks: 5 executed, 2 up-to-date
    | Built application to build/libs using environment: production
    
    默认打包环境为生产环境。
    打包文件默认生成在build/libs/helloworldgrails4-0.1.war 。
    默认情况下,Grails 将在 WAR 文件中包含可嵌入版本的 Tomcat
  • 使用java命令启动内嵌Tomcat的war文件
    /Users/xiaosan/Documents/work/helloworldgrails4 >java -Dgrails.env=prod -jar build/libs/helloworldgrails4-0.1.war
    Grails application running at http://localhost:8080 in environment: production
    启动可以添加一些参数:
    -Dgrails.env参数可以指定启动的环境,不加这个参数默认是生产环境。
    例如:
    -Dgrails.env=prod:生产环境
    -Dgrails.env=dev:开发环境
    -Dgrails.env=test:测试环境
    使用 -server 参数给JVM容器分配内存。
    例如:
    -server -Xmx768M -XX:MaxPermSize=256m
    参数一起使用:
    ➜ /Users/xiaosan/Documents/work/helloworldgrails4 >java -Dgrails.env=prod -server -Xmx768M -XX:MaxPermSize=256m -jar build/libs/helloworldgrails4-0.1.war
    Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=256m; support was removed in 8.0
    Grails application running at http://localhost:8080 in environment: production
    

启动成功访问http://localhost:8080,注意这里我将application.yml中的上下文路径给删除了,没有配置上下文路径。 在这里插入图片描述
由于MaxPermSize参数在JDK8中已经删除了,所以这里可以去掉这个参数。

jvm参数含义
-vmargs -Xms128M -Xmx512M -XX:PermSize=64M -XX:MaxPermSize=128M
-vmargs 说明后面是VM的参数,所以后面的其实都是JVM的参数了
-Xms128m JVM初始分配的堆内存
-Xmx512m JVM最大允许分配的堆内存,按需分配
-XX:PermSize=64M JVM初始分配的非堆内存
-XX:MaxPermSize=128M JVM最大允许分配的非堆内存,按需分配

2. 使用外部Tomtcat8部署

  • 打包前修改build.gradle 文件,在依赖项中将 Tomcat 依赖的范围更改为provided
    compile "org.springframework.boot:spring-boot-starter-tomcat"
    改为
    provided "org.springframework.boot:spring-boot-starter-tomcat"
    如果使用IDEA工具修改,改后后记得gradle图标,重新加载依赖
    重新加载gradle
  • 开始打包,进入项目所在目录,执行grails war命令。
    | Resolving Dependencies. Please wait...
    CONFIGURE SUCCESSFUL in 1sBUILD SUCCESSFUL in 6s
    7 actionable tasks: 6 executed, 1 up-to-date
    | Built application to build/libs using environment: production
    
  • 下载Tomcat8,解压到目录/opt/apache-tomcat-8.5-grails4
  • 删除/opt/apache-tomcat-8.5-grails4/webapps/ROOT目录
  • 将war包复制到/opt/apache-tomcat-8.5-grails4/webapps/,并改名为ROOT.war
    cp build/libs/helloworldgrails4-0.1.war /opt/apache-tomcat-8.5-grails4/webapps/ROOT.war
    Tomcat8项目目录
  • 修改Tomcat的端口号。修改/opt/apache-tomcat-8.5-grails4/conf/server.xml,将8080端口改为8880
  • 启动Tomcat。进入/opt/apache-tomcat-8.5-grails4/bin目录,执行
    /opt/apache-tomcat-8.5-grails4/bin >./startup.sh
    Using CATALINA_BASE:   /opt/apache-tomcat-8.5-grails4
    Using CATALINA_HOME:   /opt/apache-tomcat-8.5-grails4
    Using CATALINA_TMPDIR: /opt/apache-tomcat-8.5-grails4/temp
    Using JRE_HOME:        /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home
    Using CLASSPATH:       /opt/apache-tomcat-8.5-grails4/bin/bootstrap.jar:/opt/apache-tomcat-8.5-grails4/bin/tomcat-juli.jar
    Tomcat started.
    
  • 查看tomcat日志输出
    ➜ /opt/apache-tomcat-8.5-grails4/bin >tail -f ../logs/catalina.out
    ...
    ...
    04-Aug-2021 16:02:03.911 信息 [main] org.apache.coyote.AbstractProtocol.start 开始协议处理句柄["http-nio-8880"]
    04-Aug-2021 16:02:03.918 信息 [main] org.apache.coyote.AbstractProtocol.start 开始协议处理句柄["ajp-nio-8809"]
    04-Aug-2021 16:02:03.920 信息 [main] org.apache.catalina.startup.Catalina.start Server startup in 13480 ms
    
  • 访问项目http://localhost:8080
    在这里插入图片描述
    注意编译的时候依赖的是Tomcat8,打包后的war只能在Tomcat8中运行,不能放在Tomcat7中运行。

3. 使用外部Tomcat7部署

  • 修改build.gradle文件,修改Tomcat的依赖版本,例如改为7.0.88,在dependencies{}上面添加ext['tomcat.version'] = '7.0.88'

  • 打包前修改build.gradle 文件,在依赖项中将 Tomcat 依赖的范围更改为provided
    compile "org.springframework.boot:spring-boot-starter-tomcat"
    改为
    provided "org.springframework.boot:spring-boot-starter-tomcat"

  • 如果IDEA修改的build.gradle,修改后注意点击gradle图标重新加载依赖重新加载gradle

  • 开始打包,进入项目所在目录,执行grails war命令。

    | Resolving Dependencies. Please wait...
    CONFIGURE SUCCESSFUL in 1s
    Processing File 7 of 43 - application.js
    ...
    ...
    > Task :assetCompile
    Finished Precompiling AssetsBUILD SUCCESSFUL in 9s
    7 actionable tasks: 5 executed, 2 up-to-date
    | Built application to build/libs using environment: production
    
  • 下载Tomcat7.0.88,解压到目录/opt/apache-tomcat-7.0.88

  • 删除/opt/apache-tomcat-7.0.88/webapps/ROOT目录

  • 将war包复制到/opt/apache-tomcat-7.0.88/webapps/,并改名为ROOT.war
    cp build/libs/helloworldgrails4-0.1.war /opt/apache-tomcat-7.0.88/webapps/ROOT.warTomcat7项目目录

  • 修改Tomcat7的端口号。修改/opt/apache-tomcat-7.0.88/conf/server.xml,将8005端口改为8705,将8080改为8780,将8009改为8709。该端口主要是为了避免多个Tomcat端口冲突,如果只有一个Tomcat就可以不用修改

  • 启动Tomcat。进入/opt/apache-tomcat-7.0.88/bin目录,执行./startup.sh

    /opt/apache-tomcat-7.0.88/bin >./startup.sh
    Using CATALINA_BASE:   /opt/apache-tomcat-7.0.88
    Using CATALINA_HOME:   /opt/apache-tomcat-7.0.88
    Using CATALINA_TMPDIR: /opt/apache-tomcat-7.0.88/temp
    Using JRE_HOME:        /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home
    Using CLASSPATH:       /opt/apache-tomcat-7.0.88/bin/bootstrap.jar:/opt/apache-tomcat-7.0.88/bin/tomcat-juli.jar
    Tomcat started.
    
  • 查看tomcat日志输出

      八月 04, 2021 7:59:54 下午 org.apache.catalina.loader.WebappClassLoaderBase validateJarFile信息: validateJarFile(/opt/apache-tomcat-7.0.88/webapps/ROOT/WEB-INF/lib/el-api-2.1.2-b03.jar) - jar not loaded. See Servlet Spec 3.0, section 10.7.2. Offending class: javax/el/Expression.class八月 04, 2021 7:59:54 下午 org.apache.catalina.loader.WebappClassLoaderBase validateJarFile信息: validateJarFile(/opt/apache-tomcat-7.0.88/webapps/ROOT/WEB-INF/lib/javax.el-3.0.0.jar) - jar not loaded. See Servlet Spec 3.0, section 10.7.2. Offending class: javax/el/Expression.class八月 04, 2021 7:59:54 下午 org.apache.catalina.loader.WebappClassLoaderBase validateJarFile信息: validateJarFile(/opt/apache-tomcat-7.0.88/webapps/ROOT/WEB-INF/lib/javax.el-api-3.0.1-b06.jar) - jar not loaded. See Servlet Spec 3.0, section 10.7.2. Offending class: javax/el/Expression.class八月 04, 2021 7:59:54 下午 org.apache.catalina.loader.WebappClassLoaderBase validateJarFile信息: validateJarFile(/opt/apache-tomcat-7.0.88/webapps/ROOT/WEB-INF/lib/javax.servlet-api-4.0.1.jar) - jar not loaded. See Servlet Spec 3.0, section 10.7.2. Offending class: javax/servlet/Servlet.class八月 04, 2021 7:59:56 下午 org.apache.catalina.startup.TldConfig execute信息: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.2021-08-04 20:00:02.899 ERROR --- [ost-startStop-1] o.s.boot.SpringApplication               : Application run failedorg.springframework.beans.factory.BeanCreationException: Error creating bean with name 'requestMappingHandlerAdapter' defined in org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter]: Factory method 'requestMappingHandlerAdapter' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mvcValidator' defined in org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration: Invocation of init method failed; nested exception is java.lang.NoClassDefFoundError: javax/el/ELManager
    

    报错nested exception is java.lang.NoClassDefFoundError: javax/el/ELManager
    因为Grails4.0.11对el-api的版本要求比较高,Tomcat7.0.88/lib/目录里自带的el-api.jar是2.1版本的,版本比较底,启动tomcat时先加载自己的底el-api.jar,后面就不会再去加载ROOT/WEB-INF/lib/目录下的高版本el-api.jar了。
    执行➜ /opt/apache-tomcat-7.0.88/bin >./shutdown.sh停止tomcat
    复制/opt/apache-tomcat-7.0.88/webapps/ROOT/WEB-INF/lib/javax.el-api-3.0.1-b06.jar
    /opt/apache-tomcat-7.0.88/lib
    重新启动Tomcat7

    八月 04, 2021 8:08:40 下午 org.apache.catalina.startup.HostConfig deployDirectory
    信息: Deployment of web application directory /opt/apache-tomcat-7.0.88/webapps/examples has finished in 149 ms
    八月 04, 2021 8:08:40 下午 org.apache.catalina.startup.HostConfig deployDirectory
    信息: Deploying web application directory /opt/apache-tomcat-7.0.88/webapps/host-manager
    八月 04, 2021 8:08:40 下午 org.apache.catalina.startup.TldConfig execute
    信息: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
    八月 04, 2021 8:08:40 下午 org.apache.catalina.startup.HostConfig deployDirectory
    信息: Deployment of web application directory /opt/apache-tomcat-7.0.88/webapps/host-manager has finished in 35 ms
    八月 04, 2021 8:08:40 下午 org.apache.coyote.AbstractProtocol start
    信息: Starting ProtocolHandler ["http-bio-8780"]
    八月 04, 2021 8:08:40 下午 org.apache.coyote.AbstractProtocol start
    信息: Starting ProtocolHandler ["ajp-bio-8709"]
    八月 04, 2021 8:08:40 下午 org.apache.catalina.startup.Catalina start
    信息: Server startup in 12017 ms
    

    启动成功,访问http://localhost:8780
    grails4-tomcat7启动成功
    注意高版本编译的代码,不要使用底版本的servlet容器运行,很容易出现由于jar包不兼容导致冲突,最好使用默认的servlet容器来运行。

Grails4支持的JavaEE容器有哪些

Grails 可以在任何支持 Servlet 3.0 及更高版本的容器上运行,并且可以在以下特定的容器产品上运行:

  • Tomcat 7
  • GlassFish 3 或以上版本
  • Resin 4 或以上版本
  • JBoss 6 或以上版本
  • Jetty 8 或以上版本
  • Oracle Weblogic 12c 或以上版本
  • IBM WebSphere 8.0 或以上版本

Grails4.0.11默认是使用Tomcat8作为内嵌servlet容器,部署时也最好使用Tomcat8。

如何快速创建控制器和领域类

1. 创建领域类

Grails提供了一些create-*命令,用于快速创建应用。当然使用IDEA创建更快。
例如:创建领域类命令create-domain-class
grails create-domain-class book
这将会创建一个helloworldgrails4.Book.groovy领域类
和对应的测试类helloworldgrails4.BookSpec.groovy
创建时没有给book指定包名,默认使用项目名作为包名。

 ➜ /Users/xiaosan/Documents/work/helloworldgrails4 >grails create-domain-class book
| Created grails-app/domain/helloworldgrails4/Book.groovy
| Created src/test/groovy/helloworldgrails4/BookSpec.groovy

查看创建的领域类

package helloworldgrails4
class Book {static constraints = {}
}

修改领域类,添加两个字段

package helloworldgrails4class Book {String title    //书名Date releaseDate = new Date()   //发布日期static constraints = {}
}

2. 使用脚手架创建控制器和视图

Grails提供了一些脚手架功能用来快速生成应用程序的骨架。
命令以 generate-* 开头,例如 generate-all,它将生成控制器(及其单元测试)和相关视图:

grails generate-all Book -force
-force 参数强制生成覆盖已有文件。

➜ /Users/xiaosan/Documents/work/helloworldgrails4 >grails generate-all Book -force
| Rendered template Controller.groovy to destination grails-app/controllers/helloworldgrails4/BookController.groovy
| Rendered template Service.groovy to destination grails-app/services/helloworldgrails4/BookService.groovy
| Rendered template Spec.groovy to destination src/test/groovy/helloworldgrails4/BookControllerSpec.groovy
| Rendered template ServiceSpec.groovy to destination src/integration-test/groovy/helloworldgrails4/BookServiceSpec.groovy
| Scaffolding completed for grails-app/domain/helloworldgrails4/Book.groovy
| Rendered template show.gsp to destination grails-app/views/book/show.gsp
| Rendered template index.gsp to destination grails-app/views/book/index.gsp
| Rendered template create.gsp to destination grails-app/views/book/create.gsp
| Rendered template edit.gsp to destination grails-app/views/book/edit.gsp
| Views generated for grails-app/domain/helloworldgrails4/Book.groovy

自动生成的有:

  • 控制器:grails-app/controllers/helloworldgrails4/BookController.groovy
  • 业务层:grails-app/services/helloworldgrails4/BookService.groovy
  • 控制器测试:src/test/groovy/helloworldgrails4/BookControllerSpec.groovy
  • 业务层测试:src/integration-test/groovy/helloworldgrails4/BookServiceSpec.groovy
  • 详情页面:grails-app/views/book/show.gsp
  • 列表页面:grails-app/views/book/index.gsp
  • 添加页面:grails-app/views/book/create.gsp
  • 修改页面:grails-app/views/book/edit.gsp

grails-app/controllers/helloworldgrails4/BookController.groovy代码

package helloworldgrails4import grails.validation.ValidationException
import static org.springframework.http.HttpStatus.*class BookController {BookService bookServicestatic allowedMethods = [save: "POST", update: "PUT", delete: "DELETE"]def index(Integer max) {params.max = Math.min(max ?: 10, 100)respond bookService.list(params), model:[bookCount: bookService.count()]}def show(Long id) {respond bookService.get(id)}def create() {respond new Book(params)}def save(Book book) {if (book == null) {notFound()return}try {bookService.save(book)} catch (ValidationException e) {respond book.errors, view:'create'return}request.withFormat {form multipartForm {flash.message = message(code: 'default.created.message', args: [message(code: 'book.label', default: 'Book'), book.id])redirect book}'*' { respond book, [status: CREATED] }}}def edit(Long id) {respond bookService.get(id)}def update(Book book) {if (book == null) {notFound()return}try {bookService.save(book)} catch (ValidationException e) {respond book.errors, view:'edit'return}request.withFormat {form multipartForm {flash.message = message(code: 'default.updated.message', args: [message(code: 'book.label', default: 'Book'), book.id])redirect book}'*'{ respond book, [status: OK] }}}def delete(Long id) {if (id == null) {notFound()return}bookService.delete(id)request.withFormat {form multipartForm {flash.message = message(code: 'default.deleted.message', args: [message(code: 'book.label', default: 'Book'), id])redirect action:"index", method:"GET"}'*'{ render status: NO_CONTENT }}}protected void notFound() {request.withFormat {form multipartForm {flash.message = message(code: 'default.not.found.message', args: [message(code: 'book.label', default: 'Book'), params.id])redirect action: "index", method: "GET"}'*'{ render status: NOT_FOUND }}}
}

grails-app/services/helloworldgrails4/BookService.groovy代码:

package helloworldgrails4import grails.gorm.services.Service@Service(Book)
interface BookService {Book get(Serializable id)List<Book> list(Map args)Long count()void delete(Serializable id)Book save(Book book)}

grails-app/views/book/show.gsp代码:

<!DOCTYPE html>
<html><head><meta name="layout" content="main" /><g:set var="entityName" value="${message(code: 'book.label', default: 'Book')}" /><title><g:message code="default.show.label" args="[entityName]" /></title></head><body><a href="#show-book" class="skip" tabindex="-1"><g:message code="default.link.skip.label" default="Skip to content&hellip;"/></a><div class="nav" role="navigation"><ul><li><a class="home" href="${createLink(uri: '/')}"><g:message code="default.home.label"/></a></li><li><g:link class="list" action="index"><g:message code="default.list.label" args="[entityName]" /></g:link></li><li><g:link class="create" action="create"><g:message code="default.new.label" args="[entityName]" /></g:link></li></ul></div><div id="show-book" class="content scaffold-show" role="main"><h1><g:message code="default.show.label" args="[entityName]" /></h1><g:if test="${flash.message}"><div class="message" role="status">${flash.message}</div></g:if><f:display bean="book" /><g:form resource="${this.book}" method="DELETE"><fieldset class="buttons"><g:link class="edit" action="edit" resource="${this.book}"><g:message code="default.button.edit.label" default="Edit" /></g:link><input class="delete" type="submit" value="${message(code: 'default.button.delete.label', default: 'Delete')}" onclick="return confirm('${message(code: 'default.button.delete.confirm.message', default: 'Are you sure?')}');" /></fieldset></g:form></div></body>
</html>

grails-app/views/book/index.gsp代码

<!DOCTYPE html>
<html><head><meta name="layout" content="main" /><g:set var="entityName" value="${message(code: 'book.label', default: 'Book')}" /><title><g:message code="default.list.label" args="[entityName]" /></title></head><body><a href="#list-book" class="skip" tabindex="-1"><g:message code="default.link.skip.label" default="Skip to content&hellip;"/></a><div class="nav" role="navigation"><ul><li><a class="home" href="${createLink(uri: '/')}"><g:message code="default.home.label"/></a></li><li><g:link class="create" action="create"><g:message code="default.new.label" args="[entityName]" /></g:link></li></ul></div><div id="list-book" class="content scaffold-list" role="main"><h1><g:message code="default.list.label" args="[entityName]" /></h1><g:if test="${flash.message}"><div class="message" role="status">${flash.message}</div></g:if><f:table collection="${bookList}" /><div class="pagination"><g:paginate total="${bookCount ?: 0}" /></div></div></body>
</html>

grails-app/views/book/create.gsp代码:

<!DOCTYPE html>
<html><head><meta name="layout" content="main" /><g:set var="entityName" value="${message(code: 'book.label', default: 'Book')}" /><title><g:message code="default.create.label" args="[entityName]" /></title></head><body><a href="#create-book" class="skip" tabindex="-1"><g:message code="default.link.skip.label" default="Skip to content&hellip;"/></a><div class="nav" role="navigation"><ul><li><a class="home" href="${createLink(uri: '/')}"><g:message code="default.home.label"/></a></li><li><g:link class="list" action="index"><g:message code="default.list.label" args="[entityName]" /></g:link></li></ul></div><div id="create-book" class="content scaffold-create" role="main"><h1><g:message code="default.create.label" args="[entityName]" /></h1><g:if test="${flash.message}"><div class="message" role="status">${flash.message}</div></g:if><g:hasErrors bean="${this.book}"><ul class="errors" role="alert"><g:eachError bean="${this.book}" var="error"><li <g:if test="${error in org.springframework.validation.FieldError}">data-field-id="${error.field}"</g:if>><g:message error="${error}"/></li></g:eachError></ul></g:hasErrors><g:form resource="${this.book}" method="POST"><fieldset class="form"><f:all bean="book"/></fieldset><fieldset class="buttons"><g:submitButton name="create" class="save" value="${message(code: 'default.button.create.label', default: 'Create')}" /></fieldset></g:form></div></body>
</html>

grails-app/views/book/edit.gsp代码:

<!DOCTYPE html>
<html><head><meta name="layout" content="main" /><g:set var="entityName" value="${message(code: 'book.label', default: 'Book')}" /><title><g:message code="default.edit.label" args="[entityName]" /></title></head><body><a href="#edit-book" class="skip" tabindex="-1"><g:message code="default.link.skip.label" default="Skip to content&hellip;"/></a><div class="nav" role="navigation"><ul><li><a class="home" href="${createLink(uri: '/')}"><g:message code="default.home.label"/></a></li><li><g:link class="list" action="index"><g:message code="default.list.label" args="[entityName]" /></g:link></li><li><g:link class="create" action="create"><g:message code="default.new.label" args="[entityName]" /></g:link></li></ul></div><div id="edit-book" class="content scaffold-edit" role="main"><h1><g:message code="default.edit.label" args="[entityName]" /></h1><g:if test="${flash.message}"><div class="message" role="status">${flash.message}</div></g:if><g:hasErrors bean="${this.book}"><ul class="errors" role="alert"><g:eachError bean="${this.book}" var="error"><li <g:if test="${error in org.springframework.validation.FieldError}">data-field-id="${error.field}"</g:if>><g:message error="${error}"/></li></g:eachError></ul></g:hasErrors><g:form resource="${this.book}" method="PUT"><g:hiddenField name="version" value="${this.book?.version}" /><fieldset class="form"><f:all bean="book"/></fieldset><fieldset class="buttons"><input class="save" type="submit" value="${message(code: 'default.button.update.label', default: 'Update')}" /></fieldset></g:form></div></body>
</html>

3. 使用脚手架生成的代码为我们实现了哪些功能

启动项目,看看Grails脚手架生成的代码为我们提供了哪些功能。
在这之前,将grails项目改为使用内嵌Tomcat方式启动,修改build.gradle
注释掉ext['tomcat.version'] = '7.0.88'
provided "org.springframework.boot:spring-boot-starter-tomcat"
改为compile "org.springframework.boot:spring-boot-starter-tomcat"
使用grails run-app命令启动项目
默认使用8080端口,没有配置上下文路径,打来浏览器访问http://localhost:8080
grails脚手架提供的功能

3.1 - 书籍列表页面

点击helloworldgrails4.BookConroller跳转到书籍列表页面http://localhost:8080/book/index
Book列表页面

3.2 - 添加书籍页面

点击NewBook跳转到添加书籍页面http://localhost:8080/book/create添加书籍页面
默认提供了领域类的属性表单,并且提供了非空验证。
添加一个书籍
添加一个书籍
点击Create保存跳转到书籍详情页面

3.3 - 书籍详情页面

点击Create后跳转到书籍详情页面http://localhost:8080/book/show/1
/book/show/1
/控制器/动作/ID
书籍详情页面
默认展示刚刚添加的属性信息,有添加成功提示信息
提供导航到书籍页面,添加书籍,修改书籍,删除书籍的链接。

3.4 - 修改书籍页面

点击Edit跳转到修改书籍页面http://localhost:8080/book/edit/1
修改书籍页面
默认提供了领域类属性表单,同样有领域类属性校验。
提供导航到书籍页面,添加书籍的链接。
点击Update跳转到书籍详情页面。
修改成功跳转到详情页面
点击Create Book再次添加一本书籍
添加第二本书籍
点击Book List跳转到书籍列表页面
书籍列表页面2

3.5 - 删除书籍页面

先在列表页面点击要删除的书籍,进入书籍详情页面
要删除的书籍详情页面
确认是否删除
点击“好” ,确认删除,删除成功后跳转到书籍列表页面
删除成功跳转到列表页面
在列表页面显示删除成功的提示信息。

3.6 - 小结

脚手架为我们提供的功能有:

  • 包含全部属性表单的添加页面
  • 包含属性的列表页面,点击属性标题可以排序
  • 包含全部属性表单的修改页面
  • 包含全部属性表单的详情页面
  • 包含删除数据功能
  • 列表页面-详情页面-添加页面-修改页面的导航链接
  • 操作数据后的提示信息
  • 属性表单的校验(非空等)

基本上为我们提供了所有的数据操作功能,我们只需要修改默认的css样式就可以了。如果不喜欢默认的排版,就需要自己写页面布局,将属性一个一个的写出来了。

完成入门

到目前为止,我们完成了从创建grails项目,到创建领域类,创建控制器,创建视图,完成增删改查,最后发布的所有流程。这只是一个简单的入门教程,主要先了解grails项目开发的几个大概步骤。Grails默认使用的H2内存数据库,数据库配置每次重启项目都会清空数据。后面会讲连接其他数据库,并持久化数据。


http://chatgpt.dhexx.cn/article/BgEK8py0.shtml

相关文章

grails java_Grails

十二、Grails 如今的Java Web开发对于需求来说已经变得过于复杂。当今众多Java领域的Web开发框架不仅使用复杂&#xff0c;而且并没有很好的遵循Don’t Repeat Yourself(DRY)原则。像Rails&#xff0c;Django和TurboGears这样的动态框架在Web开发领域开辟了一条新的道路&#x…

JAVA AIO编程

Asynchronous IO&#xff1a; 异步非阻塞的编程方式 与NIO不同&#xff0c;当进行读写操作时&#xff0c;只须直接调用API的read或write方法即可。这两种方法均为异步的&#xff0c;对于读操作而言&#xff0c;当有流可读取时&#xff0c;操作系统会将可读的流传入read方法的缓…

java aio_Java AIO详解

JDK1.7升级了NIO类库&#xff0c;升级后的NIO类库被称为NIO 2.0。 Java正式提供了异步文件I/O操作&#xff0c;同时提供了与UNIX网络编程事件驱动I/O对应的AIO。 AIO编程 NIO 2.0引入了新的异步通道的概念&#xff0c;并提供了异步文件通道和异步套接字通道的实现。 异步通道获…

Java I/O 模型之 AIO

❤️ 个人主页&#xff1a;水滴技术 &#x1f680; 支持水滴&#xff1a;点赞&#x1f44d; 收藏⭐ 留言&#x1f4ac; &#x1f338; 订阅专栏&#xff1a;Java 教程&#xff1a;从入门到精通 大家好&#xff0c;我是水滴~~ 文章目录 服务端API创建服务端套接字的异步通道接…

如何用AIO技术提高程序性能

写在前面 这是一篇关于 AIO 的文章。本篇文章详细对比了几个常见的I/O模型&#xff0c;并且介绍了AIO相关的一些API。 我把英文原文翻译过来整理成这篇文章。目的一个是自己学习&#xff0c;一个是方便不习惯看英文资料的同学进行学习。 英文原文地址&#xff1a; https://…

08【AIO编程】

八、AIO编程 8.1 AIO编程简介 8.1.1 AIO编程概述 AIO也叫异步非阻塞&#xff0c;JDK1.7之后的新特性&#xff0c;AIO引入异步通道的概念&#xff0c;采用了 Proactor 模式&#xff0c;简化了程序编写&#xff0c;有效的请求才启动线程&#xff0c;它的特点是先由操作系统完成…

linux aio进程简介,Linux AIO机制

Linux的I/O机制经历了一下几个阶段的演进&#xff1a; 1. 同步阻塞I/O: 用户进程进行I/O操作&#xff0c;一直阻塞到I/O操作完成为止。 2. 同步非阻塞I/O: 用户程序可以通过设置文件描述符的属性O_NONBLOCK&#xff0c;I/O操作可以立即返回&#xff0c;但是并不保证I/O操作成功…

关于aio

AIO 是异步非阻塞 同步意味着&#xff0c;在进行读写操作时&#xff0c;线程需要等待结果&#xff0c;还是相当于闲置 异步意味着&#xff0c;在进行读写操作时&#xff0c;线程不必等待结果&#xff0c;而是将来由操作系统来通过回调方式由另外的线程来获得结果 AIO的read方…

linux AIO

AIO 是 Linux 下的异步读写模型&#xff0c;它是 2.6 内核提供的一个标准增强特性。对于文件的读写&#xff0c;即使以 O_NONBLOCK 方式来打开一个文件&#xff0c;也会处于 “阻塞” 状态&#xff0c;因为文件时时刻刻处于可读状态&#xff0c;而从磁盘到内存所等待的时间是惊…

AIO模型

目录 AIO模型介绍 AsynchronousServerSocketChannel&#xff1a;AIO中网络通信服务端的socket 1、future方法 2、callback回调方式 AIO 的回调方式编程 BIO、NIO、AIO的比较 1、释义 BIO&#xff1a;同步阻塞IO模型 NIO&#xff1a;同步非阻塞IO模型 AIO&#xff1a;…

java中IO模型-AIO模型

AIO模型介绍 AIO&#xff08;Asynchronous I/O&#xff09; 异步非阻塞模型&#xff0c; 在javajdk.17版本开始支持AIO&#xff0c;AIO模型需要操作系统的支持。 AIO最大的特性是异步能力&#xff0c;对socket和I/O起作用。 异步IO模型类似的 与NIO模型不同&#xff0c;读写操…

架构解密从分布式到微服务:深入理解网络,AIO

AIO AIO是I/O模型里一个很高的层次&#xff0c;体现了大道至简的软件美学理念。与NIO相比&#xff0c;AIO的框架和使用方法相对简单很多。 AIO包括两大部分:AIO Files解决了文件的异步处理问题&#xff0c;AIO Sockets解决了Socket的异步处理问题。AIO的核心概念为应用发起非…

BIO,NIO,AIO区别

BIO,NIO,AIO 总结 Java 中的 BIO、NIO和 AIO 理解为是 Java 语言对操作系统的各种 IO 模型的封装。程序员在使用这些 API 的时候&#xff0c;不需要关心操作系统层面的知识&#xff0c;也不需要根据不同操作系统编写不同的代码。只需要使用Java的API就可以了。 在讲 BIO,NIO,…

Apache CXF - 快速指南

Apache CXF - 简介 在当今的环境中&#xff0c;您可以使用多个选项来创建 Web 服务应用程序。您可以使用多种标准和广泛接受的协议中的一种或多种进行通信。例如SOAP、XML/HTTP、RESTful HTTP和CORBA&#xff08;通用对象请求代理架构&#xff0c;在过去非常流行&#xff0c;但…

java cxf 安全_CXF client在并发下的线程安全问题

这个是发生在上周周末的真实案例&#xff0c;因为cxf client 端线程安全导致的错误&#xff0c;总结出来希望其他使用cxf的兄弟注意。 首先描述一下背景&#xff0c;简单的说就是使用cxf作为web service的客户端&#xff0c;运行在weblogic上&#xff0c;连接外部的服务器。为了…

linux cxf服务端,Apache CXF 框架应用实战

一、概述 Apache CXF提供了用于方便地构建和开发WebService的可靠基础架构。它允许创建高性能和可扩展的服务&#xff0c;可以部署在Tomcat和基于Spring的轻量级容器中&#xff0c;也可以部署在更高级的服务器上&#xff0c;例如Jboss、WebSphere或WebLogic。 CXF提供了以下功能…

使用CXF调用WSDL

简介 时隔多年&#xff0c;再次遇到需要调用WebService的业务&#xff0c;对方给予的wsdl说明文档还是内网的链接&#xff0c;并且设有基础访问权限&#xff0c;即在浏览器打开wsdl链接时需要输入【用户名密码】登录后方可查看wsdl文档&#xff0c;这需要设置代理&#xff08;我…

spring5.x cxf3.4.x 服务端和客户端 非maven版本

文章目录 一、资料准备1. 官网链接2. 解压3. 依赖梳理 二、spring集成cxf2.1.创建spring项目2.2. 创建接口2.3. impl2.4. spring-cxf.xml2.5. 客户端2.6. 开源项目 一、资料准备 1. 官网链接 http://cxf.apache.org/download.html 下载apache-cxf-3.4.5.zip 2. 解压 3. 依赖…

CXF实现WebService

一、CXF简介 Apache CXF Celtix XFire&#xff0c;开始叫 Apache CeltiXfire&#xff0c;后来更名为 Apache CXF 了&#xff0c;以下简称为 CXF。CXF 继承了 Celtix 和 XFire 两大开源项目的精华&#xff0c;提供了对 JAX-WS 全面的支持&#xff0c;并且提供了多种 Binding …

SpringBoot2 整合 CXF 服务端和客户端

文章目录 一、CXF服务端1. 导入依赖2. 创建service接口3. 接口实现类4. cxf配置类5. 查看wsdl结果 二、CXF客户端2.1. 客户端2.2. 断点调试2.3. 发起调用服务开源源码. 一、CXF服务端 1. 导入依赖 <properties><cxf.version>3.3.1</cxf.version></proper…