讀者如果閱讀過”插件化”與”組件化”這兩篇文章的話妖碉,可能多少對下面這張圖應(yīng)該會有印象
上圖我用紅色著重標(biāo)記出來的Maven倉庫咆瘟,它的作用是什么温鸽?為什么會引入這樣一臺服務(wù)器瓤鼻?
如果我們足夠細(xì)化架構(gòu)秉版,那么必然會有通用的組件或模塊被提取出來,通常每個通用組件或模塊都有專門的團(tuán)隊來負(fù)責(zé)開發(fā)維護(hù)茬祷,既然是通用的清焕,那么其他功能模塊的研發(fā)團(tuán)隊都需要依賴他們來做事情,而依賴的方式大概有以下兩種:
1. 代碼依賴
就像上圖祭犯,組件團(tuán)隊提交代碼與依賴庫到SCM秸妥,模塊團(tuán)隊將組件工程從SCM上checkout下來,將代碼copy到工程內(nèi)或?qū)⒔M件工程作為lib依賴沃粗,這兩種做法非常容易引起依賴沖突與無用的依賴粥惧,而且這種依賴是直接的代碼依賴,具有很大的安全性性問題最盅。
2. 二進(jìn)制包依賴
二進(jìn)制包的依賴應(yīng)該說是比較好的依賴方式(沒有安全性等問題)突雪,但是面臨的問題是以什么樣的方式去獲取依賴包?
2.1 copy依賴包
組件團(tuán)隊將組件打包成AAR/JAR后檩禾,將組件包與依賴的第三方包都上傳到約定的服務(wù)器上,各模塊團(tuán)隊都到指定服務(wù)器上去取疤祭。 這種方式雖然避免了模塊團(tuán)隊直接依賴代碼盼产,但是由于需要手動從服務(wù)器下載組件包與依賴包,出錯概率很高勺馆,很有可能出現(xiàn)在手動copy過程中出現(xiàn)依賴版本copy錯誤的問題戏售,而且要想更新組件所依賴的第三方庫的版本也是非常繁瑣的事情。
2.2 通過maven
在看上圖草穆,組件團(tuán)隊將組件打包成AAR/JAR(同時生成POM文件灌灾,文件中聲明依賴),并通過Maven插件上傳到指定的私有Maven服務(wù)器上悲柱,各模塊團(tuán)隊通過Maven插件去引用組件(沒有手動過程)锋喜,而依靠Maven的依賴傳遞性將自動把各組件所依賴的所有第三方庫及第三方庫依賴的包進(jìn)行下載并引入。
很明顯2.2部分,使用maven進(jìn)行依賴的管理是最佳的方案嘿般,那么Maven是什么段标?
Maven是一個項目的管理工具,它能夠貫穿整個研發(fā)流程炉奴,從開發(fā)逼庞,測試,打包瞻赶,發(fā)布等都能派上用場赛糟,而Maven最突出的能力就是依賴管理,也就是我們今天所說的內(nèi)容砸逊。 使用了Maven的依賴管理璧南,從此告別手動copy依賴包,人工維護(hù)依賴包版本痹兜,依賴沖突與重復(fù)依賴等問題穆咐,節(jié)省了很多時間,同時避免了很多錯誤字旭。
在Android中对湃,我們基本上都是通過Gradle來操作maven(Gradle繼承了maven的依賴管理操作,并將依賴的引用變的更加簡單)遗淳,使用nexus來搭建Maven服務(wù)器(nexus實現(xiàn)了maven的依賴管理)拍柒。
在iOS中,則通過cocoapods來進(jìn)行依賴管理屈暗,我們后面會在iOS系列文章中講解cocoapods的使用拆讯。
Maven是如何進(jìn)行依賴管理的,在Gradle中如何使用养叛?
定位依賴包
我們要使用依賴包种呐,首先需要定位到這個依賴包,而在maven中弃甥,要定義一個依賴包爽室,需要了解包的定位坐標(biāo)groupid,artifactId,version,基于三者的信息就可以在本地或遠(yuǎn)程服務(wù)器進(jìn)行定位淆攻。
例如:commons-collections的定位坐標(biāo)阔墩,看下圖XML部分定義
Gradle中定義一個依賴包
commons-collections:commons-collections:3.2.2
依賴版本
通常我們可能會有多個組件引用了commons-collections包,如果需要統(tǒng)一升級commons-collections版本瓶珊,我們只能逐個到項目中去修改澎办,這樣就增加了出錯的概率蔑歌。 而maven中支持我們通過引用變量的方式筒扒,在POM文件中以${變量名}來進(jìn)行引用护奈。那么我們通過修改這個變量名對應(yīng)的值就可以做到批量修改對應(yīng)的版本號。
變量名=值(commonscollections.version = 3.2.2)
Maven配置
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>${commonscollections.version}</version>
</dependency>
Gradle引入
commons-collections:commons-collections:${commonsnet.version}
在Gradle中,我們通常將版本配置在.gradle中柑土,或配置在.properties文件中蜀肘。
依賴范圍
根據(jù)依賴范圍的配置,我們可以控制所依賴的包稽屏,是否被打包進(jìn)我們的產(chǎn)品中扮宠。
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>${commonscollections.version}</version>
<scope>provided</scope>
</dependency>
依賴范圍 是否被打包
compile true
runtime true
provided false
test false
依賴分類
通常可以通過groupid(組ID),artifactId(依賴包名稱),version(依賴包版本)來定位一個依賴包狐榔。 但是有時坛增,我們還需要依賴包的其他部分:比如,源碼(src)薄腻,資源(sources)收捣,文檔(javadoc)等,這些包他們的groupid,artifactid,version是相同的庵楷,只是類型不同罢艾,也就是classifier不同。 我們看下dbutils組件都做了哪些分類尽纽,從下圖XML部分咐蚯,可以看到當(dāng)前classifier是src,也就表明弄贿,這是一個源代碼的包春锋。
從下圖XML中,可以看到當(dāng)前classifier是sources差凹,表明這是一個資源的包期奔。
我們也可以將同一個jar包打包成兩個不同classifier的jar包來進(jìn)行區(qū)分,使用者根據(jù)classifier來進(jìn)行引用危尿。
通過classifier呐萌,使我們可供依賴的資源變的更加豐富和靈活。
通過Gradle來引入classifier的依賴包
Gradle引入
commons-dbutils:commons-dbutils:1.6:sources
依賴傳遞
假設(shè)我們有模塊A , B, C , D谊娇, A->B(A直接依賴B)肺孤,B->C,C->D邮绿。那么A間接依賴B,C,D模塊(A->B->C->D)渠旁,這就是依賴傳遞攀例。
禁止傳遞
Maven配置船逮,排除所有依賴
<dependency>
<groupId>xx.xx</groupId>
<artifactId>YY</artifactId>
<version>oo</version>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
gradle禁止依賴傳遞
compile('xx:YY:xx') {
transitive = false
}
依賴排除
按照上面例子,A間接依賴B,C,D模塊粤铭,如果A想排除對D的依賴挖胃,可以在pom文件中添加如下配置。
<dependency>
<groupId>xx.xx</groupId>
<artifactId>YY</artifactId>
<version>oo</version>
<exclusions>
<exclusion>
<groupId>xx.xx</groupId>
<artifactId>YY</artifactId>
</exclusion>
</exclusions>
</dependency>
在gradle中,排除依賴
dependencies {
compile("xx.xx.xx:YY:oo") {
exclude group: 'xx.xx', module: 'YY'
}
}
依賴沖突
A->B->collections3.1, B->C->D->collections3.2.2
從上面依賴關(guān)系可以看出酱鸭,B依賴collections3.1吗垮,而按照B的依賴路徑,同時也會依賴collections3.2.2凹髓,細(xì)心的讀者可能發(fā)現(xiàn)了問題烁登。
maven會選擇最短的路徑進(jìn)行依賴蔚舀,也就是v3.1,而我們本意是要使用最新的版本做為依賴赌躺,這樣可能會導(dǎo)致崩潰或功能性錯誤。我們可以縮短高版本的依賴路徑礼患,修改POM文件為A->B->collections3.2.2,而B->C->D->collections3.1
而Gradle默認(rèn)是按照高版本進(jìn)行依賴的缅叠, 即便上面的依賴路徑,Gradle也會去依賴v3.2.2的版本痪署。而有時码泞,我們可能也需要強(qiáng)制的依賴某個版本余寥,就像下面:
compile('xxx.xxx:YY:oo') {
force = true
}
全局配置強(qiáng)制依賴
configurations.all {
resolutionStrategy {
force 'xx.xx:YY:oo'
}
}
通過上面的講述,相信讀者基本已經(jīng)了解maven的依賴管理的優(yōu)勢悯森。 那么如何搭建自己的Maven服務(wù)器呢宋舷? 上面也提到了使用Nexus,關(guān)于Nexus的搭建教程推薦大家這篇文章
http://blog.csdn.net/wang379275614/article/details/43940259
通過這篇文章瓢姻,相信讀者也基本了解了Nexus相關(guān)概念和搭建技巧祝蝠。
而如何去操作nexus,通常有兩種方法:
1. 手動上傳
在nexus的web ui上進(jìn)行管理
2. 通過gradle的maven-plugin
通過gradle命令進(jìn)行管理
gradle如何配置和使用命令
1. 配置
Nexus相關(guān)配置 build-nexus-config.gradle
gradle.ext {
NEXUS_SERVER_GROUP='http://ip:port/nexus/content/groups/public/'
NEXUS_SERVER_RELEASE='http://ip:port/nexus/content/repositories/release/'
NEXUS_SERVER_SNAPSHOT='http://ip:port/nexus/content/repositories/snapshot/'
NEXUS_SERVER_UNAME='admin'
NEXUS_SERVER_UPWD='123456'
}
版本相關(guān)配置build-artifact-config.gradle
gradle.ext {
ARTIFACT_SNAPSHOT_VERSION='1.0.0.5-SNAPSHOT'
ARTIFACT_RELEASE_VERSION='1.0.0.5'
ARTIFACT_ID=’common’
ARTIFACT_GROUP_ID='com.xx.xx'
}
在build.gradle中配置倉庫地址
allprojects {
repositories {
maven{ url gradle.NEXUS_SERVER_GROUP }
jcenter()
}
}
2. 上傳組件
在上傳時需要區(qū)分當(dāng)前要上傳的是snapshot倉庫幻碱,還是relaese倉庫绎狭。
可以通過參數(shù)來進(jìn)行區(qū)分(由自己定義),默認(rèn)情況下褥傍,發(fā)布的為快照版本儡嘶,如果添加參數(shù)名為'repositoryRelease'則表示為正式版本。
Gradle發(fā)布snapshot命令:
gradle uploadArchives
Gradle發(fā)布release命令:
gradle uploadArchives -P repositoryRelease
apply plugin: 'maven'
apply plugin: "maven-publish"
def isSnapshot = true
if(project.hasProperty('repositoryRelease')){
isSnapshot = false
}
gradle.ext.isSnapshotVersion = isSnapshot;
uploadArchives {
repositories {
mavenDeployer {
repository(url: isSnapshot ? gradle.NEXUS_SERVER_SNAPSHOT : gradle.NEXUS_SERVER_RELEASE) {
authentication(userName: gradle.NEXUS_SERVER_UNAME, password: gradle.NEXUS_SERVER_UPWD)
}
pom.version = isSnapshot ? gradle.ARTIFACT_SNAPSHOT_VERSION :gradle.ARTIFACT_RELEASE_VERSION
pom.artifactId = gradle.ARTIFACT_ID
pom.groupId = gradle.ARTIFACT_GROUP_ID
pom.withXml {
def dependenciesNode = asNode().appendNode('dependencies')
configurations.compile.allDependencies.each {
def dependencyNode = dependenciesNode.appendNode('dependency')
dependencyNode.appendNode('groupId', it.group)
dependencyNode.appendNode('artifactId', it.name)
dependencyNode.appendNode('version', it.version)
}
}
}
}
}
通過以上恍风,我們可以總結(jié)出下圖:
從上圖可以看出蹦狂,所有通用組件(業(yè)務(wù)/技術(shù)/SDK等)全部都存儲在Nexus服務(wù)器上誓篱,Nexus上由不同的倉庫(snapshot/release/proxy/group等)組成。組件團(tuán)隊與業(yè)務(wù)團(tuán)隊均通過配置Maven插件達(dá)到對指定的倉庫進(jìn)行上傳和下載凯楔。
另外一個Maven重要的功能窜骄,既是代理遠(yuǎn)程的倉庫,見下圖:
通過代理遠(yuǎn)端倉庫摆屯,達(dá)到加快本地快速訪問的目的邻遏。
寫到這里,不知道讀者是不是已經(jīng)對依賴管理有了一些了解虐骑。如果讀者在搭建過程中有任何問題党远,我們可以一起討論。