【系列教程1】Gradle入门系列三:依赖管理
在现实生活中,要创造一个没有任何外部依赖的应用程序并非不可能,但也是极具挑战的。这也是为什么依赖管理对于每个软件项目都是至关重要的一部分。
这篇教程主要讲述如何使用Gradle管理我们项目的依赖,我们会学习配置应用仓库以及所需的依赖,我们也会理论联系实际,实现一个简单的演示程序。
一、仓库管理简介
本质上说,仓库是一种存放依赖的容器,每一个项目都具备一个或多个仓库。
Gradle支持以下仓库格式:
- Ivy仓库
- Maven仓库
- Flat directory仓库
我们来看一下,对于每一种仓库类型,我们在构建中应该如何配置。
1.1 在构建中加入Ivy仓库
我们可以通过URL地址或本地文件系统地址,将Ivy仓库加入到我们的构建中。
如果想通过URL地址添加一个Ivy仓库,我们可以将以下代码片段加入到build.gradle文件中:
repositories {
ivy {
url "http://ivy.petrikainulainen.net/repo"
}
}
如果想通过本地文件系统地址添加一个Ivy仓库,我们可以将以下代码片段加入到build.gradle文件中:
repositories {
ivy {
url "../ivy-repo"
}
}
小贴士:如果你想要获得更多关于Ivy仓库配置的信息,你可以参考以下资源:
- Section 50.6.6 Ivy Repositories of the Gradle User Guide
- The API documentation of the IvyArtifactRepository
我们继续,下面是如何在构建中加入Maven仓库。
1.2 在构建中加入Maven仓库
与Ivy仓库很类似,我们可以通过URL地址或本地文件系统地址,将Maven仓库加入到我们的构建中。
如果想通过URL地址添加一个Maven仓库,我们可以将以下代码片段加入到build.gradle文件中:
repositories {
maven {
url "http://maven.petrikainulainen.net/repo"
}
}
如果想通过本地文件系统地址添加一个Maven仓库,我们可以将以下代码片段加入到build.gradle文件中:
repositories {
maven {
url "../maven-repo"
}
}
在加入Maven仓库时,Gradle提供了三种“别名”供我们使用,它们分别是:
- mavenCentral()别名,表示依赖是从Central Maven 2 仓库中获取的。
- jcenter()别名,表示依赖是从Bintary’s JCenter Maven 仓库中获取的。
- mavenLocal()别名,表示依赖是从本地的Maven仓库中获取的。
如果我们想要将Central Maven 2 仓库加入到构建中,我们必须在build.gradle文件中加入以下代码片段:
repositories {
mavenCentral()
}
小贴士:如果你想要获取更多关于Maven仓库配置的信息,你可以参考这篇文章:
section 50.6.4 Maven Repositories of the Gradle User Guide
我们继续,下面是如何在构建中加入Flat Directory仓库。
1.3 在构建中加入Flat Directory仓库
如果我们想要使用Flat Directory仓库,我们需要将以下代码片段加入到build.gradle文件中:
repositories {
flatDir {
dirs 'lib'
}
}
这意味着系统将在lib目录下搜索依赖,同样的,如果你愿意的话可以加入多个目录,代码片段如下:
repositories {
flatDir {
dirs 'libA', 'libB'
}
}
小贴士:如果你想要获得更多关于Flat Directory仓库配置的信息,你可以参考以下资源:
- Section 50.6.5 Flat directory repository of the Gradle User Guide
- Flat Dir Repository post to the gradle-user mailing list
我们继续,下面要讲的是,如何使用Gradle管理项目中的依赖。
二、依赖管理简介
在配置完项目仓库后,我们可以声明其中的依赖,如果我们想要声明一个新的依赖,可以采用如下步骤:
- 指定依赖的配置。
- 声明所需的依赖。
让我们看一下详细步骤:
2.1 配置中的依赖分类
在Gradle中,依赖是按照指定名称进行分类的,这些分类被称为配置项,我们可以使用配置项声明项目的外部依赖。
Java插件指定了若干依赖配置项,其描述如下:
- compile配置项中当项目的源代码被编译时的依赖是必须的。
- runtime配置项中包含的依赖在运行时是必须的。
- testCompile配置项中包含的依赖在编译项目的测试代码时是必须的。
- testRuntime配置项中包含的依赖在运行测试代码时是必须的。
- archives配置项中包含项目生成的文件(如Jar文件)。
- default配置项中包含运行时必须的依赖。
2.2 声明项目依赖
最普遍的依赖称为外部依赖,这些依赖存放在外部仓库中。一个外部依赖可以由以下属性指定:
- group属性指定依赖的分组(在Maven中,就是groupId)。
- name属性指定依赖的名称(在Maven中,就是artifactId)。
- version属性指定外部依赖的版本(在Maven中,就是version)。
小贴士:这些属性在Maven仓库中是必须的,如果你使用其他仓库,一些属性可能是可选的。打个比方,如果你使用Flat directory仓库,你可能只需要指定名称和版本。
我们假设我们需要指定以下依赖:
- 依赖的分组是foo。
- 依赖的名称是foo。
- 依赖的版本是0.1。
- 在项目编译时需要这些依赖。
我们可以将以下代码片段加入到build.gradle中,进行依赖声明:
dependencies {
compile group: 'foo', name: 'foo', version: '0.1'
}
我们也可以采用一种快捷方式声明依赖:[group]:[name]:[version]。如果我们想用这种方式,我们可以将以下代码段加入到build.gradle中:
dependencies {
compile 'foo:foo:0.1'
}
我们也可以在同一个配置项中加入多个依赖,传统的方式如下:
dependencies {
compile (
[group: 'foo', name: 'foo', version: '0.1'],
[group: 'bar', name: 'bar', version: '0.1']
)
}
如果采用快捷方式,那可以是这样:
dependencies {
compile 'foo:foo:0.1', 'bar:bar:0.1'
}
自然地,声明属于不同配置项的依赖也是可以的。比如说,如果我们想要声明属于compile和testCompile配置项的依赖,可以这么做:
dependencies {
compile group: 'foo', name: 'foo', version: '0.1'
testCompile group: 'test', name: 'test', version: '0.1'
}
同样的,给力的快捷方式又来了( ̄︶ ̄)
|
1
2
3
4
|
dependencies { compile 'foo:foo:0.1' testCompile 'test:test:0.1'} |
小贴士:你可以从这篇文章中获得更多关于依赖声明的信息。
我们已经学习了依赖管理的基础知识,下面我们来实现一个演示程序。
三、创建演示程序
演示程序的需求是这样的:
- 演示程序的构建脚本必须使用Maven central仓库。
- 演示程序必须使用Log4j写入日志。
- 演示程序必须包含包含单元测试,保证正确的信息返回,单元测试必须使用JUnit编写。
- 演示程序必须创建一个可执行的Jar文件。
我们来看一下怎样实现这些需求。
3.1 配置仓库
我们的演示程序的一个需求是构建脚本必须使用Maven central仓库,在我们使用Maven central仓库配置构建脚本后,源代码如下:
apply plugin: 'java'
repositories {
mavenCentral()
}
jar {
manifest {
attributes 'Main-Class': 'net.petrikainulainen.gradle.HelloWorld'
}
}
我们再来看一下如何对我们的演示程序进行依赖声明。
3.2 依赖声明
在build.gradle文件中,我们声明了两个依赖:
- Log4j(版本1.2.17)用来记录日志。
- JUnit(版本4.11)用来编写单元测试。
在我们声明了这些依赖后,build.gradle文件是这样的:
apply plugin: 'java'
repositories {
mavenCentral()
}
dependencies {
compile 'log4j:log4j:1.2.17'
testCompile 'junit:junit:4.11'
}
jar {
manifest {
attributes 'Main-Class': 'net.petrikainulainen.gradle.HelloWorld'
}
}
我们继续,稍微加入一些代码。
3.3 编写代码
为了实现我们演示程序的需求,“我们不得不过度工程化一下”,我们会按照下列步骤创建程序:
- 创建一个MessageService类,当其中的getMessage()方法被调用时,返回字符串“Hello World!”。
- 创建一个MessageServiceTest类,确保MessageService类中的getMessage()方法返回字符串“Hello World!”。
- 创建程序的主类,从MessageService对象获取信息,并使用Log4j写入日志。
- 配置Log4j。
我们按部就班的操作一下。
首先,在src/main/java/net/petrikainulainen/gradle目录下新建一个MessageService类并加以实现,代码如下:
package net.petrikainulainen.gradle;
public class MessageService {
public String getMessage() {
return "Hello World!";
}
}
其次,在src/main/test/net/petrikainulainen/gradle目录下新建一个MessageServiceTest类,编写单元测试,代码如下:
package net.petrikainulainen.gradle;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class MessageServiceTest {
private MessageService messageService;
@Before
public void setUp() {
messageService = new MessageService();
}
<a href="http://www.jobbole.com/members/test/" rel="nofollow">@Test</a>
public void getMessage_ShouldReturnMessage() {
assertEquals("Hello World!", messageService.getMessage());
}
}
第三,在src/main/java/net/petrikainulainen/gradle目录下新建一个HelloWorld类,这是程序的主类,从MessageService对象获取信息,并使用Log4j写入日志,代码如下:
package net.petrikainulainen.gradle;
import org.apache.log4j.Logger;
public class HelloWorld {
private static final Logger LOGGER = Logger.getLogger(HelloWorld.class);
public static void main(String[] args) {
MessageService messageService = new MessageService();
String message = messageService.getMessage();
LOGGER.info("Received message: " + message);
}
}
第四,在src/main/resources目录中,使用log4j.properties配置log4j,log4j.properties文件如下:
log4j.appender.Stdout=org.apache.log4j.ConsoleAppender
log4j.appender.Stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.Stdout.layout.conversionPattern=%-5p - %-26.26c{1} - %m\n
log4j.rootLogger=DEBUG,Stdout
3.4 执行测试
我们可以通过以下命令执行测试。
gradle test
当测试通过时,我们能看到如下输出:
> gradle test :compileJava :processResources :classes :compileTestJava :processTestResources :testClasses :test BUILD SUCCESSFUL Total time: 4.678 secs
然而,如果测试失败,我们将看到如下输出:
> gradle test
:compileJava
:processResources
:classes
:compileTestJava
:processTestResources
:testClasses
:test
net.petrikainulainen.gradle.MessageServiceTest > getMessage_ShouldReturnMessageFAILED
org.junit.ComparisonFailure at MessageServiceTest.java:22
1 test completed, 1 failed
:test FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':test'.
> There were failing tests. See the report at: file:///Users/loke/Projects/Java/Blog/gradle-examples/dependency-management/build/reports/tests/index.html
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
BUILD FAILED
Total time: 4.461 secs
正如我们所看到的,如果单元测试失败了,输出信息中将描述以下信息:
- 哪一个测试失败了。
- 执行了几个测试,其中几个失败了。
- 测试报告的位置,测试报告提供了失败(或成功)的测试的额外信息。
当我们执行单元测试时,Gradle会在相应目录创建测试报告:
- build/test-results目录包含每次测试执行的原始数据。
- build/reports/tests目录包含一个HTML报告,描述了测试的结果。
HTML测试报告是一个非常有用的工具,因为它描述了测试失败的原因。比如说,如果我们的单元测试认为MessageService类中的getMessage()方法返回字符串“Hello Worl1d!”,那么HTML报告看上去就像下图一样:

我们继续,下面是如何打包和运行我们的演示程序。
3.5 打包和运行程序
我们能够可以使用以下任意一种命令打包程序:gradle assembly或gradle build,这两个命令都会在build/libs目录中创建dependency-management.jar文件。
当我们使用java -jar dependency-management.jar命令运行演示程序时,我们可以看到如下输出:
> java -jar dependency-management.jar
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/log4j/Logger
at net.petrikainulainen.gradle.HelloWorld.<clinit>(HelloWorld.java:10)
Caused by: java.lang.ClassNotFoundException: org.apache.log4j.Logger
at java.net.URLClassLoader$1.run(URLClassLoader.java:372)
at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:360)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 1 more
抛出异常的原因是,当我们运行程序时,Log4j的依赖在classpath中没有找到。
解决这个问题最简单的方式是创建一个所谓的“胖”Jar文件,即把所有程序运行所需的依赖都打包到Jar文件中去。
通过查阅Gradle Cookbook中的教程,可以修改构建脚本,如下:
apply plugin: 'java'
repositories {
mavenCentral()
}
dependencies {
compile 'log4j:log4j:1.2.17'
testCompile 'junit:junit:4.11'
}
jar {
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
manifest {
attributes 'Main-Class': 'net.petrikainulainen.gradle.HelloWorld'
}
}
现在,我们可以运行演示程序了(打包后),一切正常:
> java -jar dependency-management.jar INFO - HelloWorld - Received message: Hello World!
这些就是今天的内容了,我们总结一下学到了什么。
四、总结
这篇教程教会了我们四个方面的内容:
- 我们学会了配置构建所需的仓库。
- 我们学会了如何声明所需的依赖以及依赖的分类(分组)。
- 我们了解了Gradle会在测试执行后创建一个HTML测试报告。
- 我们学会了创建一个所谓的“胖”Jar文件。
【系列教程1】Gradle入门系列三:依赖管理的更多相关文章
- Java Gradle入门指南之依赖管理(添加依赖、仓库、版本冲突)
开发任何软件,如何管理依赖是一道绕不过去的坎,软件开发过程中,我们往往会使用这样那样的第三方库,这个时候,一个好的依赖管理就显得尤为重要了.作为一个自动构建工作,Gradle对依赖管理有着很好 ...
- Android Studio系列教程五--Gradle命令详解与导入第三方包
Android Studio系列教程五--Gradle命令详解与导入第三方包 2015 年 01 月 05 日 DevTools 本文为个人原创,欢迎转载,但请务必在明显位置注明出处!http://s ...
- Android Studio系列教程四--Gradle基础
Android Studio系列教程四--Gradle基础 2014 年 12 月 18 日 DevTools 本文为个人原创,欢迎转载,但请务必在明显位置注明出处!http://stormzhang ...
- [转]Android Studio系列教程六--Gradle多渠道打包
转自:http://www.stormzhang.com/devtools/2015/01/15/android-studio-tutorial6/ Android Studio系列教程六--Grad ...
- Android Studio系列教程六--Gradle多渠道打包
Android Studio系列教程六--Gradle多渠道打包 2015 年 01 月 15 日 DevTools 本文为个人原创,欢迎转载,但请务必在明显位置注明出处!http://stormzh ...
- Gradle入门系列(转)
Gradle是一种构建工具,它抛弃了基于XML的构建脚本,取而代之的是采用一种基于Groovy的内部领域特定语言.近期,Gradle获得了极大的关注,这也是我决定去研究Gradle的原因. 这篇文章是 ...
- Cobalt Strike系列教程第四章:文件/进程管理与键盘记录
Cobalt Strike系列教程分享如约而至,新关注的小伙伴可以先回顾一下前面的内容: Cobalt Strike系列教程第一章:简介与安装 Cobalt Strike系列教程第二章:Beacon详 ...
- 【Gradle教程】Gradle 入门
本文为我在学习群内分享时在B站直播分享时的文档,直播间地址 http://live.bilibili.com/22263819 PS:问一下,Linux下有什么好用的会议软件么? 知道的朋友烦请评论告 ...
- Windows Azure 入门系列课程Windows Azure 入门系列课程
Windows Azure 入门系列课程 https://www.microsoft.com/china/msdn/events/webcasts/shared/webcast/NewSeries/A ...
随机推荐
- 1.sklearn库的安装
sklearn库 sklearn是scikit-learn的简称,是一个基于Python的第三方模块.sklearn库集成了一些常用的机器学习方法,在进行机器学习任务时,并不需要实现算法,只需要简单的 ...
- JavaScript(四):运算符&数据类型转换
+:算符的加法:连接字符串 加法会将其它类型的值,自动转为字符串,然后再进行连接运算! var a=1+2; console.log('first: '+a); var a=1+2+'3';//先计算 ...
- 变量part2
一 变量值具有三个特征: 1. id:变量值的唯一编号,内存地址不同id则不同 2. type:类型 3. value(值) #name='xia' #print(id(name)) #age= ...
- qDeleteAll 之后必须清空容器
[1]qDeleteAll应用示例 qDeleteAll源码如下: template <typename ForwardIterator> Q_OUTOFLINE_TEMPLATE voi ...
- caffe生成voc格式lmdb
要训练ssd基本都是在liu wei框架下改,生成lmdb这一关照葫芦画瓢总遇坑,记录之: 1. labelmap_voc.prototxt要根据自己的分类修改,比如人脸检测改成这样: item { ...
- File §2
Previously speaking,File can be seen as one ducument, also can be seen as list of documents like dir ...
- npm 包下载的各种姿势
最近在写Node程序的时候,突然对 npm install 的-save和-save-dev 这两个参数的使用比较混乱.其实博主在这之前对这两个参数的理解也是模糊的,各种查资料和实践后对它们之间的异同 ...
- 基于FPGA摄像头图像采集显示系统
本系统主要由FPGA主控模块.图像采集模块.图像存储模块以及图像显示模块等模块组成.其中图像采集模块选择OV7670摄像头模块,完成对视频图像的采集和解码功能,并以RGB565标准输出RGB 5:6: ...
- .pages怎么在windows上打开?Windows下打开在Mac中编辑的.pages文件方法
.pages怎么在windows上打开?Windows下打开在Mac中编辑的.pages文件方法 1.最简单的方法是修改后缀名为.zip然后解压,解压后就可以看到一张图片,这个就是文档内容了. 2.更 ...
- JustOj 2038: 叶神的字符串
题目描述 众所周知,ACS协会会长叶神学习特别好,算法能力也强,作为一个弱渣的大豪哥特别崇拜叶神,觉得‘Y’‘S’这两个字符特别厉害,所以大豪哥的一个键盘上就只有Y,S两个键,大豪哥用这个键盘打出了一 ...