依赖的 Scope
- compile (使用 implementation 替代,提高编译速度)
- runtime
- compileOnly (新版才支持,就是 provided)
- testCompile (使用 testImplementation 替代)
- testRuntime
项目依赖关系
1 2
| gradle dep gradle dependencies
|
依赖 Maven 仓库
1
| compile 'groupId:artifactId:version'
|
依赖其他子项目
1 2
| compile project(':dependency-project') api(project(":spring-boot-project:spring-boot-starters:spring-boot-starter"))
|
被依赖的项目会被打成 jar 包放到 lib 目录(包含它的资源文件,资源文件在 jar 包里,不能进行修改呢)。
依赖本地 jar 包
1 2
| compile files('spring-core.jar', 'spring-aop.jar') compile fileTree(dir: 'src/main/webapp/WEB-INF/lib', include: '*.jar')
|
提示:
- dir 可以是绝对路径,也可以是相对于工程目录的项目路径
- 不能使用
implementation files('src/main/lib/*.jar')
打包工程
1 2
| gradle assemble // 编译,打包 gradle build // 编译,执行测试,打包
|
定制包名
打包时制定包名,例如默认打包出来是 Exam.war
,而我们需要的是 Exam.zip
1
| war.archiveFileName = 'Exam.zip'
|
编译工程
指定编译的输出目录
1
| buildDir = new File(rootProject.projectDir, '../build/' + project.name)
|
一般是不会修改这个属性的
指定 Maven 仓库
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| allprojects { repositories { mavenLocal() mavenCentral() maven { url 'http://maven.edu-edu.com.cn/content/groups/public/' } maven { url 'http://maven.edu-edu.com.cn/content/groups/public/' credentials { username 'admin' password '*****' } } } }
|
上传 jar 包到 Maven 仓库
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| uploadArchives { repositories { mavenDeployer { repository(url: "http://maven.edu-edu.com.cn/nexus/content/repositories/releases/") { authentication(userName: "admin", password: "*****") } pom.groupId = "$project.group" pom.artifactId = "$artifactId" pom.version = "$project.version" } } }
|
排除冲突的 jar 包
例如 dubbo 2.5 依赖 Spring 2,现在基本都用 Spring 4,所以需要把 dubbo 依赖的 Spring 2 的依赖去掉,使用 exclude:
1 2 3
| compile('com.alibaba:dubbo:2.5.3') { exclude group: 'org.springframework', module: 'spring' }
|
打包时过滤掉不需要的 jar 包
1 2 3 4 5 6 7 8
| war { classpath = classpath.filter { file -> file.name.startsWith('lms-client') || file.name.startsWith('dict-client') || file.name.startsWith('e-platform-client') } }
|
也可以把不需要的 jar 的依赖替换为 compileOnly,这样就不需要判断了
打包时替换 META-INF/context.xml
中的占位符
1 2 3 4 5 6 7 8 9
| war { from("$webAppDir/META-INF") { eachFile { it.filter(ReplaceTokens, tokens: loadConfiguration()) } into('META-INF') } }
|
运行时替换 META-INF/context.xml
中的占位符
任务 prepareInplaceWebApp
执行完后执行自定义任务,例如替换 context.xml
中的占位符
1 2 3 4 5 6 7 8 9 10 11 12 13
| project.afterEvaluate { tasks.getByPath('prepareInplaceWebApp').doLast { copy { from("$webAppDir/META-INF") { eachFile { it.filter(ReplaceTokens, tokens: loadConfiguration()) } } into("$buildDir/inplaceWebapp/META-INF") } } }
|
获取命令中的参数
推荐使用 -D 的方式
1 2 3 4 5
| 命令: gradle -Denv=development clean assemble 获取: def environment = System.getProperty('env', 'development')
命令: gradle -Penv=develop clean assemble 获取: def environment = hasProperty('env') ? env : 'development'
|
资源文件内容动态替换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| def loadConfiguration() { def environment = System.getProperty('env', 'development') def configFile = file('config.groovy') return new ConfigSlurper(environment).parse(configFile.toURI().toURL()).toProperties() } processResources { from(sourceSets.main.resources.srcDirs) { filter(ReplaceTokens, tokens: loadConfiguration()) } }
processResources { from(sourceSets.main.resources.srcDirs) { eachFile { file -> if (file.name.endsWith('.properties')) { file.filter(org.apache.tools.ant.filters.ReplaceTokens, tokens: loadConfiguration()) } } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| environments { development { db { host = '127.0.0.1' username = 'root' password = 'root' database = 'db_exam' } } production { db { host = '192.168.10.123' username = 'root' password = 'sluper' database = 'db_exam' } } }
|
使用嵌入式服务器 Tomcat
1 2 3 4 5 6 7 8 9 10 11
| plugins { id 'war' id 'java' id 'org.gretty' version '2.2.0' } gretty { httpPort = 8080 contextPath = '' servletContainer = 'tomcat8' }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| gretty { httpPort = 8080 debugPort = httpPort + 1 servicePort = httpPort + 2 statusPort = httpPort + 3 httpsPort = httpPort + 4 httpsEnabled = true contextPath = '' jvmArgs = ['-Xmx1024M', '-XX:PermSize=128M', '-XX:MaxPermSize=256M'] servletContainer = 'jetty7' scanInterval = 0 inplaceMode = 'hard' debugSuspend = false enableNaming = true managedClassReload = true }
|
Tomcat 的 GET 请求的默认编码是 ISO8859-1,如果 GET 需要使用 UTF-8 的话,需要在 server.xml
中设置 URIEncoding
为 UTF-8:
1 2 3 4
| <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" URIEncoding="UTF-8"/>
|
在 Gretty 中没有提供设置 URIEncoding
的选项,但是可以设置 serverConfigFile
引用 Tomcat 的 server.xml
来达到目的,例如:
1 2 3 4 5 6 7 8 9 10 11
| gretty { httpPort = 8080 contextPath = '' servletContainer = 'tomcat7' serverConfigFile = "server.xml"
inplaceMode = 'hard' debugSuspend = false managedClassReload = true recompileOnSourceChange = false }
|
Gretty 热部署优化
Gretty 热部署的相关配置,下面几项都默认设置为 true (reload 表示重新加载整个 web 应用):
recompileOnSourceChange
// Java 文件变化后自动编译
reloadOnClassChange
// Class 文件变化后 reload
reloadOnConfigChange
// 配置文件如 web.xml 变化后 reload
reloadOnLibChange
// lib 目录下的 jar 变化后 reload
例如 Java 文件修改保存后会自动编译,导致 Class 文件变化,然后 gretty 会重新加载整个 Web 应用,例如应用启动时要执行计划任务,检查数据库的结构等,这样的热部署效率太低。
如果 Class 文件变化后,能不能只加载 Class 文件,而不是重新加载整个 Web 应用呢?设置 managedClassReload=true
即可 (使用的是 SpringLoaded 来进行热加载,并自动设置 reloadOnClassChange=false):
1 2 3 4 5 6 7 8
| gretty { httpPort = 80 contextPath = '/' servletContainer = 'tomcat8' managedClassReload = true recompileOnSourceChange = false }
|
使用 JNDI
参考 JNDI 数据源:
1 2 3 4 5 6 7 8
| gretty { port = 8080 contextPath = '' servletContainer = 'tomcat7' enableNaming = true contextConfigFile = 'tomcat-context.xml' serverConfigFile = 'tomcat-server.xml' }
|
多模块工程
由 3 个模块组成:
parent
是父模块
mix1
和 mix2
是子模块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| └── parent ├── mix1 │ ├── build.gradle │ └── src │ └── main │ ├── java │ └── resources ├── mix2 │ ├── build.gradle │ └── src │ └── main │ ├── java │ └── resources ├── build.gradle └── settings.gradle
|
1 2 3
| include 'mix1' include 'mix2'
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
|
allprojects { ext { profile = System.getProperty("profile", "dev") } println '++ Building: ' + project.name }
subprojects { apply plugin: 'java'
tasks.withType(JavaCompile) { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 }
[compileJava, compileTestJava, javadoc]*.options*.encoding = 'UTF-8'
repositories { mavenLocal() mavenCentral() } }
project('mix1') { println 'Building module mix1' }
configure(subprojects.findAll { it.name.equals('mix1') || it.name.equals('mix2') }) { apply plugin: 'java' }
|
进入 parent 目录:
- 执行
gradle build
构建所有的模块
- 执行
gradle :mix1:build
构建子模块 mix1
获取路径
1 2 3 4
| println "${rootProject.projectDir}/config" 不能用 println ${rootProject.projectDir} + "/config"
|
settings.gradle
除了构建脚本文件,Gradle 还定义了一个约定名称的设置文件 (默认为 settings.gradle),该文件在初始化阶段被执行,对于多项目构建必须保证在根目录下有 settings.gradle 文件,对于单项目构建设置文件是可选的
强制 Gradle 使用 UTF-8
1 2
| 打开 gradle/bin 目录下的 gradle.bat 文件,修改 12 行附近的代码为 set DEFAULT_JVM_OPTS="-Dfile.encoding=UTF-8"
|
或者 gradle.properties 里添加
1
| systemProp.file.encoding=UTF-8
|
创建工程目录结构
Gradle 默认没有提供创建各种项目目录结构的任务,但是可以使用 gradle-templates
来创建工程的目录结构。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| apply plugin: 'java' apply plugin: 'templates'
repositories { mavenCentral() }
buildscript { repositories { maven { url 'http://dl.bintray.com/cjstehno/public' } }
dependencies { classpath 'gradle-templates:gradle-templates:1.5' } }
|
运行 gradle tasks
可以看到有很多创建不同项目结构的任务
- 使用
gradle initJavaProject
创建 Java 应用程序的目录结构
- 使用
gradle initWebappProject
创建 Web 项目的目录结构
使用 Spring 的应用程序打包
Gradle 默认的打包任务 jar
不能带上依赖的类,在有依赖 Spring 的项目中,最好是使用 shadowJar
来打包,其会合并 spring 冲突的配置文件,例如 fatJar 则不会。
如果是可执行的 jar 包,配置 mainClassName 为 jar 包启动运行的类,否则可以忽略掉。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| plugins { id 'java' id 'application' id 'com.github.johnrengelman.shadow' version '2.0.2' }
ext { project.mainClassName = System.getProperty("mainClass", "DefaultMainClass") }
shadowJar { mergeServiceFiles('META-INF/spring.*') }
|
打包时只要指定的 jar 包、解压
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| war { classpath = classpath.filter { file -> file.name.startsWith('javaee-mvc-framework') || file.name.startsWith('e-platform-client') || file.name.equals('main') }
archiveName = 'qa.zip' def zipFile = file("$buildDir/libs/qa.zip") doLast { copy { from zipFile into "$rootProject.projectDir/output/$school/" } def outputDir = file("$rootProject.projectDir/output/qa") delete outputDir copy{ from zipTree(zipFile) into outputDir } } }
|
设置测试输出的目录
1 2 3 4 5 6 7 8
| test { testLogging { events "PASSED", "STARTED", "FAILED", "SKIPPED" }
reports.html.destination = "${buildDir}/reports/test" }
|
执行一个具体的测试函数
1
| gradle test --tests=xtuer.UserTest.findUsers
|
修改本地仓库地址
修改环境变量 GRADLE_USER_HOME 的路径值,参考 Where is Gradle Cache Location。
下载源码和文档
在 build.gradle 中增加:
1 2 3 4 5 6 7 8 9 10 11
| plugins { id 'java' id 'idea' }
idea { module { downloadJavadoc = true downloadSources = true } }
|
参考