Maven底層容器Plexus Container的前世今生祟牲,一代芳華終落幕
前言
說(shuō)實(shí)話隙畜,我非常地糾結(jié),大家平時(shí)只是用Maven说贝,對(duì)于內(nèi)部的實(shí)現(xiàn)其實(shí)也不關(guān)心议惰,我現(xiàn)在非要拉著大家給大家講。這就有個(gè)問(wèn)題乡恕,Maven的內(nèi)部换淆,還是相對(duì)沒(méi)那么簡(jiǎn)單的,也算是個(gè)不小的工程了几颜。
核心功能倍试,大家是清楚的,內(nèi)部的執(zhí)行流程蛋哭,大家也大概猜的出來(lái):
- 解析命令行參數(shù)
- 準(zhǔn)備各種上下文县习,簡(jiǎn)單的mvn clean就涉及到當(dāng)前項(xiàng)目的元數(shù)據(jù)pom.xml、settings.xml(主要是本地谆趾、遠(yuǎn)程倉(cāng)庫(kù)相關(guān))躁愿;
- 根據(jù)mvn clean或者mvn compile,找到對(duì)應(yīng)的生命周期(大家應(yīng)該都知道m(xù)aven的三個(gè)lifecycle吧)沪蓬;然后看看要執(zhí)行生命周期中的哪些階段彤钟,順序是啥(這個(gè)和打包方式也有關(guān),jar/war時(shí)跷叉,打包組件就不同)逸雹;
- 順序執(zhí)行生命周期中的每個(gè)階段的時(shí)候营搅,去找到對(duì)應(yīng)的綁定的插件,然后執(zhí)行插件(執(zhí)行插件又包括:根據(jù)插件坐標(biāo)梆砸,去本地倉(cāng)庫(kù)/遠(yuǎn)程倉(cāng)庫(kù)找對(duì)應(yīng)的artifact转质,以及解析artifact中的插件元數(shù)據(jù),元數(shù)據(jù)中會(huì)告訴你帖世,支持傳哪些參數(shù)休蟹,參數(shù)類型是啥)
- 執(zhí)行完成后,返回結(jié)果日矫。
這么一個(gè)不小的工程赂弓,想必,還是會(huì)有很多對(duì)象互相依賴的哪轿,在沒(méi)有依賴注入前拣展,都是靠new,或者是工廠來(lái)緩解缔逛;我查了下歷史資料,maven的開發(fā)者在一篇文章中(https://blog.sonatype.com/2010/01/from-plexus-to-guice-1-why-guice/)姓惑,提到:
We knew we needed some sort of component framework, some standard mechanism to instantiate plugins and configure them based on a set of configuration points
大概意思褐奴,他們也發(fā)現(xiàn)他們需要一個(gè)組件框架,一種標(biāo)準(zhǔn)化能夠?qū)嵗M件的機(jī)制于毙,能夠基于一組配置來(lái)配置這些組件敦冬。
說(shuō)明,早期的Maven開發(fā)者們唯沮,已經(jīng)意識(shí)到了自己可能需要一個(gè)類似容器的東西脖旱,那,為啥沒(méi)有選用Spring呢?
下邊,讓我們來(lái)層層揭開歷史的面紗衰猛。
Maven初起步
查閱了Maven官網(wǎng)霞揉,發(fā)現(xiàn)Maven的第一個(gè)版本,竟然早在2002年童番。(http://jakarta.apache.org/site/news/news-2002.html)。
2003年,成為Apache頂級(jí)項(xiàng)目巍虫,2004年,發(fā)布1.0版本鳍刷。
另外一邊占遥,我們熟知的,無(wú)人不知無(wú)人不曉的Spring呢输瓜,第一個(gè)版本是什么時(shí)候呢瓦胎?
The first version was written by Rod Johnson, who released the framework with the publication of his book Expert One-on-One J2EE Design and Development in October 2002.
這里說(shuō)芬萍,spring的第一個(gè)版本同樣發(fā)布于2002年,是跟隨著Rod Johnson的書《Expert One-on-One J2EE Design and Development 》一起發(fā)布的凛捏。也是在03年担忧,使用Apache 2.0 License,發(fā)布了一個(gè)版本坯癣;04年3月瓶盛,發(fā)布了一個(gè)生產(chǎn)用的版本。
所以示罗,我們就可以理解惩猫,Maven的開發(fā)者在那篇2010年的文章里寫的:
When we started the Maven project, dependency injection was still developing. Spring was just starting out and the Avalon project at Apache was really the only IoC framework around
簡(jiǎn)單說(shuō),就是蚜点,當(dāng)開始搞Maven的時(shí)候轧房,依賴注入還在發(fā)展當(dāng)中。Spring剛起步绍绘,Avalon項(xiàng)目奶镶,也僅僅只是一個(gè)ioc框架。
既然外部不成熟陪拘,他們的重心也不在這些依賴注入框架上面厂镇,所以他們就基于自己的需求,自己搞了一個(gè)適合Maven的左刽,它就叫:Plexus捺信。
Maven早期:做自己的IOC容器
Plexus項(xiàng)目
Plexus: 發(fā)音(?pleks?s),a network of nerves or vessels in the body.
Plexus欠痴,中文意思迄靠,可能是神經(jīng)網(wǎng)絡(luò)或者血管網(wǎng)絡(luò),就因?yàn)檠苁蔷W(wǎng)狀的喇辽,像他么互聯(lián)網(wǎng)一樣掌挚,所以被拿來(lái)當(dāng)一個(gè)框架名了嗎,maybe菩咨。
這個(gè)項(xiàng)目(官網(wǎng):https://codehaus-plexus.github.io)疫诽,定位是做容器。官網(wǎng)不知道為啥旦委,這會(huì)打不開奇徒,但是我發(fā)現(xiàn)一個(gè)記錄互聯(lián)網(wǎng)歷史的網(wǎng)站:https://web.archive.org/web/20150323083530/http://plexus.codehaus.org/index.html。
Plexus項(xiàng)目缨硝,基于其中的Plexus Container子項(xiàng)目摩钙,應(yīng)用程序可以使用基于組件的編程方式,構(gòu)建模塊化的查辩、可復(fù)用的組件胖笛。Plexus類似其他的IOC框架网持,如Spring,但它還額外提供了很多特性长踊,如:組件生命周期管理功舀、組件實(shí)例化策略、嵌套容器身弊、組件配置辟汰、自動(dòng)注入、組件依賴阱佛、各種依賴注入方式(如構(gòu)造器注入帖汞、setter注入、字段注入)凑术。
總的來(lái)說(shuō)翩蘸,我個(gè)人感覺(jué),這些特性淮逊,Spring好像也有啊催首,哈哈。
Plexus 下組件
Plexus這么一個(gè)項(xiàng)目泄鹏,當(dāng)然不止容器郎任,大概有如下幾個(gè):
Plexus Classworlds,類加載器框架命满,Maven至今還在用,個(gè)人感覺(jué)也挺不錯(cuò)绣版,推薦學(xué)習(xí)學(xué)習(xí)胶台;
Plexus Container,IOC容器杂抽,Maven 1.x/2.x在用诈唬,3.0版本后,Maven自身也沒(méi)有再使用了
-
Plexus Components
Maven的工作就是和各種文件缩麸、目錄打交道铸磅,這期間,會(huì)沉淀出來(lái)很多公用組件:
- IO相關(guān)的杭朱,
Plexus IO Components
阅仔,它的maven坐標(biāo):
<!-- https://mvnrepository.com/artifact/org.codehaus.plexus/plexus-io --> <dependency> <groupId>org.codehaus.plexus</groupId> <artifactId>plexus-io</artifactId> <version>3.2.0</version> </dependency>
-
歸檔相關(guān)的,Plexus Archiver Component弧械,maven坐標(biāo):
<!-- https://mvnrepository.com/artifact/org.codehaus.plexus/plexus-archiver --> <dependency> <groupId>org.codehaus.plexus</groupId> <artifactId>plexus-archiver</artifactId> <version>4.2.5</version> </dependency>
cli相關(guān)八酒,Plexus CLI
編譯相關(guān),Plexus Compiler
Digest/Hashcode相關(guān)刃唐,Plexus Digest / Hashcode Components
國(guó)際化相關(guān)羞迷,i18n
還有些其他的界轩,我懶得列舉了,大家自己看吧衔瓮,https://web.archive.org/web/20150225072024/http://plexus.codehaus.org/plexus-components/
- IO相關(guān)的杭朱,
Plexus Maven Plugin浊猾,用來(lái)支持Maven插件
Plexus Utils,工具類热鞍,至今仍在用
Plexus組件的現(xiàn)狀
打開我本機(jī)的maven安裝目錄的lib葫慎,發(fā)現(xiàn)plexus相關(guān)的,僅剩少數(shù)幾個(gè)了碍现,如幅疼,下圖的幾個(gè)工具:
當(dāng)初說(shuō)好的IOC容器,結(jié)果Maven怎么自己也不用了呢昼接?我們來(lái)看看這個(gè)容器相關(guān)的組件吧爽篷。
容器相關(guān)的,一共4個(gè)maven組件慢睡。
這里面逐工,plexus-component-annotations我們剛看到,還在用漂辐,他是干嘛的呢泪喊,就是類似于Spring里面的注解,比如@Service/@Controller這種髓涯。
而plexus-component-metadata袒啼,是一個(gè)maven插件,用來(lái)生成組件的xml纬纪,有點(diǎn)像我們的spring的xml時(shí)代蚓再,這個(gè)工程呢,就可以分析我們代碼包各,幫我們生成spring的bean.xml這種摘仅,就不需要手動(dòng)配置依賴了。
這里面问畅,真正的IOC容器實(shí)現(xiàn)娃属,就是:plexus-container-default。
該組件护姆,可以說(shuō)是Maven的結(jié)發(fā)妻子矾端,陪伴了Maven的青年時(shí)期,我們看看這個(gè)組件是什么時(shí)候第一次登場(chǎng)的卵皂。
下邊是它1.0版本的坐標(biāo):
<!-- https://mvnrepository.com/artifact/plexus/plexus-container-default -->
<dependency>
<groupId>plexus</groupId>
<artifactId>plexus-container-default</artifactId>
<version>1.0-alpha-1</version>
</dependency>
時(shí)間是2005年须床。
而maven什么時(shí)候開始使用該容器呢,我沒(méi)有查到maven 1.x版本的pom依賴渐裂,但是在2.0.alpha版本豺旬,已經(jīng)看到了該容器的身影:
此時(shí)钠惩,是2006年。
僅僅幾年后族阅,在maven 3.0的版本中篓跛,已經(jīng)不再有plexus ioc容器的身影,卻來(lái)了一個(gè)不速之客坦刀。
在開始說(shuō)不速之客之前愧沟,我們還是要問(wèn)問(wèn),plexus ioc容器鲤遥,為啥就不行了呢沐寺?對(duì)這個(gè)歷史感興趣的,可以直接看:
https://blog.sonatype.com/2010/01/from-plexus-to-guice-1-why-guice/
為什么呢盖奈?因?yàn)闀r(shí)代變了混坞,此時(shí),Spring已經(jīng)開始成為事實(shí)上的IOC容器標(biāo)準(zhǔn)钢坦,不過(guò)究孕,雖然Spring在應(yīng)用開發(fā)領(lǐng)域,所向披靡爹凹,但是在各種框架中厨诸,框架開發(fā)者們還是覺(jué)得Spring太重了,一下就要引入好幾個(gè)jar包禾酱,實(shí)在是過(guò)于臃腫微酬。因此,google 在2007年的時(shí)候颤陶,就推出了一個(gè)輕量級(jí)的依賴注入框架颗管,叫g(shù)oogle guice。
此時(shí)指郁,經(jīng)過(guò)多年的迭代忙上,在2010年前后拷呆,guice已經(jīng)比較成熟了闲坎,在google內(nèi)部也而得到了廣泛應(yīng)用,且依賴注入這個(gè)領(lǐng)域茬斧,也在持續(xù)不斷地發(fā)展中腰懂,比如java官方定義了相關(guān)的標(biāo)準(zhǔn)api。
而此時(shí)项秉,Maven的開發(fā)者們已經(jīng)難以同時(shí)維護(hù)Plexus IOC容器(比如適配java官方新出標(biāo)準(zhǔn)绣溜,和周邊Spring兼容等等各類工作),因此娄蔼,Maven決定怖喻,為了節(jié)省精力底哗,Maven將不再基于Plexus IOC容器,而是使用Guice锚沸,以后就只管用了跋选,而guice的維護(hù)升級(jí),自然有Guice的開源團(tuán)隊(duì)去跟進(jìn)哗蜈。
說(shuō)了那么多前标,為了紀(jì)念Plexus Container的落幕,我們還是來(lái)看看距潘,這個(gè)IOC組件到底怎么用的吧炼列?
Plexus IOC容器初使用
例子也是來(lái)自于官網(wǎng),我根據(jù)文檔音比,整理成了一個(gè)maven module俭尖。大家可以拉代碼下來(lái)。
1.像所有的IOC容器一樣硅确,定義一個(gè)接口和實(shí)現(xiàn)類
public interface Cheese
{
/** The Plexus role identifier. */
String ROLE = Cheese.class.getName();
/**
* Slices the cheese for apportioning onto crackers.
* @param slices the number of slices
*/
void slice( int slices );
/**
* Get the description of the aroma of the cheese.
* @return the aroma
*/
String getAroma();
}
public class ParmesanCheese
implements Cheese
{
public void slice( int slices )
{
throw new UnsupportedOperationException( "No can do" );
}
public String getAroma()
{
return "strong";
}
}
就像spring的xml時(shí)代一樣目溉,定義組件的依賴關(guān)系
注意一下,這里的組件配置中菱农,有三個(gè)元素:
- role缭付,此處放:接口名稱
- role-hint,此處放:實(shí)現(xiàn)類的qualifier循未,類似spring中陷猫,一個(gè)接口多個(gè)實(shí)現(xiàn)類,我們就會(huì)給這多個(gè)實(shí)現(xiàn)類的妖,定義一個(gè)顯示的名字绣檬,@Qualifier
- implementation,實(shí)現(xiàn)類的類名嫂粟。
ok娇未,這就定義好了一個(gè)組件。
我們開始測(cè)試星虹。
從容器中獲取組件
public class App {
public static void main(String args[]) throws Exception
{
// 1 定義一個(gè)容器零抬,容器會(huì)去加載classpath下的META-INF/Plexus/component.xml中的組件
PlexusContainer container= new DefaultPlexusContainer();
// 2 獲取組件,完成依賴注入等工作
Cheese cheese = (Cheese) container.lookup( Cheese.ROLE, "parmesan" );
// 3 使用組件
System.out.println( "Parmesan is " + cheese.getAroma() );
// 4 銷毀容器
container.dispose();
}
}
其他用法
如果這個(gè)組件宽涌,依賴其他組件平夜,怎么辦呢,怎么注入呢卸亮?
我在maven源碼工程里看到這樣的組件配置忽妒,想必,就是像如下這樣配置。
<?xml version="1.0"?>
<component-set>
<components>
<component>
<role>org.apache.maven.profiles.activation.ProfileActivator</role>
<role-hint>faulty</role-hint>
<implementation>org.ext.App</implementation>
<requirements>
<requirement>
<role>org.apache.maven.artifact.ArtifactResolver</role>
<field-name>artifactResolver</field-name>
</requirement>
</requirements>
</component>
</components>
</component-set>
循環(huán)依賴怎么辦呢段直?放心吃溅,人家也是可以解決的,這里就不截圖了鸯檬。
總結(jié)
一個(gè)組件罕偎,寫出來(lái),竟然感覺(jué)就像是也有感情一樣京闰,也是有點(diǎn)意思颜及。不過(guò)不管怎么說(shuō),Plexus Container在陪伴Maven度過(guò)了整個(gè)2.x版本后蹂楣,終將落下帷幕俏站。
接下來(lái),是Guice的時(shí)代痊土,而現(xiàn)在肄扎,十多年后的2021年,Guice依然穩(wěn)定地支撐著Maven赁酝。Guice足夠優(yōu)秀犯祠,在此之前,我竟然幾乎沒(méi)什么了解酌呆,Guice在哪些地方有應(yīng)用呢衡载,簡(jiǎn)單列舉幾個(gè):
- google內(nèi)部
- scalatest
- TestNG
- Caffeine Cache
- Spring Security Config
- elastic search
- jenkins
以及一些其他的我不太熟悉的技術(shù),大家可以查看:
https://mvnrepository.com/artifact/com.google.inject/guice/usages
本文由博客一文多發(fā)平臺(tái) OpenWrite 發(fā)布隙袁!