随着项目的不断发展,项目中的依赖也越来越多,有时可能会有几百个,这个时候对项目依赖做一个统一的管理很有必要,我们一般会有以下需求:
1、项目依赖统一管理,在单独文件中配置。
2、不同Module中的依赖版本号统一。
3、不同项目中的依赖版本号统一。
针对这些需求,目前其实已经有了一些方案:
使用循环优化Gradle依赖管理
https://juejin.cn/post/6947675376835362846#heading-2
使用buildSrc管理Gradle依赖
https://juejin.cn/post/6844903615346245646
使用includeBuild统一配置依赖版本
https://juejin.cn/post/6844904169833234439
上面的方案支持在不同Module间统一版本号,同时如果需要在项目间共享,也可以做成Gradle插件发布到远端,已经基本可以满足我们的需求。
不过Gradle7.0推出了一个新的特性,使用Catalog统一依赖版本,它支持以下特性:
1、对所有module可见,可统一管理所有module的依赖。
2、支持声明依赖bundles,即总是一起使用的依赖可以组合在一起。
3、支持版本号与依赖名分离,可以在多个依赖间共享版本号。
4、支持在单独的libs.versions.toml文件中配置依赖。
5、支持在项目间共享依赖。
注意,Catalog仍然是一个孵化中的特性,如需使用,需要在settings.gradle中添加以下内容:
enableFeaturePreview('VERSION_CATALOGS')
从命名上也可以看出,Version Catalog其实就是一个版本的目录,我们可以从目录中选出我们需要的依赖使用。
我们可以通过如下方式使用Catalog中声明的依赖。
dependencies {
implementation(libs.retrofit)
implementation(libs.groovy.core)
}
在这种情况下,libs是一个目录,retrofit表示该目录中可用的依赖项。与直接在构建脚本中声明依赖项相比,Version Catalog具有许多优点:
1、对于每个catalog,Gradle都会生成类型安全的访问器,以便你在IDE中可以自动补全。(注:目前在build.gradle中还不能自动补全,可能是指kts或者开发中?)
2、声明在catalog中的依赖对所有module可见,当修改版本号时,可以统一管理统一修改。
3、catalog支持声明一个依赖bundles,即一些总是一起使用的依赖的组合。
4、catalog支持版本号与依赖名分离,可以在多个依赖间共享版本号。
Version Catalog可以在settings.gradle(.kts)文件中声明。
dependencyResolutionManagement { versionCatalogs { libs { alias('retrofit').to('com.squareup.retrofit2:retrofit:2.9.0') alias('groovy-core').to('org.codehaus.groovy:groovy:3.0.5') alias('groovy-json').to('org.codehaus.groovy:groovy-json:3.0.5') alias('groovy-nio').to('org.codehaus.groovy:groovy-nio:3.0.5') alias('commons-lang3').to('org.apache.commons', 'commons-lang3').version { strictly '[3.8, 4.0[' prefer '3.9' } } } }
别名必须由一系列以破折号(-,推荐)、下划线 (_) 或点 (.) 分隔的标识符组成。
标识符本身必须由ascii字符组成,最好是小写,最后是数字。
值得注意的是,groovy-core会被映射成libs.groovy.core。
如果你想避免映射可以使用大小写来区分,比如groovyCore会被处理成libs.groovyCore。
在上面的示例中,我们可以看到三个groovy依赖具有相同的版本号,我们可以把它们统一起来。
dependencyResolutionManagement {
versionCatalogs {
libs {
version('groovy','3.0.5')
version('compilesdk','30')
version('targetsdk','30')
alias('groovy-core').to('org.codehaus.groovy','groovy').versionRef('groovy')
alias('groovy-json').to('org.codehaus.groovy','groovy-json').versionRef('groovy')
alias('groovy-nio').to('org.codehaus.groovy','groovy-nio').versionRef('groovy')
alias('commons-lang3').to('org.apache.commons','commons-lang3').version {
strictly'[3.8, 4.0['
prefer'3.9'
}
}
}
}
除了在依赖中,我们同样可以在build.gradle中获取版本,比如可以用来指定compileSdk等。
android {
compileSdk libs.versions.compilesdk.get().toInteger()
defaultConfig {
applicationId"com.zj.gradlecatalog"
minSdk21
targetSdk libs.versions.targetsdk.get().toInteger()
}
}
如上,可以使用catalog统一compileSdk,targetSdk,minSdk的版本号。
因为在不同的项目中经常系统地一起使用某些依赖项,所以Catalog提供了bundle(依赖包)的概念。依赖包基本上是几个依赖项打包的别名。
例如,你可以这样使用一个依赖包,而不是像上面那样声明 3 个单独的依赖项:
dependencies {
implementation libs.bundles.groovy
}
groovy依赖包声明如下:
dependencyResolutionManagement {
versionCatalogs {
libs {
version('groovy','3.0.5')
version('checkstyle','8.37')
alias('groovy-core').to('org.codehaus.groovy','groovy').versionRef('groovy')
alias('groovy-json').to('org.codehaus.groovy','groovy-json').versionRef('groovy')
alias('groovy-nio').to('org.codehaus.groovy','groovy-nio').versionRef('groovy')
alias('commons-lang3').to('org.apache.commons','commons-lang3').version {
strictly'[3.8, 4.0['
prefer'3.9'
}
bundle('groovy', ['groovy-core','groovy-json','groovy-nio'])
}
}
}
如上所示:添加groovy依赖包等同于添加依赖包下的所有依赖项。
除了Library之外,Catalog还支持声明插件版本。
因为library由它们的group、artifact和version表示,但Gradle插件仅由它们的id和version标识。
因此,插件需要单独声明:
dependencyResolutionManagement {
versionCatalogs {
libs {
alias('jmh').toPluginId('me.champeau.jmh').version('0.6.5')
}
}
}
然后可以在plugins块下面使用:
plugins {
id'java-library'
id'checkstyle'
// 使用声明的插件
alias(libs.plugins.jmh)
}
除了在settings.gradle中声明Catalog外,也可以通过一个单独的文件来配置Catalog。
如果在根构建的gradle目录中找到了libs.versions.toml文件,则将使用该文件的内容自动声明一个Catalog。
TOML文件主要由4个部分组成:
[versions] 部分用于声明可以被依赖项引用的版本。
[libraries]部分用于声明Library的别名。
[bundles]部分用于声明依赖包。
[plugins] 部分用于声明插件。
如下所示:
[versions]
groovy ="3.0.5"
checkstyle ="8.37"
compilesdk ="30"
targetsdk ="30"
[libraries]
retrofit ="com.squareup.retrofit2:retrofit:2.9.0"
groovy-core = { module ="org.codehaus.groovy:groovy", version.ref ="groovy"}
groovy-json = { module ="org.codehaus.groovy:groovy-json", version.ref ="groovy"}
groovy-nio = { module ="org.codehaus.groovy:groovy-nio", version.ref ="groovy"}
commons-lang3 = { group ="org.apache.commons", name ="commons-lang3", version = { strictly ="[3.8, 4.0[", prefer="3.9"} }
[bundles]
groovy = ["groovy-core","groovy-json","groovy-nio"]
[plugins]
jmh = { id ="me.champeau.jmh", version ="0.6.5"}
如上所示,依赖可以定义成一个字符串,也可以将module与version分离开来。
其中versions可以定义成一个字符串,也可以定义成一个范围,详情可参见rich-version。
https://docs.gradle.org/current/userguide/rich_versions.html#rich-version-constraints
[versions]
my-lib = { strictly ="[1.0, 2.0[", prefer ="1.2"}
Catalog不仅可以在项目内统一管理依赖,同样可以实现在项目间共享。
例如我们需要在团队内制定一个依赖规范,不同组的不同项目需要共享这些依赖,这是个很常见的需求。
Catalog支持通过从Toml文件引入依赖,这就让我们可以通过指定文件路径来实现共享依赖。
如下所示,我们在settins.gradle中配置如下:
dependencyResolutionManagement {
versionCatalogs {
libs {
from(files("../gradle/libs.versions.toml"))
}
}
}
此技术可用于声明来自不同文件的多个目录:
dependencyResolutionManagement {
versionCatalogs {
// 声明一个'testLibs'目录, 从'test-libs.versions.toml'文件中
testLibs {
from(files('gradle/test-libs.versions.toml'))
}
}
}
虽然从本地文件导入Catalog很方便,但它并没有解决在组织或外部消费者中共享Catalog的问题。
我们还可能通过Catalog插件来发布目录,这样用户直接引入这个插件即可。
Gradle提供了一个Catalog插件,它提供了声明然后发布Catalog的能力。
1. 首先引入两个插件
plugins {
id'version-catalog'
id'maven-publish'
}
然后,此插件将公开可用于声明目录的catalog扩展。
2. 定义目录
上面引入插件后,即可使用catalog扩展定义目录。
catalog {
// 定义目录
versionCatalog {
from files('../libs.versions.toml')
}
}
然后可以通过maven-publish插件来发布目录。
3. 发布目录
publishing {
publications {
maven(MavenPublication) {
groupId ='com.zj.catalog'
artifactId ='catalog'
version ='1.0.0'
from components.versionCatalog
}
}
}
我们定义好groupId,artifactId,version,from就可以发布了。
我们这里发布到mavenLocal,你也可以根据需要配置发布到自己的maven。
以上发布的所有代码可见:Catalog发布相关代码。
https://github.com/shenzhen2017/GradleCatalog
4. 使用目录
因为我们已经发布到了mavenLocal,在仓库中引入mavenLocal就可以使用插件了。
# settings.gradle
dependencyResolutionManagement {
//...
repositories {
mavenLocal()
//...
}
}
enableFeaturePreview('VERSION_CATALOGS')
dependencyResolutionManagement {
versionCatalogs {
libs {
from("com.zj.catalog:catalog:1.0.0")
// 我们也可以重写覆盖catalog中的groovy版本
version("groovy","3.0.6")
}
}
}
如上就成功引入了插件,就可以使用catalog中的依赖了。
这样就完成了依赖的项目间共享,以上使用的所有代码可见:Catalog使用相关代码。
https://github.com/shenzhen2017/GradleCatalog/tree/feature/useCatalog
项目间共享依赖是比较常见的需求,虽然我们也可以通过自定义插件实现,但还是不够方便
Gradle官方终于推出了Catalog,让我们可以方便地实现依赖的共享,Catalog主要具有以下特性:
1、对所有module可见,可统一管理所有module的依赖。
2、支持声明依赖bundles,即总是一起使用的依赖可以组合在一起。
3、支持版本号与依赖名分离,可以在多个依赖间共享版本号。
4、支持在单独的libs.versions.toml文件中配置依赖。
5、支持在项目间共享依赖。
设置:狂欢ruby1.9.2高线(1.6.13)描述:我已经相当习惯在其他一些项目中使用highline,但已经有几个月没有使用它了。现在,在Ruby1.9.2上全新安装时,它似乎不允许在同一行回答提示。所以以前我会看到类似的东西:require"highline/import"ask"Whatisyourfavoritecolor?"并得到:Whatisyourfavoritecolor?|现在我看到类似的东西:Whatisyourfavoritecolor?|竖线(|)符号是我的终端光标。知道为什么会发生这种变化吗? 最佳答案
我正在尝试修改当前依赖于定义为activeresource的gem:s.add_dependency"activeresource","~>3.0"为了让gem与Rails4一起工作,我需要扩展依赖关系以与activeresource的版本3或4一起工作。我不想简单地添加以下内容,因为它可能会在以后引起问题:s.add_dependency"activeresource",">=3.0"有没有办法指定可接受版本的列表?~>3.0还是~>4.0? 最佳答案 根据thedocumentation,如果你想要3到4之间的所有版本,你可以这
我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("
我的问题的一个例子是体育游戏。一场体育比赛有两支球队,一支主队和一支客队。我的事件记录模型如下:classTeam"Team"has_one:away_team,:class_name=>"Team"end我希望能够通过游戏访问一个团队,例如:Game.find(1).home_team但我收到一个单元化常量错误:Game::team。谁能告诉我我做错了什么?谢谢, 最佳答案 如果Gamehas_one:team那么Rails假设您的teams表有一个game_id列。不过,您想要的是games表有一个team_id列,在这种情况下
有什么方法可以告诉sidekiq一项工作依赖于另一项工作,并且在后者完成之前无法开始? 最佳答案 仅使用Sidekiq;答案是否定的。正如DickieBoy所建议的那样,您应该能够在依赖作业完成时将其启动。像这样。#app/workers/hard_worker.rbclassHardWorkerincludeSidekiq::Workerdefperform()puts'Doinghardwork'LazyWorker.perform_async()endend#app/workers/lazy_worker.rbclassLaz
如thisanswer中所述,Array.new(size,object)创建一个数组,其中size引用相同的object。hash=Hash.newa=Array.new(2,hash)a[0]['cat']='feline'a#=>[{"cat"=>"feline"},{"cat"=>"feline"}]a[1]['cat']='Felix'a#=>[{"cat"=>"Felix"},{"cat"=>"Felix"}]为什么Ruby会这样做,而不是对object进行dup或clone? 最佳答案 因为那是thedocumenta
>>a=5=>5>>b=a=>5>>b=4=>4>>a=>5如何将“b”设置为实际的“a”,以便在示例中,变量a也将变为4。谢谢。 最佳答案 classRefdefinitializeval@val=valendattr_accessor:valdefto_s@val.to_sendenda=Ref.new(4)b=aputsa#=>4putsb#=>4a.val=5putsa#=>5putsb#=>5当您执行b=a时,b指向与a相同的对象(它们具有相同的object_id).当你执行a=some_other_thing时,a将指向
我的ruby脚本从命令行参数获取某些输入。它检查是否缺少任何命令行参数,然后提示用户输入。但是我无法使用gets从用户那里获得输入。示例代码:test.rbname=""ARGV.eachdo|a|ifa.include?('-n')name=aputs"Argument:#{a}"endendifname==""puts"entername:"name=getsputsnameend运行脚本:rubytest.rbraghav-k错误结果:test.rb:6:in`gets':Nosuchfileordirectory-raghav-k(Errno::ENOENT)fromtes
如果我想使用“create”构建策略创建和实例,然后想使用“attributes_for”构建策略进行验证,是否可以这样做?如果我在工厂中使用序列?在Machinistgem中有可能吗? 最佳答案 不太确定我是否完全理解。而且我不是机械师的用户。但听起来您只是想做这样的事情。@attributes=FactoryGirl.attributes_for(:my_object)my_object=MyObject.create(@attributes)my_object.some_property.should==@attributes
我有以下haml:9%strongAskedby:10=link_to@user.full_name,user_path(@user)11.small="(#{@question.created_at.strftime("%B%d,%Y")})"这当前将链接和日期放在不同的行上,当它看起来像“链接(日期)”并且日期的类跨度为小...... 最佳答案 您的代码将生成类似这样的html:Askedby:UsernameApril26,2011当您使用类似.small的东西(即使用点而不指定元素类型)时,haml会创建一个implicit