文章目录
- 依赖的方式
- 直接依赖
- 项目依赖
- 本地jar包依赖
- 依赖的类型
- api和implementation的区别
- 依赖冲突及解决方案
- 移除某个依赖
- 不允许依赖传递
- 强制使用某个版本
- 依赖冲突时立刻构建失败
依赖的方式
Gradle中的依赖方式有直接依赖、项目依赖和本地jar包依赖三种:
dependencies {
// 1、依赖当前项目下的某个模块[子工程]
implementation project(':subject01')
// 2、直接依赖本地的某个jar文件
implementation files('libs/foo.jar', 'libs/bar.jar')
// 2、配置某文件夹作为依赖项
implementation fileTree(dir: 'libs', include: ['*.jar'])
// 3、直接依赖
implementation 'org.apache.logging.log4j:log4j:2.17.2'
}
直接依赖
在项目中直接导入的依赖,即为直接依赖,如:
implementation 'org.apache.logging.log4j:log4j:2.17.2'
完整写法如下,其中group/name/version
共同定位一个远程jar包:
implementation group: 'org.apache.logging.log4j', name: 'log4j;, version: '2.17.2'
项目依赖
依赖项目中的另一个模块,被依赖的模块需要是library模块,并且在settings.gradle中配置:
implementation project(':subject01')
本地jar包依赖
即依赖本地jar包,有如下两种方式:
// 2、直接依赖本地的某个jar文件
implementation files('libs/foo.jar', 'libs/bar.jar')
// 2、配置某文件夹作为依赖项
implementation fileTree(dir: 'libs', include: ['*.jar'])
依赖的类型
其中java插件的功能,java-library插件都提供。
api和implementation的区别
如下表所示:
api | implementation | |
---|---|---|
编译时 | 能进行依赖传递;底层变,上层全部都要变;编译速度慢 | 不能进行依赖传递;底层变,上层不会变化;编译速度快 |
运行时 | 运行时会加载,所有模块的类都会被加载 | 运行时会加载,所有模块的类都会被加载 |
应用场景 | 适用于多模块依赖,避免重复依赖 | 多数情况下使用implementation |
以下图为例:
当libC
发生变化时,libA
和projectX
也随之变化,都需要重新编译;当libD
发生变化时,直接依赖它的libB
随之变化,而没有直接依赖libD
的projectX
不会发生变化,也只有libD
和libB
要重新编译。
再考虑一种场景:一个工程中,moduleA
依赖moduleB
和moduleC
,moduleB
也依赖moduleC
,因此可以让moduleB
以api
的方式依赖moduleC
,moduleA
则只implementation
依赖moduleB
即可。
再例如,一个工程中有ABCD四个模块:
1)、A implmentation B,B implementation C,则A不可用C;
2)、A implmentation B,B api C,则A可用C;
3)、A implmentation B,B implementation C,C api D,则B可用D,A不可用D;
4)、A implmentation B,B api C,C api D,则A可用D。
任何情况下,发生依赖的模块里所有的类都会被加载。
依赖冲突及解决方案
依赖冲突是指,在编译过程中,若存在对某个包的多版本依赖,构建系统要选择哪个进行构建,如下图所示:
其中,ABC都是本地项目或模块,log4j
时远程依赖。编译时,B
和C
各用各的log4j
,彼此没有冲突。但打包时,只能有一个版本的代码被打到jar包中,因此就发生了冲突。
事实上,gradle默认会选择最新的版本去打包,因为新版本的jar包一般都是向下兼容的,因此推荐这种官方的默认解决方法,不过gradle也提供了一系列的解决依赖冲突的方法:移除某个依赖;不允许依赖传递或强制使用某个版本。
移除某个依赖
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
implementation('org.hibernate:hibernate-core:3.6.3.Final'){
// 排除某一个库(slf4j)依赖:如下三种写法都行
exclude group: 'org.slf4j'
exclude module: 'slf4j-api'
exclude group: 'org.slf4j',module: 'slf4j-api'
// 排除之后,使用手动的引入即可。
implementation 'org.slf4j:slf4j-api:1.4.0'
}
不允许依赖传递
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
implementation('org.hibernate:hibernate-core:3.6.3.Final'){
// 不允许依赖传递,一般不用
transitive(false)
}
// 排除之后,使用手动的引入即可
implementation 'org.slf4j:slf4j-api:1.4.0'
}
不允许依赖传递,则该依赖的所有内部依赖均不会添加到编译或运行时的类路径中,
强制使用某个版本
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
implementation('org.hibernate:hibernate-core:3.6.3.Final')
// 强制使用某个版本【官方建议使用这种方式】
implementation('org.slf4j:slf4j-api:1.4.0!!')
// 这种效果和上面那种一样,强制指定某个版本
implementation('org.slf4j:slf4j-api:1.4.0'){
version{
strictly("1.4.0")
}
}
}
依赖冲突时立刻构建失败
事实上,我们可以配置当Gradle遇到依赖冲突时,立刻构建失败,从而找出项目或模块中的所有的依赖冲突:
// 项目或模块的build.gradle
configurations.all {
Configuration config -> {
// 当遇到版本冲突时直接构建失败
config.resolutionStrategy.failOnVersionConflict()
}
}